From 2e12f79a8c127f1ce5ac5b1e5b17aa5ba7dec93e Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Thu, 17 Sep 2015 00:08:04 +0100 Subject: [PATCH 001/112] Sidebar Length Apapting to Loaded Content --- .../src/jsp/css/theResponsiveCss.css | 2 +- SecurityShepherdCore/src/jsp/index.jsp | 24 ++++++++++++++++-- SecurityShepherdCore/src/jsp/js/ajaxCalls.js | 25 ------------------- 3 files changed, 23 insertions(+), 28 deletions(-) delete mode 100644 SecurityShepherdCore/src/jsp/js/ajaxCalls.js diff --git a/SecurityShepherdCore/src/jsp/css/theResponsiveCss.css b/SecurityShepherdCore/src/jsp/css/theResponsiveCss.css index 904470808..810ec7402 100644 --- a/SecurityShepherdCore/src/jsp/css/theResponsiveCss.css +++ b/SecurityShepherdCore/src/jsp/css/theResponsiveCss.css @@ -20,7 +20,7 @@ border-top: 3px solid #A878EF; border-right: 3px solid #A878EF; position: absolute; - height: 100%; + height: 150%; border-radius: 2px; text-decoration: none; vertical-align: top; diff --git a/SecurityShepherdCore/src/jsp/index.jsp b/SecurityShepherdCore/src/jsp/index.jsp index e6227cb00..866fe2c30 100644 --- a/SecurityShepherdCore/src/jsp/index.jsp +++ b/SecurityShepherdCore/src/jsp/index.jsp @@ -114,7 +114,7 @@ if (request.getSession() != null)
-
+
@@ -253,7 +253,14 @@ if (request.getSession() != null)
- + <% //Hide UI Scripts from Users (Blocked at session level anyway, just stops spiders finding the links) if (userRole.compareTo("admin") == 0){ %> @@ -621,6 +628,7 @@ if (request.getSession() != null) }); <% } %> }).appendTo('#contentDiv'); + $("#theSidebarWrapper").height($("#contentDiv").height()); } else { @@ -676,6 +684,7 @@ if (request.getSession() != null) }); <% } %> }).appendTo('#contentDiv'); + $("#theSidebarWrapper").height($("#contentDiv").height()); } else { @@ -828,10 +837,21 @@ if (request.getSession() != null) }); <% } %> + function resizeSidebar() { + //Make Sidebar as Long as Page + if($("#contentDiv").height() > 700) { + console.log("Updating Sidebar Length to " + $("#contentDiv").height()); + $("#theSidebarWrapper").height($("#contentDiv").height()); + } else{ + console.log("Setting Sidebar to 130% because " + $("#contentDiv").height() + "px is too short."); + $("#theSidebarWrapper").height("130%"); + } + }
-

Insecure Direct Object Reference Challenge One

+

<%= i18nChallengeName %>

- The result key for this challenge is stored in the private message for a user that is not listed below... + <%= bundle.getString("challenge.whatToDo") %>

@@ -72,8 +80,8 @@ String levelName = "Insecure Direct Object References Challenge One"; -
- +
"/>
+ @@ -89,7 +97,7 @@ String levelName = "Insecure Direct Object References Challenge One"; $("#resultsDiv").hide("slow", function(){ var ajaxCall = $.ajax({ type: "POST", - url: "o9a450a64cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c", + url: "<%= levelHash %>", data: { userId: optionValue }, diff --git a/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp b/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp index 3658fa0a4..e16355f6d 100644 --- a/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp @@ -1,5 +1,5 @@ <%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> - +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** * Insecure Direct Object References Challenge Two @@ -22,7 +22,15 @@ * @author Mark Denihan */ -String levelName = "Insecure Direct Object References Challenge Two"; + String levelName = "Insecure Direct Object References Challenge Two"; + String levelHash = "vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4"; + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.directObject." + levelHash, locale); + //Used more than once translations + String i18nChallengeName = bundle.getString("challenge.challengeName"); + ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -48,15 +56,15 @@ String levelName = "Insecure Direct Object References Challenge Two"; - Security Shepherd - Insecure Direct Object References Challenge Two + Security Shepherd - <%= i18nChallengeName %>
-

Insecure Direct Object Reference Challenge Two

+

<%= i18nChallengeName %>

- The result key for this challenge is stored in the private message for a user that is not listed below... + <%= bundle.getString("challenge.whatToDo") %>

@@ -72,8 +80,8 @@ String levelName = "Insecure Direct Object References Challenge Two"; -
- +
"/>
+ @@ -89,7 +97,7 @@ String levelName = "Insecure Direct Object References Challenge Two"; $("#resultsDiv").hide("slow", function(){ var ajaxCall = $.ajax({ type: "POST", - url: "vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4", + url: "<%= levelHash %>", data: { userId: optionValue }, diff --git a/SecurityShepherdCore/src/servlets/module/challenge/DirectObject1.java b/SecurityShepherdCore/src/servlets/module/challenge/DirectObject1.java index df4544ef3..6c082abb0 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/DirectObject1.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/DirectObject1.java @@ -5,6 +5,8 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -47,7 +49,6 @@ public class DirectObject1 extends HttpServlet private static org.apache.log4j.Logger log = Logger.getLogger(DirectObject1.class); private static String levelName = "Insecure Direct Object Challenge Challenge One"; private static String levelHash = "o9a450a64cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c"; - private static String levelResult = ""; //Stored in DB. Not user Specific /** * The user must abuse this functionality to reveal a hidden user. The result key is hidden in this users profile. * @param userId To be used in generating the HTML output @@ -58,6 +59,11 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) //Setting IpAddress To Log and taking header for original IP if forwarded from proxy ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectRef1", locale); + HttpSession ses = request.getSession(true); if(Validate.validateSession(ses)) { @@ -82,14 +88,14 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) log.debug("Found user: " + resultSet.getString(1)); String userName = resultSet.getString(1); String privateMessage = resultSet.getString(2); - htmlOutput = "

" + userName + "'s Message

" + + htmlOutput = "

" + userName + "'s " + bundle.getString("response.message") + "

" + "

" + privateMessage + "

"; } else { log.debug("No Profile Found"); Encoder encoder = ESAPI.encoder(); - htmlOutput = "

User: 404 - User Not Found

User '" + encoder.encodeForHTML(userId) + "' could not be found or does not exist.

"; + htmlOutput = "

" + bundle.getString("response.notFound") + "

" + bundle.getString("response.notFoundMessage.1") + " '" + encoder.encodeForHTML(userId) + "' " + bundle.getString("response.notFoundMessage.2") + "

"; } log.debug("Outputting HTML"); out.write(htmlOutput); @@ -97,7 +103,7 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) } catch(Exception e) { - out.write("An Error Occurred! You must be getting funky!"); + out.write(errors.getString("error.funky")); log.fatal(levelName + " - " + e.toString()); } } diff --git a/SecurityShepherdCore/src/servlets/module/challenge/DirectObject2.java b/SecurityShepherdCore/src/servlets/module/challenge/DirectObject2.java index b69b94194..f7338eb3e 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/DirectObject2.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/DirectObject2.java @@ -5,6 +5,8 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -47,7 +49,6 @@ public class DirectObject2 extends HttpServlet private static org.apache.log4j.Logger log = Logger.getLogger(DirectObject2.class); private static String levelName = "Insecure Direct Object Reference Challenge Two"; private static String levelHash = "vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4"; - private static String levelResult = ""; //Stored in DB. Not user Specific /** * The user must abuse this functionality to reveal a hidden user. The result key is hidden in this users profile. * @param userId To be used in generating the HTML output @@ -58,6 +59,12 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) //Setting IpAddress To Log and taking header for original IP if forwarded from proxy ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); HttpSession ses = request.getSession(true); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectRef2", locale); + if(Validate.validateSession(ses)) { ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); @@ -81,14 +88,14 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) log.debug("Found user: " + resultSet.getString(1)); String userName = resultSet.getString(1); String privateMessage = resultSet.getString(2); - htmlOutput = "

" + userName + "'s Message

" + + htmlOutput = "

" + userName + "'s " + bundle.getString("response.message") + "

" + "

" + privateMessage + "

"; } else { log.debug("No Profile Found"); Encoder encoder = ESAPI.encoder(); - htmlOutput = "

User: 404 - User Not Found

User '" + encoder.encodeForHTML(userId) + "' could not be found or does not exist.

"; + htmlOutput = "

" + bundle.getString("response.notFound") + "

" + bundle.getString("response.notFoundMessage.1") + " '" + encoder.encodeForHTML(userId) + "' " + bundle.getString("response.notFoundMessage.2") + "

"; } log.debug("Outputting HTML"); out.write(htmlOutput); @@ -96,7 +103,7 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) } catch(Exception e) { - out.write("An Error Occurred! You must be getting funky!"); + out.write(errors.getString("error.funky")); log.fatal(levelName + " - " + e.toString()); } } diff --git a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankCurrentBalance.java b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankCurrentBalance.java index c06f7e232..f56e8c302 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankCurrentBalance.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankCurrentBalance.java @@ -2,9 +2,9 @@ import java.io.IOException; import java.io.PrintWriter; -import java.sql.CallableStatement; -import java.sql.Connection; import java.sql.SQLException; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -16,7 +16,6 @@ import utils.ShepherdLogManager; import utils.Validate; -import dbProcs.Database; /** * Insecure Direct Object Reference Bank Challenge Get Balance Function @@ -46,7 +45,7 @@ public class DirectObjectBankCurrentBalance extends HttpServlet private static String levelName = "Insecure Direct Object Bank Challenge (Refresh Balance)"; private static String levelHash = "1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c"; /** - * This Servlet is used to register a new bank account + * This Servlet is used by users to register a new bank account in the Insecure Direct Object Bank Challenge. */ public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException @@ -54,6 +53,12 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) //Setting IpAddress To Log and taking header for original IP if forwarded from proxy ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); HttpSession ses = request.getSession(true); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectBank", locale); + if(Validate.validateSession(ses)) { ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); @@ -73,12 +78,12 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) } catch(SQLException e) { - out.write("An Error Occurred! You must be getting funky! Could not get Balance!"); + out.write(errors.getString("error.funky") + " " + bundle.getString("login.error.couldNotGetBalance")); log.fatal(levelName + " SQL Error - " + e.toString()); } catch(Exception e) { - out.write("An Error Occurred! You must be getting funky!"); + out.write(errors.getString("error.funky")); log.fatal(levelName + " - " + e.toString()); } } diff --git a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogin.java b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogin.java index fb08df7b3..34a0c48c1 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogin.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogin.java @@ -6,6 +6,8 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -47,10 +49,12 @@ public class DirectObjectBankLogin extends HttpServlet private static final long serialVersionUID = 1L; private static org.apache.log4j.Logger log = Logger.getLogger(DirectObjectBankLogin.class); private static String levelName = "Insecure Direct Object Bank Challenge"; - private static String levelHash = "1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c"; + public static String levelHash = "1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c"; private static String levelResult = "4a1df02af317270f844b56edc0c29a09f3dd39faad3e2a23393606769b2dfa35"; /** - * TODO - This Servlet is used to Sign In as Bank Account + * This Servlet is used in the Insecure Direct Object Bank to sign in to a specific bank account. + * It does this by checking the user DB credentials and then returns the bank form the user needs + * to call Bank Functions. */ public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException @@ -58,6 +62,12 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) //Setting IpAddress To Log and taking header for original IP if forwarded from proxy ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); HttpSession ses = request.getSession(true); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectBank", locale); + if(Validate.validateSession(ses)) { ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); @@ -83,13 +93,13 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) String accountNumber = resultSet.getString(1); log.debug("Found Account Number: " + accountNumber); ses.setAttribute("directObjectBankAccount", accountNumber); - htmlOutput += bankForm(accountNumber, applicationRoot, ses); + htmlOutput += bankForm(accountNumber, applicationRoot, ses, bundle, errors); } else { log.debug("Authentication Failed"); Encoder encoder = ESAPI.encoder(); - htmlOutput = "ERROR: User '" + encoder.encodeForHTML(accountHolder) + "' could not be logged in"; + htmlOutput = bundle.getString("login.authFailedMessage.1") + " '" + encoder.encodeForHTML(accountHolder) + "' " + bundle.getString("login.authFailedMessage.2"); } log.debug("Outputting HTML"); out.write(htmlOutput); @@ -97,12 +107,12 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) } catch(SQLException e) { - out.write("An Error Occurred! You must be getting funky! Could not get Balance!"); + out.write(errors.getString("error.funky") + " " + bundle.getString("login.error.couldNotGetBalance")); log.fatal(levelName + " SQL Error - " + e.toString()); } catch(Exception e) { - out.write("An Error Occurred! You must be getting funky!"); + out.write(errors.getString("error.funky")); log.fatal(levelName + " - " + e.toString()); } } @@ -112,36 +122,92 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) } } + /** + * Method used to return the bank interaction view for the user that is signed into the Direct Object Bank challenge + * @param accountNumber + * @param applicationRoot + * @param ses + * @param bundle + * @param errors + * @return + * @throws SQLException + */ + public static String bankForm(String accountNumber, String applicationRoot, HttpSession ses, ResourceBundle bundle, ResourceBundle errors) throws SQLException + { + Encoder encoder = ESAPI.encoder(); + float currentBalance = getAccountBalance(accountNumber, applicationRoot); + String bankForm = "

" + bundle.getString("bankForm.yourAccount") + "

" + + "

" + bundle.getString("bankForm.yourAccount.balance") + "

" + currentBalance + "

"; + if(currentBalance > 5000000) + { + //Level Complete As the user has more than 5000000 in account. Return Key + bankForm += "

" + bundle.getString("result.complete") + "

" + bundle.getString("result.wellDone") + "

" + + "" + bundle.getString("result.theKeyIs") + " " + encoder.encodeForHTML(Hash.generateUserSolution(levelResult, (String)ses.getAttribute("userName"))) + ""; + } + bankForm += "" + + "" + + "

" + bundle.getString("bankForm.transferFunds") + "

" + bundle.getString("bankForm.transferFunds.whatToDo") + "

" + + "
" + + "" + + "" + + "" + + "
" + bundle.getString("bankForm.recieverNumber") + "
" + bundle.getString("bankForm.amountToSend") + "
" + + "
" + + "

" + bundle.getString("bankForm.refreshBalance") + "

" + bundle.getString("bankForm.refreshBalance.whatToDo") + "

" + + "
" + + "
" + + "
" + + "
" + + "

" + bundle.getString("bankForm.logoutOfAccount") + "

" + bundle.getString("bankForm.logoutOfAccount.whatToDo") + "

" + + "
" + + "
" + + "
" + + "
"; + return bankForm; + } + + /** + * Method used to return the bank interaction view for the user that is signed into the Direct Object Bank challenge. This method pulls the local level translation from the session submitted + * @param accountNumber + * @param applicationRoot + * @param ses + * @return + * @throws SQLException + */ public static String bankForm(String accountNumber, String applicationRoot, HttpSession ses) throws SQLException { + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(ses)); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectBank", locale); + Encoder encoder = ESAPI.encoder(); float currentBalance = getAccountBalance(accountNumber, applicationRoot); - String bankForm = "

Your Account

" + - "

Your account balance is currently:

" + currentBalance + "

"; + String bankForm = "

" + bundle.getString("bankForm.yourAccount") + "

" + + "

" + bundle.getString("bankForm.yourAccount.balance") + "

" + currentBalance + "

"; if(currentBalance > 5000000) { //Level Complete As the user has more than 5000000 in account. Return Key - bankForm += "

Challenge Complete

Congradulations, you have sucessfully completed this challenge. Use the following result key at the top of the page to mark this level as complete in the sytem.

" - + "The result key for this challenge is " + encoder.encodeForHTML(Hash.generateUserSolution(levelResult, (String)ses.getAttribute("userName"))) + ""; + bankForm += "

" + bundle.getString("result.complete") + "

" + bundle.getString("result.wellDone") + "

" + + "" + bundle.getString("result.theKeyIs") + " " + encoder.encodeForHTML(Hash.generateUserSolution(levelResult, (String)ses.getAttribute("userName"))) + ""; } bankForm += "" + "" - + "

Transfer Funds

Use this form to send money to other accounts in this bank. All you need to do is enter their account number and the ammount you want to send!

" + + "

" + bundle.getString("bankForm.transferFunds") + "

" + bundle.getString("bankForm.transferFunds.whatToDo") + "

" + "
" - + "" - + "" - + "" - + "
Reciever Account Number:
Amount to Send:
" + + "" + + "" + + "" + + "
" + bundle.getString("bankForm.recieverNumber") + "
" + bundle.getString("bankForm.amountToSend") + "
" + "
" - + "

Refresh Balance

Use this form to refresh your balance above. That way you can see if any money came in recently!

" + + "

" + bundle.getString("bankForm.refreshBalance") + "

" + bundle.getString("bankForm.refreshBalance.whatToDo") + "

" + "
" - + "
" - + "
" + + "
" + + "
" + "
" - + "

Logout of Account

Use this form to sign out of your bank account when your done giving your money away.

" + + "

" + bundle.getString("bankForm.logoutOfAccount") + "

" + bundle.getString("bankForm.logoutOfAccount.whatToDo") + "

" + "
" - + "
" - + "
" + + "
" + + "" + "
"; return bankForm; } diff --git a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogout.java b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogout.java index b2ffd74b6..427e73236 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogout.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankLogout.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.io.PrintWriter; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -42,7 +44,7 @@ public class DirectObjectBankLogout extends HttpServlet private static String levelName = "Insecure Direct Object Bank Challenge (Logout)"; private static String levelHash = "1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c"; /** - * This Servlet is used to Sign out of a Bank Account + * This Servlet is used by a user to Sign out of a Bank Account Session in the Insecure Direct Bank Challenge */ public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException @@ -50,6 +52,11 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) //Setting IpAddress To Log and taking header for original IP if forwarded from proxy ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); HttpSession ses = request.getSession(true); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectBank", locale); + if(Validate.validateSession(ses)) { ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); @@ -57,7 +64,7 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) PrintWriter out = response.getWriter(); out.print(getServletInfo()); ses.removeAttribute("directObjectBankAccount"); - out.write("Logged Out"); + out.write(bundle.getString("logout.loggedOut")); } else { diff --git a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankRegistration.java b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankRegistration.java index 20ed2b4d8..07b3c8bbf 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankRegistration.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankRegistration.java @@ -5,6 +5,8 @@ import java.sql.CallableStatement; import java.sql.Connection; import java.sql.SQLException; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -54,6 +56,12 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) //Setting IpAddress To Log and taking header for original IP if forwarded from proxy ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); HttpSession ses = request.getSession(true); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectBank", locale); + if(Validate.validateSession(ses)) { ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); @@ -76,18 +84,18 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) callstmt.execute(); log.debug("Sucessfully ran create account procedure."); log.debug("Outputting HTML"); - htmlOutput = "User account has been registered! Please Sign in!"; + htmlOutput = bundle.getString("register.accountCreated"); out.write(htmlOutput); Database.closeConnection(conn); } catch(SQLException e) { - out.write("An Error Occurred! You must be getting funky! Could not create account!"); + out.write(errors.getString("error.funky") + " " + bundle.getString("register.error")); log.fatal(levelName + " SQL Error - " + e.toString()); } catch(Exception e) { - out.write("An Error Occurred! You must be getting funky!"); + out.write(errors.getString("error.funky")); log.fatal(levelName + " - " + e.toString()); } } diff --git a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankTransfer.java b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankTransfer.java index b77b2315d..401fec4e1 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankTransfer.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/DirectObjectBankTransfer.java @@ -5,6 +5,8 @@ import java.sql.CallableStatement; import java.sql.Connection; import java.sql.SQLException; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -46,7 +48,7 @@ public class DirectObjectBankTransfer extends HttpServlet private static String levelName = "Insecure Direct Object Bank Challenge (Transfer)"; private static String levelHash = "1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c"; /** - * This Servlet is used to register a new bank account + * This Servlet is used to transfer funds from one bank account to another, insecurely, in the Direct Object Reference Bank challenge */ public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException @@ -54,6 +56,12 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) //Setting IpAddress To Log and taking header for original IP if forwarded from proxy ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); HttpSession ses = request.getSession(true); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.directObject.directObjectBank", locale); + if(Validate.validateSession(ses)) { ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); @@ -84,10 +92,10 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) performTransfer = true; } else - errorMessage = "Your account does not have the necessary funds to transfer that amount."; + errorMessage = bundle.getString("transfer.error.notEnoughCash"); } else - errorMessage = "The amount being transfered must be greater than zero."; + errorMessage = bundle.getString("transfer.error.moreThanZero"); String htmlOutput = new String(); if(performTransfer) @@ -100,25 +108,25 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) callstmt.setFloat(3, tranferAmount); callstmt.execute(); log.debug("Sucessfully ran Transfer Funds procedure."); - htmlOutput = "Funds have been transfered sucessfully!"; + htmlOutput = bundle.getString("transfer.success"); Database.closeConnection(conn); } else { log.debug("Invalid Data Detected: " + errorMessage); - htmlOutput = "An Error Occured: " + errorMessage; + htmlOutput = bundle.getString("transfer.error.occurred") + " " + errorMessage; } log.debug("Outputting HTML"); out.write(htmlOutput); } catch(SQLException e) { - out.write("An Error Occurred! You must be getting funky! Could not get Transfer Funds!"); + out.write(errors.getString("error.funky") + " " + bundle.getString("transfer.error.couldNotTransfer")); log.fatal(levelName + " SQL Error - " + e.toString()); } catch(Exception e) { - out.write("An Error Occurred! You must be getting funky!"); + out.write(errors.getString("error.funky")); log.fatal(levelName + " - " + e.toString()); } } From 73b0ab555b8f58f9c579444757c4ecb0a859ef0b Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Wed, 23 Sep 2015 00:28:01 +0100 Subject: [PATCH 006/112] Making Menu Refresh Modular --- SecurityShepherdCore/src/dbProcs/Getter.java | 145 ++++++++++++------ SecurityShepherdCore/src/jsp/index.jsp | 103 +++++++++++-- .../src/servlets/module/FeedbackSubmit.java | 29 +--- .../src/servlets/module/SolutionSubmit.java | 2 +- 4 files changed, 193 insertions(+), 86 deletions(-) diff --git a/SecurityShepherdCore/src/dbProcs/Getter.java b/SecurityShepherdCore/src/dbProcs/Getter.java index bff1a2883..d0487c29f 100644 --- a/SecurityShepherdCore/src/dbProcs/Getter.java +++ b/SecurityShepherdCore/src/dbProcs/Getter.java @@ -733,53 +733,7 @@ public static String getIncrementalModules (String ApplicationRoot, String userI } //This is the script for menu interaction - output += ""; + output += ""; } catch(Exception e) { @@ -790,6 +744,103 @@ public static String getIncrementalModules (String ApplicationRoot, String userI return output; } + /** + * This method prepares the incremental module menu. This is when Security Shepherd is in "Game Mode". + * Users are presented with one uncompleted module at a time. This method does not return the JS script describing how the menu used should work + * @param ApplicationRoot The running context of the application. + * @param userId The user identifier of the user. + * @param csrfToken The cross site request forgery token + * @return A HTML menu of a users current module progress and a script for interaction with this menu + */ + public static String getIncrementalModulesWithoutScript (String ApplicationRoot, String userId, String lang, String csrfToken) + { + log.debug("*** Getter.getIncrementalChallengesWithoutScript ***"); + String output = new String(); + Encoder encoder = ESAPI.encoder(); + Connection conn = Database.getCoreConnection(ApplicationRoot); + + Locale.setDefault(new Locale("en")); + Locale locale = new Locale(lang); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.text", locale); + ResourceBundle levelNames = ResourceBundle.getBundle("i18n.moduleGenerics.moduleNames", locale); + + try + { + CallableStatement callstmt = conn.prepareCall("call moduleIncrementalInfo(?)"); + callstmt.setString(1, userId); + log.debug("Gathering moduleIncrementalInfo ResultSet"); + ResultSet modules = callstmt.executeQuery(); + log.debug("Opening Result Set from moduleIncrementalInfo"); + boolean lastRow = false; + boolean completedModules = false; + + + //Preparing first Category header; "Completed" + output = "
  • \n" + + "
  • "; + } + else + { + //NO completed modules, so dont show any... + output = new String(); + } + + //Second category - Uncompleted + output += "" + + "" + + ""; + output += "
  • "; + } + } + + if(!lastRow) //If true, then the user has completed all challenges + { + output += "

    " + bundle.getString("getter.button.finished") + "

    \n" + + ""; + } + if(output.isEmpty()) //If this method has gone so far without any output, create a error message + { + output = "
  • " + bundle.getString("getter.button.noModulesFound") + "
  • "; + } + else //final tags to ensure valid HTML + { + log.debug("Appending End tags"); + //output += ""; //Commented Out to prevent Search Box being pushed into Footer + } + } + catch(Exception e) + { + log.error("Challenge Retrieval: " + e.toString()); + } + Database.closeConnection(conn); + log.debug("*** END getIncrementalChallengesWithoutScript() ***"); + return output; + } + /** * Use to return the current progress of a class in JSON format with information like userid, user name and score * @param applicationRoot The current running context of the application diff --git a/SecurityShepherdCore/src/jsp/index.jsp b/SecurityShepherdCore/src/jsp/index.jsp index 866fe2c30..99a3d1bec 100644 --- a/SecurityShepherdCore/src/jsp/index.jsp +++ b/SecurityShepherdCore/src/jsp/index.jsp @@ -191,7 +191,7 @@ if (request.getSession() != null) <% } else { if(ModulePlan.isIncrementalFloor()){ %>
    - <%= Getter.getIncrementalModules(ApplicationRoot, (String)ses.getAttribute("userStamp"), ses.getAttribute("lang").toString() ,csrfToken) %> + <%= Getter.getIncrementalModulesWithoutScript(ApplicationRoot, (String)ses.getAttribute("userStamp"), ses.getAttribute("lang").toString() ,csrfToken) %>
    <% } else {%>
  • @@ -200,6 +200,7 @@ if (request.getSession() != null) <% } } //End of Module List Output %> +
    ...">
    @@ -207,17 +208,22 @@ if (request.getSession() != null) "; } - - private static String refreshMenuScript1 = ""; } diff --git a/SecurityShepherdCore/src/servlets/module/SolutionSubmit.java b/SecurityShepherdCore/src/servlets/module/SolutionSubmit.java index 91eb9a9ec..5c54382d7 100644 --- a/SecurityShepherdCore/src/servlets/module/SolutionSubmit.java +++ b/SecurityShepherdCore/src/servlets/module/SolutionSubmit.java @@ -151,7 +151,7 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) encoder.encodeForHTML(result) + " completed! Congratulations."); htmlOutput += "

    "; if(ModulePlan.isIncrementalFloor()) - htmlOutput += FeedbackSubmit.refreshMenuScript(encoder.encodeForHTML((String)tokenParmeter)); + htmlOutput += FeedbackSubmit.refreshMenuScript(encoder.encodeForHTML((String)tokenParmeter), "Refresh Error"); log.debug("Resetting user's Bad Submisison count to 0"); Setter.resetBadSubmission(ApplicationRoot, userId); out.write(htmlOutput); From 9321bd271401b773f4b79c195389f98fa604d1e0 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Wed, 23 Sep 2015 18:55:04 +0100 Subject: [PATCH 007/112] Sidemenu Refresh & Reinitialise in More Scenarios --- SecurityShepherdCore/src/dbProcs/Getter.java | 2 +- .../moduleManagement/changeLevelLayout.jsp | 7 + .../moduleManagement/incrementalModules.jsp | 109 -------- .../moduleManagement/openCloseByCategory.jsp | 6 + .../jsp/admin/moduleManagement/setStatus.jsp | 3 + SecurityShepherdCore/src/jsp/index.jsp | 241 +++++++++++------- SecurityShepherdCore/src/jsp/js/toggle.js | 25 -- .../src/servlets/module/FeedbackSubmit.java | 4 +- .../src/servlets/module/RefreshMenu.java | 50 +++- .../src/servlets/module/SolutionSubmit.java | 4 +- 10 files changed, 202 insertions(+), 249 deletions(-) delete mode 100644 SecurityShepherdCore/src/jsp/admin/moduleManagement/incrementalModules.jsp delete mode 100644 SecurityShepherdCore/src/jsp/js/toggle.js diff --git a/SecurityShepherdCore/src/dbProcs/Getter.java b/SecurityShepherdCore/src/dbProcs/Getter.java index d0487c29f..ce1e39852 100644 --- a/SecurityShepherdCore/src/dbProcs/Getter.java +++ b/SecurityShepherdCore/src/dbProcs/Getter.java @@ -733,7 +733,7 @@ public static String getIncrementalModules (String ApplicationRoot, String userI } //This is the script for menu interaction - output += ""; + output += ""; } catch(Exception e) { diff --git a/SecurityShepherdCore/src/jsp/admin/moduleManagement/changeLevelLayout.jsp b/SecurityShepherdCore/src/jsp/admin/moduleManagement/changeLevelLayout.jsp index 7533fa14d..9009077c6 100644 --- a/SecurityShepherdCore/src/jsp/admin/moduleManagement/changeLevelLayout.jsp +++ b/SecurityShepherdCore/src/jsp/admin/moduleManagement/changeLevelLayout.jsp @@ -99,6 +99,7 @@ if (request.getSession() != null) //Session If
    <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> diff --git a/SecurityShepherdCore/src/jsp/admin/moduleManagement/incrementalModules.jsp b/SecurityShepherdCore/src/jsp/admin/moduleManagement/incrementalModules.jsp deleted file mode 100644 index 0454af48a..000000000 --- a/SecurityShepherdCore/src/jsp/admin/moduleManagement/incrementalModules.jsp +++ /dev/null @@ -1,109 +0,0 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> - -<% - ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), "DEBUG: incrementalModules.jsp *************************"); - -/** - * This file is part of the Security Shepherd Project. - * - * The Security Shepherd project is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version.
    - * - * The Security Shepherd project is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details.
    - * - * You should have received a copy of the GNU General Public License - * along with the Security Shepherd project. If not, see . - * - * @author Mark Denihan - */ - -if (request.getSession() != null) -{ -HttpSession ses = request.getSession(); -Getter get = new Getter(); -//Getting CSRF Token from client -Cookie tokenCookie = null; -try -{ - tokenCookie = Validate.getToken(request.getCookies()); -} -catch(Exception htmlE) -{ - ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), "DEBUG(incrementalModules.jsp): tokenCookie Error:" + htmlE.toString()); -} -// validateAdminSession ensures a valid session, and valid administrator credentials -// Also, if tokenCookie != null, then the page is good to continue loading -// Token is now validated when accessing admin pages to stop attackers causing other users to tigger logs of access attempts -Object tokenParmeter = request.getParameter("csrfToken"); -if(Validate.validateAdminSession(ses, tokenCookie, tokenParmeter)) -{ - //Logging Username - ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), "Accessed by: " + ses.getAttribute("userName").toString(), ses.getAttribute("userName")); -// Getting Session Variables -//This encoder should escape all output to prevent XSS attacks. This should be performed everywhere for safety -Encoder encoder = ESAPI.encoder(); -String csrfToken = encoder.encodeForHTMLAttribute(tokenCookie.getValue()); -String ApplicationRoot = getServletContext().getRealPath(""); -%> -
    -

    CTF Mode

    -
    -
    - <% if (!ModulePlan.incrementalFloor) {%> -

    If you enable the CTF floor plan, players will have to complete lessons to unlock links to the next module.

    -
    - - - -
    - -
    - - <% } else { %> -

    CTF Mode is already enabled!

    - <% } %> -
    - <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> -
    -
    - <% -} -else -{ -response.sendRedirect("../../loggedOutSheep.html"); -} -} -else -{ -response.sendRedirect("../../loggedOutSheep.html"); -} -%> \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/admin/moduleManagement/openCloseByCategory.jsp b/SecurityShepherdCore/src/jsp/admin/moduleManagement/openCloseByCategory.jsp index 9bda5d1be..1b01f3970 100644 --- a/SecurityShepherdCore/src/jsp/admin/moduleManagement/openCloseByCategory.jsp +++ b/SecurityShepherdCore/src/jsp/admin/moduleManagement/openCloseByCategory.jsp @@ -116,6 +116,9 @@ String ApplicationRoot = getServletContext().getRealPath(""); }, 1000); }); }); + var theRefreshError = "Could not Refresh Menu"; + //Refresh the Side Menu + refreshSideMenu(theCsrfToken, theRefreshError); }); $("#openCategories").click(function(){ @@ -156,6 +159,9 @@ String ApplicationRoot = getServletContext().getRealPath(""); }, 1000); }); }); + var theRefreshError = "Could not Refresh Menu"; + //Refresh the Side Menu + refreshSideMenu(theCsrfToken, theRefreshError); }); <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> diff --git a/SecurityShepherdCore/src/jsp/admin/moduleManagement/setStatus.jsp b/SecurityShepherdCore/src/jsp/admin/moduleManagement/setStatus.jsp index 6aaff6b58..2b90f87ca 100644 --- a/SecurityShepherdCore/src/jsp/admin/moduleManagement/setStatus.jsp +++ b/SecurityShepherdCore/src/jsp/admin/moduleManagement/setStatus.jsp @@ -114,6 +114,9 @@ String ApplicationRoot = getServletContext().getRealPath(""); }, 1000); }); }); + var theRefreshError = "Could not Refresh Menu"; + //Refresh the Side Menu + refreshSideMenu(theCsrfToken, theRefreshError); }); <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> diff --git a/SecurityShepherdCore/src/jsp/index.jsp b/SecurityShepherdCore/src/jsp/index.jsp index 99a3d1bec..75529bd56 100644 --- a/SecurityShepherdCore/src/jsp/index.jsp +++ b/SecurityShepherdCore/src/jsp/index.jsp @@ -175,6 +175,7 @@ if (request.getSession() != null) <% } %>
    +
    <% if(ModulePlan.isOpenFloor()) { %>
  • @@ -190,15 +191,14 @@ if (request.getSession() != null)
  • <% } else { if(ModulePlan.isIncrementalFloor()){ %> -
    <%= Getter.getIncrementalModulesWithoutScript(ApplicationRoot, (String)ses.getAttribute("userStamp"), ses.getAttribute("lang").toString() ,csrfToken) %> -
    <% } else {%>
  • <%= Getter.getTournamentModules(ApplicationRoot, (String)ses.getAttribute("userStamp"), lang) %>
  • <% } } //End of Module List Output %> +
    @@ -258,7 +258,6 @@ if (request.getSession() != null)
    - 7Q~HMguV2z@qo~Gr(g4%Y(g= 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
    - * This file is part of the Security Shepherd Project. - * - * The Security Shepherd project is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version.
    - * - * The Security Shepherd project is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details.
    - * - * You should have received a copy of the GNU General Public License - * along with the Security Shepherd project. If not, see . - * @author Mark Denihan - * - */ -public class XssLesson -extends HttpServlet -{ - private static final long serialVersionUID = 1L; - private static org.apache.log4j.Logger log = Logger.getLogger(XssLesson.class); - private static String levelName = "XSS Lesson"; - private static String levelHash = "zf8ed52591579339e590e0726c7b24009f3ac54cdff1b81a65db1688d86efb3a"; - /** - * Cross Site Request Forgery safe Reflected XSS vulnerability. cannot be remotely deployed, and therefore only is executable against the person initiating the function. - * @param searchTerm To be spat back out at the user - */ - public void doPost (HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException - { - //Setting IpAddress To Log and taking header for original IP if forwarded from proxy - ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); - log.debug(levelName + " Servlet Accessed"); - PrintWriter out = response.getWriter(); - out.print(getServletInfo()); - - //Translation Stuff - Locale locale = new Locale(Validate.validateLanguage(request.getSession())); - ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); - ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.lessons.xss", locale); - - try - { - HttpSession ses = request.getSession(true); - if(Validate.validateSession(ses)) - { - ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); - log.debug(levelName + " accessed by: " + ses.getAttribute("userName").toString()); - Cookie tokenCookie = Validate.getToken(request.getCookies()); - Object tokenParmeter = request.getParameter("csrfToken"); - if(Validate.validateTokens(tokenCookie, tokenParmeter)) - { - String searchTerm = request.getParameter("searchTerm"); - log.debug("User Submitted - " + searchTerm); - String htmlOutput = new String(); - if(FindXSS.search(searchTerm)) - { - String theHash = this.getClass().getSimpleName(); - log.debug("XSS Lesson Completed!"); - Encoder encoder = ESAPI.encoder(); - htmlOutput = "

    " + bundle.getString("result.wellDone") + "

    " + - "

    " + bundle.getString("result.youDidIt") + "
    " + - "" + bundle.getString("result.resultKey") + " " + - encoder.encodeForHTML( - Hash.generateUserSolution(Getter.getModuleResultFromHash(getServletContext().getRealPath(""), levelHash), (String)ses.getAttribute("userName")) - ) + - ""; - } - log.debug("Adding searchTerm to Html: " + searchTerm); - htmlOutput += "

    " + bundle.getString("response.searchResults") + "

    " + - "

    " + bundle.getString("response.noResults") + " '" + - searchTerm + - "'

    "; - log.debug("Outputting HTML"); - out.write(htmlOutput); - } - } - else - { - log.error(levelName + " accessed with no session"); - out.write(errors.getString("error.noSession")); - } - } - catch(Exception e) - { - out.write(errors.getString("error.funky")); - log.fatal(levelName + " - " + e.toString()); - } - log.debug("End of " + levelName + " Servlet"); - } -} +package servlets.module.lesson; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Locale; +import java.util.ResourceBundle; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.log4j.Logger; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Encoder; + +import dbProcs.Getter; +import utils.FindXSS; +import utils.Hash; +import utils.ShepherdLogManager; +import utils.Validate; + +/** + * Cross Site Scripting Lesson + *

    + * This file is part of the Security Shepherd Project. + * + * The Security Shepherd project is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version.
    + * + * The Security Shepherd project is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details.
    + * + * You should have received a copy of the GNU General Public License + * along with the Security Shepherd project. If not, see . + * @author Mark Denihan + * + */ +public class XssLesson +extends HttpServlet +{ + private static final long serialVersionUID = 1L; + private static org.apache.log4j.Logger log = Logger.getLogger(XssLesson.class); + private static String levelName = "XSS Lesson"; + private static String levelHash = "zf8ed52591579339e590e0726c7b24009f3ac54cdff1b81a65db1688d86efb3a"; + /** + * Cross Site Request Forgery safe Reflected XSS vulnerability. cannot be remotely deployed, and therefore only is executable against the person initiating the function. + * @param searchTerm To be spat back out at the user + */ + public void doPost (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + //Setting IpAddress To Log and taking header for original IP if forwarded from proxy + ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); + log.debug(levelName + " Servlet Accessed"); + PrintWriter out = response.getWriter(); + out.print(getServletInfo()); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.lessons.xss", locale); + + try + { + HttpSession ses = request.getSession(true); + if(Validate.validateSession(ses)) + { + ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); + log.debug(levelName + " accessed by: " + ses.getAttribute("userName").toString()); + Cookie tokenCookie = Validate.getToken(request.getCookies()); + Object tokenParmeter = request.getParameter("csrfToken"); + if(Validate.validateTokens(tokenCookie, tokenParmeter)) + { + String searchTerm = request.getParameter("searchTerm"); + log.debug("User Submitted - " + searchTerm); + String htmlOutput = new String(); + if(FindXSS.search(searchTerm)) + { + String theHash = this.getClass().getSimpleName(); + log.debug("XSS Lesson Completed!"); + Encoder encoder = ESAPI.encoder(); + htmlOutput = "

    " + bundle.getString("result.wellDone") + "

    " + + "

    " + bundle.getString("result.youDidIt") + "
    " + + "" + bundle.getString("result.resultKey") + + Hash.generateUserSolution(Getter.getModuleResultFromHash(getServletContext().getRealPath(""), levelHash), (String)ses.getAttribute("userName")); + } + log.debug("Adding searchTerm to Html: " + searchTerm); + htmlOutput += "

    " + bundle.getString("response.searchResults") + "

    " + + "

    " + bundle.getString("response.noResults") + " '" + + searchTerm + + "'

    "; + log.debug("Outputting HTML"); + out.write(htmlOutput); + } + } + else + { + log.error(levelName + " accessed with no session"); + out.write(errors.getString("error.noSession")); + } + } + catch(Exception e) + { + out.write(errors.getString("error.funky")); + log.fatal(levelName + " - " + e.toString()); + } + log.debug("End of " + levelName + " Servlet"); + } +} diff --git a/SecurityShepherdCore/src/utils/Hash.java b/SecurityShepherdCore/src/utils/Hash.java index 1df7f0e19..b3db1f935 100644 --- a/SecurityShepherdCore/src/utils/Hash.java +++ b/SecurityShepherdCore/src/utils/Hash.java @@ -1,418 +1,428 @@ -package utils; - -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import javax.servlet.http.Cookie; - -import org.apache.log4j.Logger; -import org.apache.commons.codec.binary.Base64; - -/** - * Class used for miscellaneous Hash use - *

    - * This file is part of the Security Shepherd Project. - * - * The Security Shepherd project is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version.
    - * - * The Security Shepherd project is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details.
    - * - * You should have received a copy of the GNU General Public License - * along with the Security Shepherd project. If not, see . - * @author Mark Denihan - * - */ -public class Hash -{ - private static org.apache.log4j.Logger log = Logger.getLogger(Hash.class); - public static String userNameKey = randomKeyLengthString(); - private static String encryptionKeySalt = randomKeyLengthString(); - private static String serverEncryptionKey = randomKeyLengthString(); - - /** - * Merges current server encryption key with user name based encryption key to create user specific key - * @param userName - * @return - */ - private static String createUserSpecificEncryptionKey (String userNameKey) throws Exception - { - if(userNameKey.length() != 16) - throw new Exception("User Name key must be 16 bytes long"); - else - { - byte[] serverKey = serverEncryptionKey.getBytes(); - byte[] userKey = userNameKey.getBytes(); - for(int i = 0; i < userKey.length; i++) - { - userKey[i] = (byte)(userKey[i] + serverKey[i]); - } - return new String(userKey, Charset.forName("US-ASCII")); - } - } - - /** - * Decrypts data using specific key and ciphertext - * @param key Encryption Key (Must be 16 Bytes) - * @param encrypted Ciphertext to decrypt - * @return Plaintext decrypted from submitted ciphertext and key - * @throws GeneralSecurityException - */ - public static String decrypt(String key, String encrypted) - throws GeneralSecurityException - { - byte[] raw = key.getBytes(Charset.forName("US-ASCII")); - if (raw.length != 16) - { - throw new IllegalArgumentException("Invalid key size."); - } - SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); - return new String(original, Charset.forName("US-ASCII")); - } - - /** - * Specifically decrypts encrypted user names - * @param encyptedUserName Encrypted user name - * @return Decrypted User name - */ - public static String decryptUserName (String encyptedUserName) - { - String decryptedUserName = new String(); - try - { - decryptedUserName = Hash.decrypt(Hash.userNameKey, encyptedUserName); - log.debug("Decrypted user-name to: " + decryptedUserName); - } - catch (GeneralSecurityException e) - { - log.error("Could not decrypt user name: " + e.toString()); - } - return decryptedUserName; - } - - public static String decryptUserSpecificSolution(String userNameKey, String encryptedSolution) - throws GeneralSecurityException, Exception - { - try - { - String key = createUserSpecificEncryptionKey(userNameKey); - byte[] raw = key.getBytes(Charset.forName("US-ASCII")); - if (raw.length != 16) - { - throw new IllegalArgumentException("Invalid key size."); - } - SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] original = cipher.doFinal(Base64.decodeBase64(encryptedSolution)); - return new String(original, Charset.forName("US-ASCII")); - } - catch (Exception e) - { - throw new Exception("Decryption Failure: Could not Craft User Key or Ciphertext was Bad"); - } - } - - /** - * Encrypts plain text into cipher text based on encryption key - * @param key Encryption Key (Must be 16 Bytes) - * @param value Plain text to encrypt - * @return Cipher text based on plain text and key submitted - * @throws GeneralSecurityException - */ - public static String encrypt(String key, String value) - throws GeneralSecurityException - { - byte[] raw = key.getBytes(Charset.forName("US-ASCII")); - if (raw.length != 16) - { - throw new IllegalArgumentException("Invalid key size."); - } - SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - return Base64.encodeBase64String(cipher.doFinal(value.getBytes(Charset.forName("US-ASCII")))); - } - - /** - * Generates user solution based on the user name stored in their encypted cookie - * @param baseKey The stored result key for the module - * @param cookies All of a users session cookies. The encrypted user name is pulled out from this array - * @return User Specific Solution - */ - public static String generateUserSolution(String baseKey, Cookie[] cookies) - { - log.debug("Getting user Specific key"); - String cookieName = "JSESSIONID3"; - Cookie myCookie = null; - String toReturn = "Key Should be here! Please refresh the home page and try again!"; - log.debug("Looking for key cookie"); - if (cookies != null) - { - for (int i = 0; i < cookies.length; i++) - { - log.debug("Looking at: " + cookies[i].getName() + " = " + cookies[i].getValue()); - if (cookies[i].getName().equals(cookieName)) - { - myCookie = cookies[i]; - log.debug("Found Cookie with value: " + myCookie.getValue()); - break; - } - } - try - { - String decryptedUserName = Hash.decrypt(Hash.userNameKey, myCookie.getValue()); - log.debug("Decrypted UserName: " + decryptedUserName); - String key = createUserSpecificEncryptionKey(Validate.validateEncryptionKey(decryptedUserName)); - toReturn = Hash.encrypt(key, baseKey + getCurrentSalt()); - log.debug("Returning: " + toReturn); - } - catch (Exception e) - { - log.error("Encryption Failure: " + e.toString()); - toReturn = "Key Should be here! Please refresh the home page and try again! If that doesn't work, sign in and out again!"; - } - } - return "" + toReturn + ""; - } - - /** - * Generates user specific solution based on the user name provided and server side encryption keys - * @param baseKey The stored result key for the module - * @param userSalt The User Specific Encryption Salt (Based on user name) - * @return User Specific Solution - */ - public static String generateUserSolution(String baseKey, String userSalt) - { - log.debug("Generating key for " + userSalt); - String toReturn = "Key Should be here! Please refresh the home page and try again!"; - try - { - String key = createUserSpecificEncryptionKey(Validate.validateEncryptionKey(userSalt)); - toReturn = Hash.encrypt(key, baseKey + getCurrentSalt()); - log.debug("Returning: " + toReturn); - } - catch (Exception e) - { - log.error("Encrypt Failure: " + e.toString()); - toReturn = "Key Should be here! Please refresh the home page and try again!"; - } - return toReturn; - } - - /** - * This is used when encrypting/decrypting the salt. If this is bypassed characters can be lost in encryption process. - * @return - */ - public static String getCurrentSalt() - { - return Base64.encodeBase64String(encryptionKeySalt.getBytes()); - } - - /** - * Outputs a MD5 digest - * @param toHash String to hash - * @return Hashed String - */ - public static String md5ThisString (String toHash) - { - String hashed = null; - byte[] byteArray = new byte[512]; - MessageDigest md; - try - { - md = MessageDigest.getInstance("MD5"); - log.debug("Hashing Value With " + md.getAlgorithm()); - byteArray = toHash.getBytes(); - md.update(byteArray); - byteArray = md.digest(); - } - catch (NoSuchAlgorithmException e) - { - log.fatal("Could not Find MD5 Algorithm: " + e.toString()); - } - hashed = new String(byteArray, Charset.forName("US-ASCII")); - - return hashed; - } - - /** - * Creates a psedorandom base64 string - * @return Random String - */ - public static String randomBase64String() - { - String result = new String(); - try - { - byte byteArray[] = new byte[256]; - SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); - - Base64 base64 = new Base64(); - psn1.setSeed(psn1.nextLong()); - psn1.nextBytes(byteArray); - result = new String(byteArray, Charset.forName("US-ASCII")); - result = base64.encode(thisString(thisString(byteArray.toString())).getBytes()).toString(); - log.debug("Generated String = " + result); - } - catch(Exception e) - { - log.error("Random Number Error : " + e.toString()); - } - return result; - } - - public static String randomKeyLengthString() - { - String result = new String(); - try - { - byte byteArray[] = new byte[16]; - SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); - psn1.setSeed(psn1.nextLong()); - psn1.nextBytes(byteArray); - result = new String(byteArray, Charset.forName("US-ASCII")); - //log.debug("Generated Key = " + result); - if(result.length() != 16) - { - log.error("Generated Key is the incorrect Length: Shortening "); - result = result.substring(0, 15); - if(result.length() != 16) - log.fatal("Encryption key length is Still not Right"); - } - } - catch(Exception e) - { - log.error("Random Number Error : " + e.toString()); - } - return result; - } - - /** - * Creates a psedorandom string - * @return Random String - */ - public static String randomString() - { - String result = new String(); - try - { - byte byteArray[] = new byte[16]; - SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); - psn1.setSeed(psn1.nextLong()); - psn1.nextBytes(byteArray); - BigInteger bigInt = new BigInteger(byteArray); - result = bigInt.toString(); - log.debug("Generated String = " + result); - - } - catch(Exception e) - { - log.error("Random Number Error : " + e.toString()); - } - return result; - } - - /** - * Generates a small psedorandom string - * @return Random String - */ - public static String smallRandomString() - { - String result = new String(); - try - { - byte byteArray[] = new byte[4]; - SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); - psn1.setSeed(psn1.nextLong()); - psn1.nextBytes(byteArray); - BigInteger bigInt = new BigInteger(byteArray); - result = bigInt.toString(); - log.debug("Generated String = " + result); - - } - catch(Exception e) - { - log.error("Random Number Error : " + e.toString()); - } - return result; - } - - /** - * Outputs a SHA256 digest - * @param toHash String to hash - * @return Hashed string - */ - public static String thisString (String toHash) - { - String hashed = null; - byte[] byteArray = new byte[256]; - MessageDigest md; - try - { - md = MessageDigest.getInstance("SHA"); - log.debug("Hashing Value With " + md.getAlgorithm()); - byteArray = toHash.getBytes(); - md.update(byteArray); - byteArray = md.digest(); - } - catch (NoSuchAlgorithmException e) - { - log.fatal("Could not Find SHA Algorithm: " + e.toString()); - } - hashed = new String(byteArray, Charset.forName("US-ASCII")); - - return hashed; - } - - - public static String validateEncryptionKey(String userSalt) - { - String newKey = new String(); - int keySize = userSalt.length(); - if(keySize == 16) - { - //log.debug("Key Already Valid"); - newKey = userSalt; - } - else - { - if(keySize > 16) - { - //log.debug("Key too Long..."); - newKey = userSalt.substring(0, 16); - } - else // Shorter than 16 - { - //log.debug("Key too Short..."); - newKey = userSalt; - int howManyTimes = (16 / keySize) - 1; - //log.debug("Repeating String " + howManyTimes + " times"); - for(int i = 0; i < howManyTimes; i++) - newKey += userSalt; - keySize = newKey.length(); - int toAdd = 16 - keySize; - //log.debug("Adding " + toAdd + " more characters"); - newKey = newKey.concat(userSalt.substring(0, toAdd)); - } - } - log.debug("Encryption key is '" + newKey + "'"); - return newKey; - } - -} +package utils; + +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.Cookie; + +import org.apache.log4j.Logger; +import org.apache.commons.codec.binary.Base64; + +/** + * Class used for miscellaneous Hash use + *

    + * This file is part of the Security Shepherd Project. + * + * The Security Shepherd project is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version.
    + * + * The Security Shepherd project is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details.
    + * + * You should have received a copy of the GNU General Public License + * along with the Security Shepherd project. If not, see . + * @author Mark Denihan + * + */ +public class Hash +{ + private static org.apache.log4j.Logger log = Logger.getLogger(Hash.class); + public static String userNameKey = randomKeyLengthString(); + private static String encryptionKeySalt = randomKeyLengthString(); + private static String serverEncryptionKey = randomKeyLengthString(); + + /** + * Merges current server encryption key with user name based encryption key to create user specific key + * @param userName + * @return + */ + private static String createUserSpecificEncryptionKey (String userNameKey) throws Exception + { + if(userNameKey.length() != 16) + throw new Exception("User Name key must be 16 bytes long"); + else + { + byte[] serverKey = serverEncryptionKey.getBytes(); + byte[] userKey = userNameKey.getBytes(); + for(int i = 0; i < userKey.length; i++) + { + userKey[i] = (byte)(userKey[i] + serverKey[i]); + } + return new String(userKey, Charset.forName("US-ASCII")); + } + } + + /** + * Decrypts data using specific key and ciphertext + * @param key Encryption Key (Must be 16 Bytes) + * @param encrypted Ciphertext to decrypt + * @return Plaintext decrypted from submitted ciphertext and key + * @throws GeneralSecurityException + */ + public static String decrypt(String key, String encrypted) + throws GeneralSecurityException + { + byte[] raw = key.getBytes(Charset.forName("US-ASCII")); + if (raw.length != 16) + { + throw new IllegalArgumentException("Invalid key size."); + } + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); + byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); + return new String(original, Charset.forName("US-ASCII")); + } + + /** + * Specifically decrypts encrypted user names + * @param encyptedUserName Encrypted user name + * @return Decrypted User name + */ + public static String decryptUserName (String encyptedUserName) + { + String decryptedUserName = new String(); + try + { + decryptedUserName = Hash.decrypt(Hash.userNameKey, encyptedUserName); + log.debug("Decrypted user-name to: " + decryptedUserName); + } + catch (GeneralSecurityException e) + { + log.error("Could not decrypt user name: " + e.toString()); + } + return decryptedUserName; + } + + public static String decryptUserSpecificSolution(String userNameKey, String encryptedSolution) + throws GeneralSecurityException, Exception + { + try + { + String key = createUserSpecificEncryptionKey(userNameKey); + byte[] raw = key.getBytes(Charset.forName("US-ASCII")); + if (raw.length != 16) + { + throw new IllegalArgumentException("Invalid key size."); + } + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); + byte[] original = cipher.doFinal(Base64.decodeBase64(encryptedSolution)); + return new String(original, Charset.forName("US-ASCII")); + } + catch (Exception e) + { + throw new Exception("Decryption Failure: Could not Craft User Key or Ciphertext was Bad"); + } + } + + /** + * Encrypts plain text into cipher text based on encryption key + * @param key Encryption Key (Must be 16 Bytes) + * @param value Plain text to encrypt + * @return Cipher text based on plain text and key submitted + * @throws GeneralSecurityException + */ + public static String encrypt(String key, String value) + throws GeneralSecurityException + { + byte[] raw = key.getBytes(Charset.forName("US-ASCII")); + if (raw.length != 16) + { + throw new IllegalArgumentException("Invalid key size."); + } + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); + return Base64.encodeBase64String(cipher.doFinal(value.getBytes(Charset.forName("US-ASCII")))); + } + + /** + * Generates user solution based on the user name stored in their encypted cookie + * @param baseKey The stored result key for the module + * @param cookies All of a users session cookies. The encrypted user name is pulled out from this array + * @return User Specific Solution + */ + public static String generateUserSolution(String baseKey, Cookie[] cookies) + { + log.debug("Getting user Specific key"); + String cookieName = "JSESSIONID3"; + Cookie myCookie = null; + String toReturn = "Key Should be here! Please refresh the home page and try again!"; + log.debug("Looking for key cookie"); + if (cookies != null) + { + for (int i = 0; i < cookies.length; i++) + { + log.debug("Looking at: " + cookies[i].getName() + " = " + cookies[i].getValue()); + if (cookies[i].getName().equals(cookieName)) + { + myCookie = cookies[i]; + log.debug("Found Cookie with value: " + myCookie.getValue()); + break; + } + } + try + { + String decryptedUserName = Hash.decrypt(Hash.userNameKey, myCookie.getValue()); + log.debug("Decrypted UserName: " + decryptedUserName); + String key = createUserSpecificEncryptionKey(Validate.validateEncryptionKey(decryptedUserName)); + toReturn = Hash.encrypt(key, baseKey + getCurrentSalt()); + log.debug("Returning: " + toReturn); + } + catch (Exception e) + { + log.error("Encryption Failure: " + e.toString()); + toReturn = "Key Should be here! Please refresh the home page and try again! If that doesn't work, sign in and out again!"; + } + } + return "" + toReturn + ""; + } + + /** + * Generates user specific solution based on the user name provided and server side encryption keys + * @param baseKey The stored result key for the module + * @param userSalt The User Specific Encryption Salt (Based on user name) + * @return User Specific Solution + */ + public static String generateUserSolution(String baseKey, String userSalt) + { + log.debug("Generating key for " + userSalt); + String toReturn = "Key Should be here! Please refresh the home page and try again!"; + String zeroClipboard = ""; + try + { + String key = createUserSpecificEncryptionKey(Validate.validateEncryptionKey(userSalt)); + String forLog = Hash.encrypt(key, baseKey + getCurrentSalt()); + toReturn = "" + + "
    " + + "" + + zeroClipboard; + log.debug("Returning: " + forLog); + } + catch (Exception e) + { + log.error("Encrypt Failure: " + e.toString()); + toReturn = "Key Should be here! Please refresh the home page and try again!"; + } + return toReturn; + } + + /** + * This is used when encrypting/decrypting the salt. If this is bypassed characters can be lost in encryption process. + * @return + */ + public static String getCurrentSalt() + { + return Base64.encodeBase64String(encryptionKeySalt.getBytes()); + } + + /** + * Outputs a MD5 digest + * @param toHash String to hash + * @return Hashed String + */ + public static String md5ThisString (String toHash) + { + String hashed = null; + byte[] byteArray = new byte[512]; + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + log.debug("Hashing Value With " + md.getAlgorithm()); + byteArray = toHash.getBytes(); + md.update(byteArray); + byteArray = md.digest(); + } + catch (NoSuchAlgorithmException e) + { + log.fatal("Could not Find MD5 Algorithm: " + e.toString()); + } + hashed = new String(byteArray, Charset.forName("US-ASCII")); + + return hashed; + } + + /** + * Creates a psedorandom base64 string + * @return Random String + */ + public static String randomBase64String() + { + String result = new String(); + try + { + byte byteArray[] = new byte[256]; + SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); + + Base64 base64 = new Base64(); + psn1.setSeed(psn1.nextLong()); + psn1.nextBytes(byteArray); + result = new String(byteArray, Charset.forName("US-ASCII")); + result = base64.encode(thisString(thisString(byteArray.toString())).getBytes()).toString(); + log.debug("Generated String = " + result); + } + catch(Exception e) + { + log.error("Random Number Error : " + e.toString()); + } + return result; + } + + public static String randomKeyLengthString() + { + String result = new String(); + try + { + byte byteArray[] = new byte[16]; + SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); + psn1.setSeed(psn1.nextLong()); + psn1.nextBytes(byteArray); + result = new String(byteArray, Charset.forName("US-ASCII")); + //log.debug("Generated Key = " + result); + if(result.length() != 16) + { + log.error("Generated Key is the incorrect Length: Shortening "); + result = result.substring(0, 15); + if(result.length() != 16) + log.fatal("Encryption key length is Still not Right"); + } + } + catch(Exception e) + { + log.error("Random Number Error : " + e.toString()); + } + return result; + } + + /** + * Creates a psedorandom string + * @return Random String + */ + public static String randomString() + { + String result = new String(); + try + { + byte byteArray[] = new byte[16]; + SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); + psn1.setSeed(psn1.nextLong()); + psn1.nextBytes(byteArray); + BigInteger bigInt = new BigInteger(byteArray); + result = bigInt.toString(); + log.debug("Generated String = " + result); + + } + catch(Exception e) + { + log.error("Random Number Error : " + e.toString()); + } + return result; + } + + /** + * Generates a small psedorandom string + * @return Random String + */ + public static String smallRandomString() + { + String result = new String(); + try + { + byte byteArray[] = new byte[4]; + SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG"); + psn1.setSeed(psn1.nextLong()); + psn1.nextBytes(byteArray); + BigInteger bigInt = new BigInteger(byteArray); + result = bigInt.toString(); + log.debug("Generated String = " + result); + + } + catch(Exception e) + { + log.error("Random Number Error : " + e.toString()); + } + return result; + } + + /** + * Outputs a SHA256 digest + * @param toHash String to hash + * @return Hashed string + */ + public static String thisString (String toHash) + { + String hashed = null; + byte[] byteArray = new byte[256]; + MessageDigest md; + try + { + md = MessageDigest.getInstance("SHA"); + log.debug("Hashing Value With " + md.getAlgorithm()); + byteArray = toHash.getBytes(); + md.update(byteArray); + byteArray = md.digest(); + } + catch (NoSuchAlgorithmException e) + { + log.fatal("Could not Find SHA Algorithm: " + e.toString()); + } + hashed = new String(byteArray, Charset.forName("US-ASCII")); + + return hashed; + } + + + public static String validateEncryptionKey(String userSalt) + { + String newKey = new String(); + int keySize = userSalt.length(); + if(keySize == 16) + { + //log.debug("Key Already Valid"); + newKey = userSalt; + } + else + { + if(keySize > 16) + { + //log.debug("Key too Long..."); + newKey = userSalt.substring(0, 16); + } + else // Shorter than 16 + { + //log.debug("Key too Short..."); + newKey = userSalt; + int howManyTimes = (16 / keySize) - 1; + //log.debug("Repeating String " + howManyTimes + " times"); + for(int i = 0; i < howManyTimes; i++) + newKey += userSalt; + keySize = newKey.length(); + int toAdd = 16 - keySize; + //log.debug("Adding " + toAdd + " more characters"); + newKey = newKey.concat(userSalt.substring(0, toAdd)); + } + } + log.debug("Encryption key is '" + newKey + "'"); + return newKey; + } + +} From 378ba67c7a83570a535cdbc7bb15c04c0e09803a Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Sat, 26 Sep 2015 03:17:00 +0100 Subject: [PATCH 025/112] zeroclipboard folder error fix --- SecurityShepherdCore/src/jsp/js/zeroclipboard | 1 - 1 file changed, 1 deletion(-) delete mode 160000 SecurityShepherdCore/src/jsp/js/zeroclipboard diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard b/SecurityShepherdCore/src/jsp/js/zeroclipboard deleted file mode 160000 index ef9d62b60..000000000 --- a/SecurityShepherdCore/src/jsp/js/zeroclipboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ef9d62b60f748fbcb7bd5844eadabd489f2b7558 From d15d5d9b13a3c801d4f1a93b2ea5760ce76c0738 Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Sat, 26 Sep 2015 03:19:55 +0100 Subject: [PATCH 026/112] Adding resources for copy to clipboard --- .../src/jsp/js/zeroclipboard/.jshintrc | 62 + .../src/jsp/js/zeroclipboard/.travis.yml | 5 + .../js/zeroclipboard/ZeroClipboard.Core.js | 2046 +++++++++++++ .../zeroclipboard/ZeroClipboard.Core.min.js | 10 + .../zeroclipboard/ZeroClipboard.Core.min.map | 1 + .../src/jsp/js/zeroclipboard/ZeroClipboard.js | 2611 +++++++++++++++++ .../jsp/js/zeroclipboard/ZeroClipboard.min.js | 10 + .../js/zeroclipboard/ZeroClipboard.min.map | 1 + .../jsp/js/zeroclipboard/ZeroClipboard.swf | Bin 0 -> 6584 bytes .../jsp/js/zeroclipboard/zeroclipboardMain.js | 4 + 10 files changed, 4750 insertions(+) create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/.jshintrc create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/.travis.yml create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.js create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.min.js create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.min.map create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.js create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.js create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.map create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.swf create mode 100644 SecurityShepherdCore/src/jsp/js/zeroclipboard/zeroclipboardMain.js diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/.jshintrc b/SecurityShepherdCore/src/jsp/js/zeroclipboard/.jshintrc new file mode 100644 index 000000000..ab4232d37 --- /dev/null +++ b/SecurityShepherdCore/src/jsp/js/zeroclipboard/.jshintrc @@ -0,0 +1,62 @@ +{ + /* Enforcing options */ + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "es3": true, + "es5": false, + "forin": true, + "freeze": true, + "immed": true, + "indent": 2, + "latedef": false, /* IDEAL: `true` */ + "newcap": true, + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": "double", + "undef": false, /* IDEAL: `true` */ + "unused": false, /* IDEAL: `true` */ + "strict": false, /* IDEAL: `true` */ + "trailing": true, + "maxparams": 4, + "maxdepth": 5, + "maxstatements": 25, + "maxlen": false, /* IDEAL: 120? */ + + + /* Relaxing options */ + "asi": false, + "boss": false, + "debug": false, + "eqnull": true, + "esnext": false, + "evil": false, + "expr": false, + "funcscope": false, + "gcl": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": false, + "maxerr": 50, + "moz": false, + "multistr": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "smarttabs": false, + "shadow": false, + "sub": false, + "supernew": false, + "validthis": false, + "noyield": false, + + /* Environments */ + "browser": true +} \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/.travis.yml b/SecurityShepherdCore/src/jsp/js/zeroclipboard/.travis.yml new file mode 100644 index 000000000..6ef04eb80 --- /dev/null +++ b/SecurityShepherdCore/src/jsp/js/zeroclipboard/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "0.10" +before_script: + - npm install -g grunt-cli diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.js b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.js new file mode 100644 index 000000000..a65187e77 --- /dev/null +++ b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.js @@ -0,0 +1,2046 @@ +/*! + * ZeroClipboard + * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. + * Copyright (c) 2009-2015 Jon Rohan, James M. Greene + * Licensed MIT + * http://zeroclipboard.org/ + * v2.3.0-beta.1 + */ +(function(window, undefined) { + "use strict"; + /** + * Store references to critically important global functions that may be + * overridden on certain web pages. + */ + var _window = window, _document = _window.document, _navigator = _window.navigator, _setTimeout = _window.setTimeout, _clearTimeout = _window.clearTimeout, _setInterval = _window.setInterval, _clearInterval = _window.clearInterval, _getComputedStyle = _window.getComputedStyle, _encodeURIComponent = _window.encodeURIComponent, _ActiveXObject = _window.ActiveXObject, _Error = _window.Error, _parseInt = _window.Number.parseInt || _window.parseInt, _parseFloat = _window.Number.parseFloat || _window.parseFloat, _isNaN = _window.Number.isNaN || _window.isNaN, _now = _window.Date.now, _keys = _window.Object.keys, _defineProperty = _window.Object.defineProperty, _hasOwn = _window.Object.prototype.hasOwnProperty, _slice = _window.Array.prototype.slice, _unwrap = function() { + var unwrapper = function(el) { + return el; + }; + if (typeof _window.wrap === "function" && typeof _window.unwrap === "function") { + try { + var div = _document.createElement("div"); + var unwrappedDiv = _window.unwrap(div); + if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) { + unwrapper = _window.unwrap; + } + } catch (e) {} + } + return unwrapper; + }(); + /** + * Convert an `arguments` object into an Array. + * + * @returns The arguments as an Array + * @private + */ + var _args = function(argumentsObj) { + return _slice.call(argumentsObj, 0); + }; + /** + * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`. + * + * @returns The target object, augmented + * @private + */ + var _extend = function() { + var i, len, arg, prop, src, copy, args = _args(arguments), target = args[0] || {}; + for (i = 1, len = args.length; i < len; i++) { + if ((arg = args[i]) != null) { + for (prop in arg) { + if (_hasOwn.call(arg, prop)) { + src = target[prop]; + copy = arg[prop]; + if (target !== copy && copy !== undefined) { + target[prop] = copy; + } + } + } + } + } + return target; + }; + /** + * Return a deep copy of the source object or array. + * + * @returns Object or Array + * @private + */ + var _deepCopy = function(source) { + var copy, i, len, prop; + if (typeof source !== "object" || source == null || typeof source.nodeType === "number") { + copy = source; + } else if (typeof source.length === "number") { + copy = []; + for (i = 0, len = source.length; i < len; i++) { + if (_hasOwn.call(source, i)) { + copy[i] = _deepCopy(source[i]); + } + } + } else { + copy = {}; + for (prop in source) { + if (_hasOwn.call(source, prop)) { + copy[prop] = _deepCopy(source[prop]); + } + } + } + return copy; + }; + /** + * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep. + * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to + * be kept. + * + * @returns A new filtered object. + * @private + */ + var _pick = function(obj, keys) { + var newObj = {}; + for (var i = 0, len = keys.length; i < len; i++) { + if (keys[i] in obj) { + newObj[keys[i]] = obj[keys[i]]; + } + } + return newObj; + }; + /** + * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit. + * The inverse of `_pick`. + * + * @returns A new filtered object. + * @private + */ + var _omit = function(obj, keys) { + var newObj = {}; + for (var prop in obj) { + if (keys.indexOf(prop) === -1) { + newObj[prop] = obj[prop]; + } + } + return newObj; + }; + /** + * Remove all owned, enumerable properties from an object. + * + * @returns The original object without its owned, enumerable properties. + * @private + */ + var _deleteOwnProperties = function(obj) { + if (obj) { + for (var prop in obj) { + if (_hasOwn.call(obj, prop)) { + delete obj[prop]; + } + } + } + return obj; + }; + /** + * Determine if an element is contained within another element. + * + * @returns Boolean + * @private + */ + var _containedBy = function(el, ancestorEl) { + if (el && el.nodeType === 1 && el.ownerDocument && ancestorEl && (ancestorEl.nodeType === 1 && ancestorEl.ownerDocument && ancestorEl.ownerDocument === el.ownerDocument || ancestorEl.nodeType === 9 && !ancestorEl.ownerDocument && ancestorEl === el.ownerDocument)) { + do { + if (el === ancestorEl) { + return true; + } + el = el.parentNode; + } while (el); + } + return false; + }; + /** + * Get the URL path's parent directory. + * + * @returns String or `undefined` + * @private + */ + var _getDirPathOfUrl = function(url) { + var dir; + if (typeof url === "string" && url) { + dir = url.split("#")[0].split("?")[0]; + dir = url.slice(0, url.lastIndexOf("/") + 1); + } + return dir; + }; + /** + * Get the current script's URL by throwing an `Error` and analyzing it. + * + * @returns String or `undefined` + * @private + */ + var _getCurrentScriptUrlFromErrorStack = function(stack) { + var url, matches; + if (typeof stack === "string" && stack) { + matches = stack.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/); + if (matches && matches[1]) { + url = matches[1]; + } else { + matches = stack.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/); + if (matches && matches[1]) { + url = matches[1]; + } + } + } + return url; + }; + /** + * Get the current script's URL by throwing an `Error` and analyzing it. + * + * @returns String or `undefined` + * @private + */ + var _getCurrentScriptUrlFromError = function() { + var url, err; + try { + throw new _Error(); + } catch (e) { + err = e; + } + if (err) { + url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack); + } + return url; + }; + /** + * Get the current script's URL. + * + * @returns String or `undefined` + * @private + */ + var _getCurrentScriptUrl = function() { + var jsPath, scripts, i; + if (_document.currentScript && (jsPath = _document.currentScript.src)) { + return jsPath; + } + scripts = _document.getElementsByTagName("script"); + if (scripts.length === 1) { + return scripts[0].src || undefined; + } + if ("readyState" in scripts[0]) { + for (i = scripts.length; i--; ) { + if (scripts[i].readyState === "interactive" && (jsPath = scripts[i].src)) { + return jsPath; + } + } + } + if (_document.readyState === "loading" && (jsPath = scripts[scripts.length - 1].src)) { + return jsPath; + } + if (jsPath = _getCurrentScriptUrlFromError()) { + return jsPath; + } + return undefined; + }; + /** + * Get the unanimous parent directory of ALL script tags. + * If any script tags are either (a) inline or (b) from differing parent + * directories, this method must return `undefined`. + * + * @returns String or `undefined` + * @private + */ + var _getUnanimousScriptParentDir = function() { + var i, jsDir, jsPath, scripts = _document.getElementsByTagName("script"); + for (i = scripts.length; i--; ) { + if (!(jsPath = scripts[i].src)) { + jsDir = null; + break; + } + jsPath = _getDirPathOfUrl(jsPath); + if (jsDir == null) { + jsDir = jsPath; + } else if (jsDir !== jsPath) { + jsDir = null; + break; + } + } + return jsDir || undefined; + }; + /** + * Get the presumed location of the "ZeroClipboard.swf" file, based on the location + * of the executing JavaScript file (e.g. "ZeroClipboard.js", etc.). + * + * @returns String + * @private + */ + var _getDefaultSwfPath = function() { + var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || ""; + return jsDir + "ZeroClipboard.swf"; + }; + /** + * Is the client's operating system some version of Windows? + * + * @returns Boolean + * @private + */ + var _isWindows = function() { + var isWindowsRegex = /win(dows|[\s]?(nt|me|ce|xp|vista|[\d]+))/i; + return !!_navigator && (isWindowsRegex.test(_navigator.appVersion || "") || isWindowsRegex.test(_navigator.platform || "") || (_navigator.userAgent || "").indexOf("Windows") !== -1); + }; + /** + * Keep track of if the page is framed (in an `iframe`). This can never change. + * @private + */ + var _pageIsFramed = function() { + return window.opener == null && (!!window.top && window != window.top || !!window.parent && window != window.parent); + }(); + /** + * Keep track of the state of the Flash object. + * @private + */ + var _flashState = { + bridge: null, + version: "0.0.0", + pluginType: "unknown", + disabled: null, + outdated: null, + sandboxed: null, + unavailable: null, + degraded: null, + deactivated: null, + overdue: null, + ready: null + }; + /** + * The minimum Flash Player version required to use ZeroClipboard completely. + * @readonly + * @private + */ + var _minimumFlashVersion = "11.0.0"; + /** + * The ZeroClipboard library version number, as reported by Flash, at the time the SWF was compiled. + */ + var _zcSwfVersion; + /** + * Keep track of all event listener registrations. + * @private + */ + var _handlers = {}; + /** + * Keep track of the currently activated element. + * @private + */ + var _currentElement; + /** + * Keep track of the element that was activated when a `copy` process started. + * @private + */ + var _copyTarget; + /** + * Keep track of data for the pending clipboard transaction. + * @private + */ + var _clipData = {}; + /** + * Keep track of data formats for the pending clipboard transaction. + * @private + */ + var _clipDataFormatMap = null; + /** + * Keep track of the Flash availability check timeout. + * @private + */ + var _flashCheckTimeout = 0; + /** + * Keep track of SWF network errors interval polling. + * @private + */ + var _swfFallbackCheckInterval = 0; + /** + * The `message` store for events + * @private + */ + var _eventMessages = { + ready: "Flash communication is established", + error: { + "flash-disabled": "Flash is disabled or not installed. May also be attempting to run Flash in a sandboxed iframe, which is impossible.", + "flash-outdated": "Flash is too outdated to support ZeroClipboard", + "flash-sandboxed": "Attempting to run Flash in a sandboxed iframe, which is impossible", + "flash-unavailable": "Flash is unable to communicate bidirectionally with JavaScript", + "flash-degraded": "Flash is unable to preserve data fidelity when communicating with JavaScript", + "flash-deactivated": "Flash is too outdated for your browser and/or is configured as click-to-activate.\nThis may also mean that the ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity.\nMay also be attempting to run Flash in a sandboxed iframe, which is impossible.", + "flash-overdue": "Flash communication was established but NOT within the acceptable time limit", + "version-mismatch": "ZeroClipboard JS version number does not match ZeroClipboard SWF version number", + "clipboard-error": "At least one error was thrown while ZeroClipboard was attempting to inject your data into the clipboard", + "config-mismatch": "ZeroClipboard configuration does not match Flash's reality", + "swf-not-found": "The ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity" + } + }; + /** + * The `name`s of `error` events that can only occur is Flash has at least + * been able to load the SWF successfully. + * @private + */ + var _errorsThatOnlyOccurAfterFlashLoads = [ "flash-unavailable", "flash-degraded", "flash-overdue", "version-mismatch", "config-mismatch", "clipboard-error" ]; + /** + * The `name`s of `error` events that should likely result in the `_flashState` + * variable's property values being updated. + * @private + */ + var _flashStateErrorNames = [ "flash-disabled", "flash-outdated", "flash-sandboxed", "flash-unavailable", "flash-degraded", "flash-deactivated", "flash-overdue" ]; + /** + * A RegExp to match the `name` property of `error` events related to Flash. + * @private + */ + var _flashStateErrorNameMatchingRegex = new RegExp("^flash-(" + _flashStateErrorNames.map(function(errorName) { + return errorName.replace(/^flash-/, ""); + }).join("|") + ")$"); + /** + * A RegExp to match the `name` property of `error` events related to Flash, + * which is enabled. + * @private + */ + var _flashStateEnabledErrorNameMatchingRegex = new RegExp("^flash-(" + _flashStateErrorNames.slice(1).map(function(errorName) { + return errorName.replace(/^flash-/, ""); + }).join("|") + ")$"); + /** + * ZeroClipboard configuration defaults for the Core module. + * @private + */ + var _globalConfig = { + swfPath: _getDefaultSwfPath(), + trustedDomains: window.location.host ? [ window.location.host ] : [], + cacheBust: true, + forceEnhancedClipboard: false, + flashLoadTimeout: 3e4, + autoActivate: true, + bubbleEvents: true, + fixLineEndings: true, + containerId: "global-zeroclipboard-html-bridge", + containerClass: "global-zeroclipboard-container", + swfObjectId: "global-zeroclipboard-flash-bridge", + hoverClass: "zeroclipboard-is-hover", + activeClass: "zeroclipboard-is-active", + forceHandCursor: false, + title: null, + zIndex: 999999999 + }; + /** + * The underlying implementation of `ZeroClipboard.config`. + * @private + */ + var _config = function(options) { + if (typeof options === "object" && options !== null) { + for (var prop in options) { + if (_hasOwn.call(options, prop)) { + if (/^(?:forceHandCursor|title|zIndex|bubbleEvents|fixLineEndings)$/.test(prop)) { + _globalConfig[prop] = options[prop]; + } else if (_flashState.bridge == null) { + if (prop === "containerId" || prop === "swfObjectId") { + if (_isValidHtml4Id(options[prop])) { + _globalConfig[prop] = options[prop]; + } else { + throw new Error("The specified `" + prop + "` value is not valid as an HTML4 Element ID"); + } + } else { + _globalConfig[prop] = options[prop]; + } + } + } + } + } + if (typeof options === "string" && options) { + if (_hasOwn.call(_globalConfig, options)) { + return _globalConfig[options]; + } + return; + } + return _deepCopy(_globalConfig); + }; + /** + * The underlying implementation of `ZeroClipboard.state`. + * @private + */ + var _state = function() { + _detectSandbox(); + return { + browser: _pick(_navigator, [ "userAgent", "platform", "appName", "appVersion" ]), + flash: _omit(_flashState, [ "bridge" ]), + zeroclipboard: { + version: ZeroClipboard.version, + config: ZeroClipboard.config() + } + }; + }; + /** + * The underlying implementation of `ZeroClipboard.isFlashUnusable`. + * @private + */ + var _isFlashUnusable = function() { + return !!(_flashState.disabled || _flashState.outdated || _flashState.sandboxed || _flashState.unavailable || _flashState.degraded || _flashState.deactivated); + }; + /** + * The underlying implementation of `ZeroClipboard.on`. + * @private + */ + var _on = function(eventType, listener) { + var i, len, events, added = {}; + if (typeof eventType === "string" && eventType) { + events = eventType.toLowerCase().split(/\s+/); + } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") { + for (i in eventType) { + if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") { + ZeroClipboard.on(i, eventType[i]); + } + } + } + if (events && events.length) { + for (i = 0, len = events.length; i < len; i++) { + eventType = events[i].replace(/^on/, ""); + added[eventType] = true; + if (!_handlers[eventType]) { + _handlers[eventType] = []; + } + _handlers[eventType].push(listener); + } + if (added.ready && _flashState.ready) { + ZeroClipboard.emit({ + type: "ready" + }); + } + if (added.error) { + for (i = 0, len = _flashStateErrorNames.length; i < len; i++) { + if (_flashState[_flashStateErrorNames[i].replace(/^flash-/, "")] === true) { + ZeroClipboard.emit({ + type: "error", + name: _flashStateErrorNames[i] + }); + break; + } + } + if (_zcSwfVersion !== undefined && ZeroClipboard.version !== _zcSwfVersion) { + ZeroClipboard.emit({ + type: "error", + name: "version-mismatch", + jsVersion: ZeroClipboard.version, + swfVersion: _zcSwfVersion + }); + } + } + } + return ZeroClipboard; + }; + /** + * The underlying implementation of `ZeroClipboard.off`. + * @private + */ + var _off = function(eventType, listener) { + var i, len, foundIndex, events, perEventHandlers; + if (arguments.length === 0) { + events = _keys(_handlers); + } else if (typeof eventType === "string" && eventType) { + events = eventType.split(/\s+/); + } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") { + for (i in eventType) { + if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") { + ZeroClipboard.off(i, eventType[i]); + } + } + } + if (events && events.length) { + for (i = 0, len = events.length; i < len; i++) { + eventType = events[i].toLowerCase().replace(/^on/, ""); + perEventHandlers = _handlers[eventType]; + if (perEventHandlers && perEventHandlers.length) { + if (listener) { + foundIndex = perEventHandlers.indexOf(listener); + while (foundIndex !== -1) { + perEventHandlers.splice(foundIndex, 1); + foundIndex = perEventHandlers.indexOf(listener, foundIndex); + } + } else { + perEventHandlers.length = 0; + } + } + } + } + return ZeroClipboard; + }; + /** + * The underlying implementation of `ZeroClipboard.handlers`. + * @private + */ + var _listeners = function(eventType) { + var copy; + if (typeof eventType === "string" && eventType) { + copy = _deepCopy(_handlers[eventType]) || null; + } else { + copy = _deepCopy(_handlers); + } + return copy; + }; + /** + * The underlying implementation of `ZeroClipboard.emit`. + * @private + */ + var _emit = function(event) { + var eventCopy, returnVal, tmp; + event = _createEvent(event); + if (!event) { + return; + } + if (_preprocessEvent(event)) { + return; + } + if (event.type === "ready" && _flashState.overdue === true) { + return ZeroClipboard.emit({ + type: "error", + name: "flash-overdue" + }); + } + eventCopy = _extend({}, event); + _dispatchCallbacks.call(this, eventCopy); + if (event.type === "copy") { + tmp = _mapClipDataToFlash(_clipData); + returnVal = tmp.data; + _clipDataFormatMap = tmp.formatMap; + } + return returnVal; + }; + /** + * The underlying implementation of `ZeroClipboard.create`. + * @private + */ + var _create = function() { + var previousState = _flashState.sandboxed; + _detectSandbox(); + if (typeof _flashState.ready !== "boolean") { + _flashState.ready = false; + } + if (_flashState.sandboxed !== previousState && _flashState.sandboxed === true) { + _flashState.ready = false; + ZeroClipboard.emit({ + type: "error", + name: "flash-sandboxed" + }); + } else if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) { + var maxWait = _globalConfig.flashLoadTimeout; + if (typeof maxWait === "number" && maxWait >= 0) { + _flashCheckTimeout = _setTimeout(function() { + if (typeof _flashState.deactivated !== "boolean") { + _flashState.deactivated = true; + } + if (_flashState.deactivated === true) { + ZeroClipboard.emit({ + type: "error", + name: "flash-deactivated" + }); + } + }, maxWait); + } + _flashState.overdue = false; + _embedSwf(); + } + }; + /** + * The underlying implementation of `ZeroClipboard.destroy`. + * @private + */ + var _destroy = function() { + ZeroClipboard.clearData(); + ZeroClipboard.blur(); + ZeroClipboard.emit("destroy"); + _unembedSwf(); + ZeroClipboard.off(); + }; + /** + * The underlying implementation of `ZeroClipboard.setData`. + * @private + */ + var _setData = function(format, data) { + var dataObj; + if (typeof format === "object" && format && typeof data === "undefined") { + dataObj = format; + ZeroClipboard.clearData(); + } else if (typeof format === "string" && format) { + dataObj = {}; + dataObj[format] = data; + } else { + return; + } + for (var dataFormat in dataObj) { + if (typeof dataFormat === "string" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === "string" && dataObj[dataFormat]) { + _clipData[dataFormat] = _fixLineEndings(dataObj[dataFormat]); + } + } + }; + /** + * The underlying implementation of `ZeroClipboard.clearData`. + * @private + */ + var _clearData = function(format) { + if (typeof format === "undefined") { + _deleteOwnProperties(_clipData); + _clipDataFormatMap = null; + } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) { + delete _clipData[format]; + } + }; + /** + * The underlying implementation of `ZeroClipboard.getData`. + * @private + */ + var _getData = function(format) { + if (typeof format === "undefined") { + return _deepCopy(_clipData); + } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) { + return _clipData[format]; + } + }; + /** + * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`. + * @private + */ + var _focus = function(element) { + if (!(element && element.nodeType === 1)) { + return; + } + if (_currentElement) { + _removeClass(_currentElement, _globalConfig.activeClass); + if (_currentElement !== element) { + _removeClass(_currentElement, _globalConfig.hoverClass); + } + } + _currentElement = element; + _addClass(element, _globalConfig.hoverClass); + var newTitle = element.getAttribute("title") || _globalConfig.title; + if (typeof newTitle === "string" && newTitle) { + var htmlBridge = _getHtmlBridge(_flashState.bridge); + if (htmlBridge) { + htmlBridge.setAttribute("title", newTitle); + } + } + var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, "cursor") === "pointer"; + _setHandCursor(useHandCursor); + _reposition(); + }; + /** + * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`. + * @private + */ + var _blur = function() { + var htmlBridge = _getHtmlBridge(_flashState.bridge); + if (htmlBridge) { + htmlBridge.removeAttribute("title"); + htmlBridge.style.left = "0px"; + htmlBridge.style.top = "-9999px"; + htmlBridge.style.width = "1px"; + htmlBridge.style.height = "1px"; + } + if (_currentElement) { + _removeClass(_currentElement, _globalConfig.hoverClass); + _removeClass(_currentElement, _globalConfig.activeClass); + _currentElement = null; + } + }; + /** + * The underlying implementation of `ZeroClipboard.activeElement`. + * @private + */ + var _activeElement = function() { + return _currentElement || null; + }; + /** + * Check if a value is a valid HTML4 `ID` or `Name` token. + * @private + */ + var _isValidHtml4Id = function(id) { + return typeof id === "string" && id && /^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(id); + }; + /** + * Create or update an `event` object, based on the `eventType`. + * @private + */ + var _createEvent = function(event) { + var eventType; + if (typeof event === "string" && event) { + eventType = event; + event = {}; + } else if (typeof event === "object" && event && typeof event.type === "string" && event.type) { + eventType = event.type; + } + if (!eventType) { + return; + } + eventType = eventType.toLowerCase(); + if (!event.target && (/^(copy|aftercopy|_click)$/.test(eventType) || eventType === "error" && event.name === "clipboard-error")) { + event.target = _copyTarget; + } + _extend(event, { + type: eventType, + target: event.target || _currentElement || null, + relatedTarget: event.relatedTarget || null, + currentTarget: _flashState && _flashState.bridge || null, + timeStamp: event.timeStamp || _now() || null + }); + var msg = _eventMessages[event.type]; + if (event.type === "error" && event.name && msg) { + msg = msg[event.name]; + } + if (msg) { + event.message = msg; + } + if (event.type === "ready") { + _extend(event, { + target: null, + version: _flashState.version + }); + } + if (event.type === "error") { + if (_flashStateErrorNameMatchingRegex.test(event.name)) { + _extend(event, { + target: null, + minimumVersion: _minimumFlashVersion + }); + } + if (_flashStateEnabledErrorNameMatchingRegex.test(event.name)) { + _extend(event, { + version: _flashState.version + }); + } + } + if (event.type === "copy") { + event.clipboardData = { + setData: ZeroClipboard.setData, + clearData: ZeroClipboard.clearData + }; + } + if (event.type === "aftercopy") { + event = _mapClipResultsFromFlash(event, _clipDataFormatMap); + } + if (event.target && !event.relatedTarget) { + event.relatedTarget = _getRelatedTarget(event.target); + } + return _addMouseData(event); + }; + /** + * Get a relatedTarget from the target's `data-clipboard-target` attribute + * @private + */ + var _getRelatedTarget = function(targetEl) { + var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute("data-clipboard-target"); + return relatedTargetId ? _document.getElementById(relatedTargetId) : null; + }; + /** + * Add element and position data to `MouseEvent` instances + * @private + */ + var _addMouseData = function(event) { + if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) { + var srcElement = event.target; + var fromElement = event.type === "_mouseover" && event.relatedTarget ? event.relatedTarget : undefined; + var toElement = event.type === "_mouseout" && event.relatedTarget ? event.relatedTarget : undefined; + var pos = _getElementPosition(srcElement); + var screenLeft = _window.screenLeft || _window.screenX || 0; + var screenTop = _window.screenTop || _window.screenY || 0; + var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft; + var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop; + var pageX = pos.left + (typeof event._stageX === "number" ? event._stageX : 0); + var pageY = pos.top + (typeof event._stageY === "number" ? event._stageY : 0); + var clientX = pageX - scrollLeft; + var clientY = pageY - scrollTop; + var screenX = screenLeft + clientX; + var screenY = screenTop + clientY; + var moveX = typeof event.movementX === "number" ? event.movementX : 0; + var moveY = typeof event.movementY === "number" ? event.movementY : 0; + delete event._stageX; + delete event._stageY; + _extend(event, { + srcElement: srcElement, + fromElement: fromElement, + toElement: toElement, + screenX: screenX, + screenY: screenY, + pageX: pageX, + pageY: pageY, + clientX: clientX, + clientY: clientY, + x: clientX, + y: clientY, + movementX: moveX, + movementY: moveY, + offsetX: 0, + offsetY: 0, + layerX: 0, + layerY: 0 + }); + } + return event; + }; + /** + * Determine if an event's registered handlers should be execute synchronously or asynchronously. + * + * @returns {boolean} + * @private + */ + var _shouldPerformAsync = function(event) { + var eventType = event && typeof event.type === "string" && event.type || ""; + return !/^(?:(?:before)?copy|destroy)$/.test(eventType); + }; + /** + * Control if a callback should be executed asynchronously or not. + * + * @returns `undefined` + * @private + */ + var _dispatchCallback = function(func, context, args, async) { + if (async) { + _setTimeout(function() { + func.apply(context, args); + }, 0); + } else { + func.apply(context, args); + } + }; + /** + * Handle the actual dispatching of events to client instances. + * + * @returns `undefined` + * @private + */ + var _dispatchCallbacks = function(event) { + if (!(typeof event === "object" && event && event.type)) { + return; + } + var async = _shouldPerformAsync(event); + var wildcardTypeHandlers = _handlers["*"] || []; + var specificTypeHandlers = _handlers[event.type] || []; + var handlers = wildcardTypeHandlers.concat(specificTypeHandlers); + if (handlers && handlers.length) { + var i, len, func, context, eventCopy, originalContext = this; + for (i = 0, len = handlers.length; i < len; i++) { + func = handlers[i]; + context = originalContext; + if (typeof func === "string" && typeof _window[func] === "function") { + func = _window[func]; + } + if (typeof func === "object" && func && typeof func.handleEvent === "function") { + context = func; + func = func.handleEvent; + } + if (typeof func === "function") { + eventCopy = _extend({}, event); + _dispatchCallback(func, context, [ eventCopy ], async); + } + } + } + return this; + }; + /** + * Check an `error` event's `name` property to see if Flash has + * already loaded, which rules out possible `iframe` sandboxing. + * @private + */ + var _getSandboxStatusFromErrorEvent = function(event) { + var isSandboxed = null; + if (_pageIsFramed === false || event && event.type === "error" && event.name && _errorsThatOnlyOccurAfterFlashLoads.indexOf(event.name) !== -1) { + isSandboxed = false; + } + return isSandboxed; + }; + /** + * Preprocess any special behaviors, reactions, or state changes after receiving this event. + * Executes only once per event emitted, NOT once per client. + * @private + */ + var _preprocessEvent = function(event) { + var element = event.target || _currentElement || null; + var sourceIsSwf = event._source === "swf"; + delete event._source; + switch (event.type) { + case "error": + var isSandboxed = event.name === "flash-sandboxed" || _getSandboxStatusFromErrorEvent(event); + if (typeof isSandboxed === "boolean") { + _flashState.sandboxed = isSandboxed; + } + if (_flashStateErrorNames.indexOf(event.name) !== -1) { + _extend(_flashState, { + disabled: event.name === "flash-disabled", + outdated: event.name === "flash-outdated", + unavailable: event.name === "flash-unavailable", + degraded: event.name === "flash-degraded", + deactivated: event.name === "flash-deactivated", + overdue: event.name === "flash-overdue", + ready: false + }); + } else if (event.name === "version-mismatch") { + _zcSwfVersion = event.swfVersion; + _extend(_flashState, { + disabled: false, + outdated: false, + unavailable: false, + degraded: false, + deactivated: false, + overdue: false, + ready: false + }); + } + _clearTimeoutsAndPolling(); + break; + + case "ready": + _zcSwfVersion = event.swfVersion; + var wasDeactivated = _flashState.deactivated === true; + _extend(_flashState, { + disabled: false, + outdated: false, + sandboxed: false, + unavailable: false, + degraded: false, + deactivated: false, + overdue: wasDeactivated, + ready: !wasDeactivated + }); + _clearTimeoutsAndPolling(); + break; + + case "beforecopy": + _copyTarget = element; + break; + + case "copy": + var textContent, htmlContent, targetEl = event.relatedTarget; + if (!(_clipData["text/html"] || _clipData["text/plain"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) { + event.clipboardData.clearData(); + event.clipboardData.setData("text/plain", textContent); + if (htmlContent !== textContent) { + event.clipboardData.setData("text/html", htmlContent); + } + } else if (!_clipData["text/plain"] && event.target && (textContent = event.target.getAttribute("data-clipboard-text"))) { + event.clipboardData.clearData(); + event.clipboardData.setData("text/plain", textContent); + } + break; + + case "aftercopy": + _queueEmitClipboardErrors(event); + ZeroClipboard.clearData(); + if (element && element !== _safeActiveElement() && element.focus) { + element.focus(); + } + break; + + case "_mouseover": + ZeroClipboard.focus(element); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) { + _fireMouseEvent(_extend({}, event, { + type: "mouseenter", + bubbles: false, + cancelable: false + })); + } + _fireMouseEvent(_extend({}, event, { + type: "mouseover" + })); + } + break; + + case "_mouseout": + ZeroClipboard.blur(); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) { + _fireMouseEvent(_extend({}, event, { + type: "mouseleave", + bubbles: false, + cancelable: false + })); + } + _fireMouseEvent(_extend({}, event, { + type: "mouseout" + })); + } + break; + + case "_mousedown": + _addClass(element, _globalConfig.activeClass); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + + case "_mouseup": + _removeClass(element, _globalConfig.activeClass); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + + case "_click": + _copyTarget = null; + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + + case "_mousemove": + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + } + if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) { + return true; + } + }; + /** + * Check an "aftercopy" event for clipboard errors and emit a corresponding "error" event. + * @private + */ + var _queueEmitClipboardErrors = function(aftercopyEvent) { + if (aftercopyEvent.errors && aftercopyEvent.errors.length > 0) { + var errorEvent = _deepCopy(aftercopyEvent); + _extend(errorEvent, { + type: "error", + name: "clipboard-error" + }); + delete errorEvent.success; + _setTimeout(function() { + ZeroClipboard.emit(errorEvent); + }, 0); + } + }; + /** + * Dispatch a synthetic MouseEvent. + * + * @returns `undefined` + * @private + */ + var _fireMouseEvent = function(event) { + if (!(event && typeof event.type === "string" && event)) { + return; + } + var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = { + view: doc.defaultView || _window, + canBubble: true, + cancelable: true, + detail: event.type === "click" ? 1 : 0, + button: typeof event.which === "number" ? event.which - 1 : typeof event.button === "number" ? event.button : doc.createEvent ? 0 : 1 + }, args = _extend(defaults, event); + if (!target) { + return; + } + if (doc.createEvent && target.dispatchEvent) { + args = [ args.type, args.canBubble, args.cancelable, args.view, args.detail, args.screenX, args.screenY, args.clientX, args.clientY, args.ctrlKey, args.altKey, args.shiftKey, args.metaKey, args.button, args.relatedTarget ]; + e = doc.createEvent("MouseEvents"); + if (e.initMouseEvent) { + e.initMouseEvent.apply(e, args); + e._source = "js"; + target.dispatchEvent(e); + } + } + }; + /** + * Continuously poll the DOM until either: + * (a) the fallback content becomes visible, or + * (b) we receive an event from SWF (handled elsewhere) + * + * IMPORTANT: + * This is NOT a necessary check but it can result in significantly faster + * detection of bad `swfPath` configuration and/or network/server issues [in + * supported browsers] than waiting for the entire `flashLoadTimeout` duration + * to elapse before detecting that the SWF cannot be loaded. The detection + * duration can be anywhere from 10-30 times faster [in supported browsers] by + * using this approach. + * + * @returns `undefined` + * @private + */ + var _watchForSwfFallbackContent = function() { + var maxWait = _globalConfig.flashLoadTimeout; + if (typeof maxWait === "number" && maxWait >= 0) { + var pollWait = Math.min(1e3, maxWait / 10); + var fallbackContentId = _globalConfig.swfObjectId + "_fallbackContent"; + _swfFallbackCheckInterval = _setInterval(function() { + var el = _document.getElementById(fallbackContentId); + if (_isElementVisible(el)) { + _clearTimeoutsAndPolling(); + _flashState.deactivated = null; + ZeroClipboard.emit({ + type: "error", + name: "swf-not-found" + }); + } + }, pollWait); + } + }; + /** + * Create the HTML bridge element to embed the Flash object into. + * @private + */ + var _createHtmlBridge = function() { + var container = _document.createElement("div"); + container.id = _globalConfig.containerId; + container.className = _globalConfig.containerClass; + container.style.position = "absolute"; + container.style.left = "0px"; + container.style.top = "-9999px"; + container.style.width = "1px"; + container.style.height = "1px"; + container.style.zIndex = "" + _getSafeZIndex(_globalConfig.zIndex); + return container; + }; + /** + * Get the HTML element container that wraps the Flash bridge object/element. + * @private + */ + var _getHtmlBridge = function(flashBridge) { + var htmlBridge = flashBridge && flashBridge.parentNode; + while (htmlBridge && htmlBridge.nodeName === "OBJECT" && htmlBridge.parentNode) { + htmlBridge = htmlBridge.parentNode; + } + return htmlBridge || null; + }; + /** + * Create the SWF object. + * + * @returns The SWF object reference. + * @private + */ + var _embedSwf = function() { + var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge); + if (!flashBridge) { + var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig); + var allowNetworking = allowScriptAccess === "never" ? "none" : "all"; + var flashvars = _vars(_extend({ + jsVersion: ZeroClipboard.version + }, _globalConfig)); + var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig); + container = _createHtmlBridge(); + var divToBeReplaced = _document.createElement("div"); + container.appendChild(divToBeReplaced); + _document.body.appendChild(container); + var tmpDiv = _document.createElement("div"); + var usingActiveX = _flashState.pluginType === "activex"; + tmpDiv.innerHTML = '" + (usingActiveX ? '' : "") + '' + '' + '' + '' + '' + '
     
    ' + "
    "; + flashBridge = tmpDiv.firstChild; + tmpDiv = null; + _unwrap(flashBridge).ZeroClipboard = ZeroClipboard; + container.replaceChild(flashBridge, divToBeReplaced); + _watchForSwfFallbackContent(); + } + if (!flashBridge) { + flashBridge = _document[_globalConfig.swfObjectId]; + if (flashBridge && (len = flashBridge.length)) { + flashBridge = flashBridge[len - 1]; + } + if (!flashBridge && container) { + flashBridge = container.firstChild; + } + } + _flashState.bridge = flashBridge || null; + return flashBridge; + }; + /** + * Destroy the SWF object. + * @private + */ + var _unembedSwf = function() { + var flashBridge = _flashState.bridge; + if (flashBridge) { + var htmlBridge = _getHtmlBridge(flashBridge); + if (htmlBridge) { + if (_flashState.pluginType === "activex" && "readyState" in flashBridge) { + flashBridge.style.display = "none"; + (function removeSwfFromIE() { + if (flashBridge.readyState === 4) { + for (var prop in flashBridge) { + if (typeof flashBridge[prop] === "function") { + flashBridge[prop] = null; + } + } + if (flashBridge.parentNode) { + flashBridge.parentNode.removeChild(flashBridge); + } + if (htmlBridge.parentNode) { + htmlBridge.parentNode.removeChild(htmlBridge); + } + } else { + _setTimeout(removeSwfFromIE, 10); + } + })(); + } else { + if (flashBridge.parentNode) { + flashBridge.parentNode.removeChild(flashBridge); + } + if (htmlBridge.parentNode) { + htmlBridge.parentNode.removeChild(htmlBridge); + } + } + } + _clearTimeoutsAndPolling(); + _flashState.ready = null; + _flashState.bridge = null; + _flashState.deactivated = null; + _zcSwfVersion = undefined; + } + }; + /** + * Map the data format names of the "clipData" to Flash-friendly names. + * + * @returns A new transformed object. + * @private + */ + var _mapClipDataToFlash = function(clipData) { + var newClipData = {}, formatMap = {}; + if (!(typeof clipData === "object" && clipData)) { + return; + } + for (var dataFormat in clipData) { + if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === "string" && clipData[dataFormat]) { + switch (dataFormat.toLowerCase()) { + case "text/plain": + case "text": + case "air:text": + case "flash:text": + newClipData.text = clipData[dataFormat]; + formatMap.text = dataFormat; + break; + + case "text/html": + case "html": + case "air:html": + case "flash:html": + newClipData.html = clipData[dataFormat]; + formatMap.html = dataFormat; + break; + + case "application/rtf": + case "text/rtf": + case "rtf": + case "richtext": + case "air:rtf": + case "flash:rtf": + newClipData.rtf = clipData[dataFormat]; + formatMap.rtf = dataFormat; + break; + + default: + break; + } + } + } + return { + data: newClipData, + formatMap: formatMap + }; + }; + /** + * Map the data format names from Flash-friendly names back to their original "clipData" names (via a format mapping). + * + * @returns A new transformed object. + * @private + */ + var _mapClipResultsFromFlash = function(clipResults, formatMap) { + if (!(typeof clipResults === "object" && clipResults && typeof formatMap === "object" && formatMap)) { + return clipResults; + } + var newResults = {}; + for (var prop in clipResults) { + if (_hasOwn.call(clipResults, prop)) { + if (prop === "errors") { + newResults[prop] = clipResults[prop] ? clipResults[prop].slice() : []; + for (var i = 0, len = newResults[prop].length; i < len; i++) { + newResults[prop][i].format = formatMap[newResults[prop][i].format]; + } + } else if (prop !== "success" && prop !== "data") { + newResults[prop] = clipResults[prop]; + } else { + newResults[prop] = {}; + var tmpHash = clipResults[prop]; + for (var dataFormat in tmpHash) { + if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) { + newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat]; + } + } + } + } + } + return newResults; + }; + /** + * Will look at a path, and will create a "?noCache={time}" or "&noCache={time}" + * query param string to return. Does NOT append that string to the original path. + * This is useful because ExternalInterface often breaks when a Flash SWF is cached. + * + * @returns The `noCache` query param with necessary "?"/"&" prefix. + * @private + */ + var _cacheBust = function(path, options) { + var cacheBust = options == null || options && options.cacheBust === true; + if (cacheBust) { + return (path.indexOf("?") === -1 ? "?" : "&") + "noCache=" + _now(); + } else { + return ""; + } + }; + /** + * Creates a query string for the FlashVars param. + * Does NOT include the cache-busting query param. + * + * @returns FlashVars query string + * @private + */ + var _vars = function(options) { + var i, len, domain, domains, str = "", trustedOriginsExpanded = []; + if (options.trustedDomains) { + if (typeof options.trustedDomains === "string") { + domains = [ options.trustedDomains ]; + } else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) { + domains = options.trustedDomains; + } + } + if (domains && domains.length) { + for (i = 0, len = domains.length; i < len; i++) { + if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === "string") { + domain = _extractDomain(domains[i]); + if (!domain) { + continue; + } + if (domain === "*") { + trustedOriginsExpanded.length = 0; + trustedOriginsExpanded.push(domain); + break; + } + trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, "//" + domain, _window.location.protocol + "//" + domain ]); + } + } + } + if (trustedOriginsExpanded.length) { + str += "trustedOrigins=" + _encodeURIComponent(trustedOriginsExpanded.join(",")); + } + if (options.forceEnhancedClipboard === true) { + str += (str ? "&" : "") + "forceEnhancedClipboard=true"; + } + if (typeof options.swfObjectId === "string" && options.swfObjectId) { + str += (str ? "&" : "") + "swfObjectId=" + _encodeURIComponent(options.swfObjectId); + } + if (typeof options.jsVersion === "string" && options.jsVersion) { + str += (str ? "&" : "") + "jsVersion=" + _encodeURIComponent(options.jsVersion); + } + return str; + }; + /** + * Extract the domain (e.g. "github.com") from an origin (e.g. "https://github.com") or + * URL (e.g. "https://github.com/zeroclipboard/zeroclipboard/"). + * + * @returns the domain + * @private + */ + var _extractDomain = function(originOrUrl) { + if (originOrUrl == null || originOrUrl === "") { + return null; + } + originOrUrl = originOrUrl.replace(/^\s+|\s+$/g, ""); + if (originOrUrl === "") { + return null; + } + var protocolIndex = originOrUrl.indexOf("//"); + originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2); + var pathIndex = originOrUrl.indexOf("/"); + originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex); + if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === ".swf") { + return null; + } + return originOrUrl || null; + }; + /** + * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`. + * + * @returns The appropriate script access level. + * @private + */ + var _determineScriptAccess = function() { + var _extractAllDomains = function(origins) { + var i, len, tmp, resultsArray = []; + if (typeof origins === "string") { + origins = [ origins ]; + } + if (!(typeof origins === "object" && origins && typeof origins.length === "number")) { + return resultsArray; + } + for (i = 0, len = origins.length; i < len; i++) { + if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) { + if (tmp === "*") { + resultsArray.length = 0; + resultsArray.push("*"); + break; + } + if (resultsArray.indexOf(tmp) === -1) { + resultsArray.push(tmp); + } + } + } + return resultsArray; + }; + return function(currentDomain, configOptions) { + var swfDomain = _extractDomain(configOptions.swfPath); + if (swfDomain === null) { + swfDomain = currentDomain; + } + var trustedDomains = _extractAllDomains(configOptions.trustedDomains); + var len = trustedDomains.length; + if (len > 0) { + if (len === 1 && trustedDomains[0] === "*") { + return "always"; + } + if (trustedDomains.indexOf(currentDomain) !== -1) { + if (len === 1 && currentDomain === swfDomain) { + return "sameDomain"; + } + return "always"; + } + } + return "never"; + }; + }(); + /** + * Get the currently active/focused DOM element. + * + * @returns the currently active/focused element, or `null` + * @private + */ + var _safeActiveElement = function() { + try { + return _document.activeElement; + } catch (err) { + return null; + } + }; + /** + * Add a class to an element, if it doesn't already have it. + * + * @returns The element, with its new class added. + * @private + */ + var _addClass = function(element, value) { + var c, cl, className, classNames = []; + if (typeof value === "string" && value) { + classNames = value.split(/\s+/); + } + if (element && element.nodeType === 1 && classNames.length > 0) { + className = (" " + (element.className || "") + " ").replace(/[\t\r\n\f]/g, " "); + for (c = 0, cl = classNames.length; c < cl; c++) { + if (className.indexOf(" " + classNames[c] + " ") === -1) { + className += classNames[c] + " "; + } + } + className = className.replace(/^\s+|\s+$/g, ""); + if (className !== element.className) { + element.className = className; + } + } + return element; + }; + /** + * Remove a class from an element, if it has it. + * + * @returns The element, with its class removed. + * @private + */ + var _removeClass = function(element, value) { + var c, cl, className, classNames = []; + if (typeof value === "string" && value) { + classNames = value.split(/\s+/); + } + if (element && element.nodeType === 1 && classNames.length > 0) { + if (element.className) { + className = (" " + element.className + " ").replace(/[\t\r\n\f]/g, " "); + for (c = 0, cl = classNames.length; c < cl; c++) { + className = className.replace(" " + classNames[c] + " ", " "); + } + className = className.replace(/^\s+|\s+$/g, ""); + if (className !== element.className) { + element.className = className; + } + } + } + return element; + }; + /** + * Attempt to interpret the element's CSS styling. If `prop` is `"cursor"`, + * then we assume that it should be a hand ("pointer") cursor if the element + * is an anchor element ("a" tag). + * + * @returns The computed style property. + * @private + */ + var _getStyle = function(el, prop) { + var value = _getComputedStyle(el, null).getPropertyValue(prop); + if (prop === "cursor") { + if (!value || value === "auto") { + if (el.nodeName === "A") { + return "pointer"; + } + } + } + return value; + }; + /** + * Get the absolutely positioned coordinates of a DOM element. + * + * @returns Object containing the element's position, width, and height. + * @private + */ + var _getElementPosition = function(el) { + var pos = { + left: 0, + top: 0, + width: 0, + height: 0 + }; + if (el.getBoundingClientRect) { + var elRect = el.getBoundingClientRect(); + var pageXOffset = _window.pageXOffset; + var pageYOffset = _window.pageYOffset; + var leftBorderWidth = _document.documentElement.clientLeft || 0; + var topBorderWidth = _document.documentElement.clientTop || 0; + var leftBodyOffset = 0; + var topBodyOffset = 0; + if (_getStyle(_document.body, "position") === "relative") { + var bodyRect = _document.body.getBoundingClientRect(); + var htmlRect = _document.documentElement.getBoundingClientRect(); + leftBodyOffset = bodyRect.left - htmlRect.left || 0; + topBodyOffset = bodyRect.top - htmlRect.top || 0; + } + pos.left = elRect.left + pageXOffset - leftBorderWidth - leftBodyOffset; + pos.top = elRect.top + pageYOffset - topBorderWidth - topBodyOffset; + pos.width = "width" in elRect ? elRect.width : elRect.right - elRect.left; + pos.height = "height" in elRect ? elRect.height : elRect.bottom - elRect.top; + } + return pos; + }; + /** + * Determine is an element is visible somewhere within the document (page). + * + * @returns Boolean + * @private + */ + var _isElementVisible = function(el) { + if (!el) { + return false; + } + var styles = _getComputedStyle(el, null); + if (!styles) { + return false; + } + var hasCssHeight = _parseFloat(styles.height) > 0; + var hasCssWidth = _parseFloat(styles.width) > 0; + var hasCssTop = _parseFloat(styles.top) >= 0; + var hasCssLeft = _parseFloat(styles.left) >= 0; + var cssKnows = hasCssHeight && hasCssWidth && hasCssTop && hasCssLeft; + var rect = cssKnows ? null : _getElementPosition(el); + var isVisible = styles.display !== "none" && styles.visibility !== "collapse" && (cssKnows || !!rect && (hasCssHeight || rect.height > 0) && (hasCssWidth || rect.width > 0) && (hasCssTop || rect.top >= 0) && (hasCssLeft || rect.left >= 0)); + return isVisible; + }; + /** + * Clear all existing timeouts and interval polling delegates. + * + * @returns `undefined` + * @private + */ + var _clearTimeoutsAndPolling = function() { + _clearTimeout(_flashCheckTimeout); + _flashCheckTimeout = 0; + _clearInterval(_swfFallbackCheckInterval); + _swfFallbackCheckInterval = 0; + }; + /** + * Reposition the Flash object to cover the currently activated element. + * + * @returns `undefined` + * @private + */ + var _reposition = function() { + var htmlBridge; + if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) { + var pos = _getElementPosition(_currentElement); + _extend(htmlBridge.style, { + width: pos.width + "px", + height: pos.height + "px", + top: pos.top + "px", + left: pos.left + "px", + zIndex: "" + _getSafeZIndex(_globalConfig.zIndex) + }); + } + }; + /** + * Sends a signal to the Flash object to display the hand cursor if `true`. + * + * @returns `undefined` + * @private + */ + var _setHandCursor = function(enabled) { + if (_flashState.ready === true) { + if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === "function") { + _flashState.bridge.setHandCursor(enabled); + } else { + _flashState.ready = false; + } + } + }; + /** + * Get a safe value for `zIndex` + * + * @returns an integer, or "auto" + * @private + */ + var _getSafeZIndex = function(val) { + if (/^(?:auto|inherit)$/.test(val)) { + return val; + } + var zIndex; + if (typeof val === "number" && !_isNaN(val)) { + zIndex = val; + } else if (typeof val === "string") { + zIndex = _getSafeZIndex(_parseInt(val, 10)); + } + return typeof zIndex === "number" ? zIndex : "auto"; + }; + /** + * Ensure OS-compliant line endings, i.e. "\r\n" on Windows, "\n" elsewhere + * + * @returns string + * @private + */ + var _fixLineEndings = function(content) { + var replaceRegex = /(\r\n|\r|\n)/g; + if (typeof content === "string" && _globalConfig.fixLineEndings === true) { + if (_isWindows()) { + if (/((^|[^\r])\n|\r([^\n]|$))/.test(content)) { + content = content.replace(replaceRegex, "\r\n"); + } + } else if (/\r/.test(content)) { + content = content.replace(replaceRegex, "\n"); + } + } + return content; + }; + /** + * Attempt to detect if ZeroClipboard is executing inside of a sandboxed iframe. + * If it is, Flash Player cannot be used, so ZeroClipboard is dead in the water. + * + * @see {@link http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Dec/0002.html} + * @see {@link https://github.com/zeroclipboard/zeroclipboard/issues/511} + * @see {@link http://zeroclipboard.org/test-iframes.html} + * + * @returns `true` (is sandboxed), `false` (is not sandboxed), or `null` (uncertain) + * @private + */ + var _detectSandbox = function(doNotReassessFlashSupport) { + var effectiveScriptOrigin, frame, frameError, previousState = _flashState.sandboxed, isSandboxed = null; + doNotReassessFlashSupport = doNotReassessFlashSupport === true; + if (_pageIsFramed === false) { + isSandboxed = false; + } else { + try { + frame = window.frameElement || null; + } catch (e) { + frameError = { + name: e.name, + message: e.message + }; + } + if (frame && frame.nodeType === 1 && frame.nodeName === "IFRAME") { + try { + isSandboxed = frame.hasAttribute("sandbox"); + } catch (e) { + isSandboxed = null; + } + } else { + try { + effectiveScriptOrigin = document.domain || null; + } catch (e) { + effectiveScriptOrigin = null; + } + if (effectiveScriptOrigin === null || frameError && frameError.name === "SecurityError" && /(^|[\s\(\[@])sandbox(es|ed|ing|[\s\.,!\)\]@]|$)/.test(frameError.message.toLowerCase())) { + isSandboxed = true; + } + } + } + _flashState.sandboxed = isSandboxed; + if (previousState !== isSandboxed && !doNotReassessFlashSupport) { + _detectFlashSupport(_ActiveXObject); + } + return isSandboxed; + }; + /** + * Detect the Flash Player status, version, and plugin type. + * + * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code} + * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript} + * + * @returns `undefined` + * @private + */ + var _detectFlashSupport = function(ActiveXObject) { + var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = ""; + /** + * Derived from Apple's suggested sniffer. + * @param {String} desc e.g. "Shockwave Flash 7.0 r61" + * @returns {String} "7.0.61" + * @private + */ + function parseFlashVersion(desc) { + var matches = desc.match(/[\d]+/g); + matches.length = 3; + return matches.join("."); + } + function isPepperFlash(flashPlayerFileName) { + return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === "chrome.plugin"); + } + function inspectPlugin(plugin) { + if (plugin) { + hasFlash = true; + if (plugin.version) { + flashVersion = parseFlashVersion(plugin.version); + } + if (!flashVersion && plugin.description) { + flashVersion = parseFlashVersion(plugin.description); + } + if (plugin.filename) { + isPPAPI = isPepperFlash(plugin.filename); + } + } + } + if (_navigator.plugins && _navigator.plugins.length) { + plugin = _navigator.plugins["Shockwave Flash"]; + inspectPlugin(plugin); + if (_navigator.plugins["Shockwave Flash 2.0"]) { + hasFlash = true; + flashVersion = "2.0.0.11"; + } + } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) { + mimeType = _navigator.mimeTypes["application/x-shockwave-flash"]; + plugin = mimeType && mimeType.enabledPlugin; + inspectPlugin(plugin); + } else if (typeof ActiveXObject !== "undefined") { + isActiveX = true; + try { + ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); + hasFlash = true; + flashVersion = parseFlashVersion(ax.GetVariable("$version")); + } catch (e1) { + try { + ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); + hasFlash = true; + flashVersion = "6.0.21"; + } catch (e2) { + try { + ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); + hasFlash = true; + flashVersion = parseFlashVersion(ax.GetVariable("$version")); + } catch (e3) { + isActiveX = false; + } + } + } + } + _flashState.disabled = hasFlash !== true; + _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion); + _flashState.version = flashVersion || "0.0.0"; + _flashState.pluginType = isPPAPI ? "pepper" : isActiveX ? "activex" : hasFlash ? "netscape" : "unknown"; + }; + /** + * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later. + */ + _detectFlashSupport(_ActiveXObject); + /** + * Always assess the `sandboxed` state of the page at important Flash-related moments. + */ + _detectSandbox(true); + /** + * A shell constructor for `ZeroClipboard` client instances. + * + * @constructor + */ + var ZeroClipboard = function() { + if (!(this instanceof ZeroClipboard)) { + return new ZeroClipboard(); + } + if (typeof ZeroClipboard._createClient === "function") { + ZeroClipboard._createClient.apply(this, _args(arguments)); + } + }; + /** + * The ZeroClipboard library's version number. + * + * @static + * @readonly + * @property {string} + */ + _defineProperty(ZeroClipboard, "version", { + value: "2.3.0-beta.1", + writable: false, + configurable: true, + enumerable: true + }); + /** + * Update or get a copy of the ZeroClipboard global configuration. + * Returns a copy of the current/updated configuration. + * + * @returns Object + * @static + */ + ZeroClipboard.config = function() { + return _config.apply(this, _args(arguments)); + }; + /** + * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard. + * + * @returns Object + * @static + */ + ZeroClipboard.state = function() { + return _state.apply(this, _args(arguments)); + }; + /** + * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc. + * + * @returns Boolean + * @static + */ + ZeroClipboard.isFlashUnusable = function() { + return _isFlashUnusable.apply(this, _args(arguments)); + }; + /** + * Register an event listener. + * + * @returns `ZeroClipboard` + * @static + */ + ZeroClipboard.on = function() { + return _on.apply(this, _args(arguments)); + }; + /** + * Unregister an event listener. + * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`. + * If no `eventType` is provided, it will unregister all listeners for every event type. + * + * @returns `ZeroClipboard` + * @static + */ + ZeroClipboard.off = function() { + return _off.apply(this, _args(arguments)); + }; + /** + * Retrieve event listeners for an `eventType`. + * If no `eventType` is provided, it will retrieve all listeners for every event type. + * + * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null` + */ + ZeroClipboard.handlers = function() { + return _listeners.apply(this, _args(arguments)); + }; + /** + * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners. + * + * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`. + * @static + */ + ZeroClipboard.emit = function() { + return _emit.apply(this, _args(arguments)); + }; + /** + * Create and embed the Flash object. + * + * @returns The Flash object + * @static + */ + ZeroClipboard.create = function() { + return _create.apply(this, _args(arguments)); + }; + /** + * Self-destruct and clean up everything, including the embedded Flash object. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.destroy = function() { + return _destroy.apply(this, _args(arguments)); + }; + /** + * Set the pending data for clipboard injection. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.setData = function() { + return _setData.apply(this, _args(arguments)); + }; + /** + * Clear the pending data for clipboard injection. + * If no `format` is provided, all pending data formats will be cleared. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.clearData = function() { + return _clearData.apply(this, _args(arguments)); + }; + /** + * Get a copy of the pending data for clipboard injection. + * If no `format` is provided, a copy of ALL pending data formats will be returned. + * + * @returns `String` or `Object` + * @static + */ + ZeroClipboard.getData = function() { + return _getData.apply(this, _args(arguments)); + }; + /** + * Sets the current HTML object that the Flash object should overlay. This will put the global + * Flash object on top of the current element; depending on the setup, this may also set the + * pending clipboard text data as well as the Flash object's wrapping element's title attribute + * based on the underlying HTML element and ZeroClipboard configuration. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.focus = ZeroClipboard.activate = function() { + return _focus.apply(this, _args(arguments)); + }; + /** + * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on + * the setup, this may also unset the Flash object's wrapping element's title attribute based on + * the underlying HTML element and ZeroClipboard configuration. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.blur = ZeroClipboard.deactivate = function() { + return _blur.apply(this, _args(arguments)); + }; + /** + * Returns the currently focused/"activated" HTML element that the Flash object is wrapping. + * + * @returns `HTMLElement` or `null` + * @static + */ + ZeroClipboard.activeElement = function() { + return _activeElement.apply(this, _args(arguments)); + }; + if (typeof define === "function" && define.amd) { + define(function() { + return ZeroClipboard; + }); + } else if (typeof module === "object" && module && typeof module.exports === "object" && module.exports) { + module.exports = ZeroClipboard; + } else { + window.ZeroClipboard = ZeroClipboard; + } +})(function() { + return this || window; +}()); \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.min.js b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.min.js new file mode 100644 index 000000000..013071679 --- /dev/null +++ b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.Core.min.js @@ -0,0 +1,10 @@ +/*! + * ZeroClipboard + * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. + * Copyright (c) 2009-2015 Jon Rohan, James M. Greene + * Licensed MIT + * http://zeroclipboard.org/ + * v2.3.0-beta.1 + */ +!function(a,b){"use strict";var c,d,e,f=a,g=f.document,h=f.navigator,i=f.setTimeout,j=f.clearTimeout,k=f.setInterval,l=f.clearInterval,m=f.getComputedStyle,n=f.encodeURIComponent,o=f.ActiveXObject,p=f.Error,q=f.Number.parseInt||f.parseInt,r=f.Number.parseFloat||f.parseFloat,s=f.Number.isNaN||f.isNaN,t=f.Date.now,u=f.Object.keys,v=f.Object.defineProperty,w=f.Object.prototype.hasOwnProperty,x=f.Array.prototype.slice,y=function(){var a=function(a){return a};if("function"==typeof f.wrap&&"function"==typeof f.unwrap)try{var b=g.createElement("div"),c=f.unwrap(b);1===b.nodeType&&c&&1===c.nodeType&&(a=f.unwrap)}catch(d){}return a}(),z=function(a){return x.call(a,0)},A=function(){var a,c,d,e,f,g,h=z(arguments),i=h[0]||{};for(a=1,c=h.length;c>a;a++)if(null!=(d=h[a]))for(e in d)w.call(d,e)&&(f=i[e],g=d[e],i!==g&&g!==b&&(i[e]=g));return i},B=function(a){var b,c,d,e;if("object"!=typeof a||null==a||"number"==typeof a.nodeType)b=a;else if("number"==typeof a.length)for(b=[],c=0,d=a.length;d>c;c++)w.call(a,c)&&(b[c]=B(a[c]));else{b={};for(e in a)w.call(a,e)&&(b[e]=B(a[e]))}return b},C=function(a,b){for(var c={},d=0,e=b.length;e>d;d++)b[d]in a&&(c[b[d]]=a[b[d]]);return c},D=function(a,b){var c={};for(var d in a)-1===b.indexOf(d)&&(c[d]=a[d]);return c},E=function(a){if(a)for(var b in a)w.call(a,b)&&delete a[b];return a},F=function(a,b){if(a&&1===a.nodeType&&a.ownerDocument&&b&&(1===b.nodeType&&b.ownerDocument&&b.ownerDocument===a.ownerDocument||9===b.nodeType&&!b.ownerDocument&&b===a.ownerDocument))do{if(a===b)return!0;a=a.parentNode}while(a);return!1},G=function(a){var b;return"string"==typeof a&&a&&(b=a.split("#")[0].split("?")[0],b=a.slice(0,a.lastIndexOf("/")+1)),b},H=function(a){var b,c;return"string"==typeof a&&a&&(c=a.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]?b=c[1]:(c=a.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]&&(b=c[1]))),b},I=function(){var a,b;try{throw new p}catch(c){b=c}return b&&(a=b.sourceURL||b.fileName||H(b.stack)),a},J=function(){var a,c,d;if(g.currentScript&&(a=g.currentScript.src))return a;if(c=g.getElementsByTagName("script"),1===c.length)return c[0].src||b;if("readyState"in c[0])for(d=c.length;d--;)if("interactive"===c[d].readyState&&(a=c[d].src))return a;return"loading"===g.readyState&&(a=c[c.length-1].src)?a:(a=I())?a:b},K=function(){var a,c,d,e=g.getElementsByTagName("script");for(a=e.length;a--;){if(!(d=e[a].src)){c=null;break}if(d=G(d),null==c)c=d;else if(c!==d){c=null;break}}return c||b},L=function(){var a=G(J())||K()||"";return a+"ZeroClipboard.swf"},M=function(){var a=/win(dows|[\s]?(nt|me|ce|xp|vista|[\d]+))/i;return!!h&&(a.test(h.appVersion||"")||a.test(h.platform||"")||-1!==(h.userAgent||"").indexOf("Windows"))},N=function(){return null==a.opener&&(!!a.top&&a!=a.top||!!a.parent&&a!=a.parent)}(),O={bridge:null,version:"0.0.0",pluginType:"unknown",disabled:null,outdated:null,sandboxed:null,unavailable:null,degraded:null,deactivated:null,overdue:null,ready:null},P="11.0.0",Q={},R={},S=null,T=0,U=0,V={ready:"Flash communication is established",error:{"flash-disabled":"Flash is disabled or not installed. May also be attempting to run Flash in a sandboxed iframe, which is impossible.","flash-outdated":"Flash is too outdated to support ZeroClipboard","flash-sandboxed":"Attempting to run Flash in a sandboxed iframe, which is impossible","flash-unavailable":"Flash is unable to communicate bidirectionally with JavaScript","flash-degraded":"Flash is unable to preserve data fidelity when communicating with JavaScript","flash-deactivated":"Flash is too outdated for your browser and/or is configured as click-to-activate.\nThis may also mean that the ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity.\nMay also be attempting to run Flash in a sandboxed iframe, which is impossible.","flash-overdue":"Flash communication was established but NOT within the acceptable time limit","version-mismatch":"ZeroClipboard JS version number does not match ZeroClipboard SWF version number","clipboard-error":"At least one error was thrown while ZeroClipboard was attempting to inject your data into the clipboard","config-mismatch":"ZeroClipboard configuration does not match Flash's reality","swf-not-found":"The ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity"}},W=["flash-unavailable","flash-degraded","flash-overdue","version-mismatch","config-mismatch","clipboard-error"],X=["flash-disabled","flash-outdated","flash-sandboxed","flash-unavailable","flash-degraded","flash-deactivated","flash-overdue"],Y=new RegExp("^flash-("+X.map(function(a){return a.replace(/^flash-/,"")}).join("|")+")$"),Z=new RegExp("^flash-("+X.slice(1).map(function(a){return a.replace(/^flash-/,"")}).join("|")+")$"),$={swfPath:L(),trustedDomains:a.location.host?[a.location.host]:[],cacheBust:!0,forceEnhancedClipboard:!1,flashLoadTimeout:3e4,autoActivate:!0,bubbleEvents:!0,fixLineEndings:!0,containerId:"global-zeroclipboard-html-bridge",containerClass:"global-zeroclipboard-container",swfObjectId:"global-zeroclipboard-flash-bridge",hoverClass:"zeroclipboard-is-hover",activeClass:"zeroclipboard-is-active",forceHandCursor:!1,title:null,zIndex:999999999},_=function(a){if("object"==typeof a&&null!==a)for(var b in a)if(w.call(a,b))if(/^(?:forceHandCursor|title|zIndex|bubbleEvents|fixLineEndings)$/.test(b))$[b]=a[b];else if(null==O.bridge)if("containerId"===b||"swfObjectId"===b){if(!oa(a[b]))throw new Error("The specified `"+b+"` value is not valid as an HTML4 Element ID");$[b]=a[b]}else $[b]=a[b];{if("string"!=typeof a||!a)return B($);if(w.call($,a))return $[a]}},aa=function(){return Va(),{browser:C(h,["userAgent","platform","appName","appVersion"]),flash:D(O,["bridge"]),zeroclipboard:{version:Xa.version,config:Xa.config()}}},ba=function(){return!!(O.disabled||O.outdated||O.sandboxed||O.unavailable||O.degraded||O.deactivated)},ca=function(a,d){var e,f,g,h={};if("string"==typeof a&&a)g=a.toLowerCase().split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof d)for(e in a)w.call(a,e)&&"string"==typeof e&&e&&"function"==typeof a[e]&&Xa.on(e,a[e]);if(g&&g.length){for(e=0,f=g.length;f>e;e++)a=g[e].replace(/^on/,""),h[a]=!0,Q[a]||(Q[a]=[]),Q[a].push(d);if(h.ready&&O.ready&&Xa.emit({type:"ready"}),h.error){for(e=0,f=X.length;f>e;e++)if(O[X[e].replace(/^flash-/,"")]===!0){Xa.emit({type:"error",name:X[e]});break}c!==b&&Xa.version!==c&&Xa.emit({type:"error",name:"version-mismatch",jsVersion:Xa.version,swfVersion:c})}}return Xa},da=function(a,b){var c,d,e,f,g;if(0===arguments.length)f=u(Q);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)w.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&Xa.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=Q[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return Xa},ea=function(a){var b;return b="string"==typeof a&&a?B(Q[a])||null:B(Q)},fa=function(a){var b,c,d;return a=pa(a),a&&!wa(a)?"ready"===a.type&&O.overdue===!0?Xa.emit({type:"error",name:"flash-overdue"}):(b=A({},a),ua.call(this,b),"copy"===a.type&&(d=Ea(R),c=d.data,S=d.formatMap),c):void 0},ga=function(){var a=O.sandboxed;if(Va(),"boolean"!=typeof O.ready&&(O.ready=!1),O.sandboxed!==a&&O.sandboxed===!0)O.ready=!1,Xa.emit({type:"error",name:"flash-sandboxed"});else if(!Xa.isFlashUnusable()&&null===O.bridge){var b=$.flashLoadTimeout;"number"==typeof b&&b>=0&&(T=i(function(){"boolean"!=typeof O.deactivated&&(O.deactivated=!0),O.deactivated===!0&&Xa.emit({type:"error",name:"flash-deactivated"})},b)),O.overdue=!1,Ca()}},ha=function(){Xa.clearData(),Xa.blur(),Xa.emit("destroy"),Da(),Xa.off()},ia=function(a,b){var c;if("object"==typeof a&&a&&"undefined"==typeof b)c=a,Xa.clearData();else{if("string"!=typeof a||!a)return;c={},c[a]=b}for(var d in c)"string"==typeof d&&d&&w.call(c,d)&&"string"==typeof c[d]&&c[d]&&(R[d]=Ua(c[d]))},ja=function(a){"undefined"==typeof a?(E(R),S=null):"string"==typeof a&&w.call(R,a)&&delete R[a]},ka=function(a){return"undefined"==typeof a?B(R):"string"==typeof a&&w.call(R,a)?R[a]:void 0},la=function(a){if(a&&1===a.nodeType){d&&(Ma(d,$.activeClass),d!==a&&Ma(d,$.hoverClass)),d=a,La(a,$.hoverClass);var b=a.getAttribute("title")||$.title;if("string"==typeof b&&b){var c=Ba(O.bridge);c&&c.setAttribute("title",b)}var e=$.forceHandCursor===!0||"pointer"===Na(a,"cursor");Sa(e),Ra()}},ma=function(){var a=Ba(O.bridge);a&&(a.removeAttribute("title"),a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px"),d&&(Ma(d,$.hoverClass),Ma(d,$.activeClass),d=null)},na=function(){return d||null},oa=function(a){return"string"==typeof a&&a&&/^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(a)},pa=function(a){var b;if("string"==typeof a&&a?(b=a,a={}):"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(b=a.type),b){b=b.toLowerCase(),!a.target&&(/^(copy|aftercopy|_click)$/.test(b)||"error"===b&&"clipboard-error"===a.name)&&(a.target=e),A(a,{type:b,target:a.target||d||null,relatedTarget:a.relatedTarget||null,currentTarget:O&&O.bridge||null,timeStamp:a.timeStamp||t()||null});var c=V[a.type];return"error"===a.type&&a.name&&c&&(c=c[a.name]),c&&(a.message=c),"ready"===a.type&&A(a,{target:null,version:O.version}),"error"===a.type&&(Y.test(a.name)&&A(a,{target:null,minimumVersion:P}),Z.test(a.name)&&A(a,{version:O.version})),"copy"===a.type&&(a.clipboardData={setData:Xa.setData,clearData:Xa.clearData}),"aftercopy"===a.type&&(a=Fa(a,S)),a.target&&!a.relatedTarget&&(a.relatedTarget=qa(a.target)),ra(a)}},qa=function(a){var b=a&&a.getAttribute&&a.getAttribute("data-clipboard-target");return b?g.getElementById(b):null},ra=function(a){if(a&&/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)){var c=a.target,d="_mouseover"===a.type&&a.relatedTarget?a.relatedTarget:b,e="_mouseout"===a.type&&a.relatedTarget?a.relatedTarget:b,h=Oa(c),i=f.screenLeft||f.screenX||0,j=f.screenTop||f.screenY||0,k=g.body.scrollLeft+g.documentElement.scrollLeft,l=g.body.scrollTop+g.documentElement.scrollTop,m=h.left+("number"==typeof a._stageX?a._stageX:0),n=h.top+("number"==typeof a._stageY?a._stageY:0),o=m-k,p=n-l,q=i+o,r=j+p,s="number"==typeof a.movementX?a.movementX:0,t="number"==typeof a.movementY?a.movementY:0;delete a._stageX,delete a._stageY,A(a,{srcElement:c,fromElement:d,toElement:e,screenX:q,screenY:r,pageX:m,pageY:n,clientX:o,clientY:p,x:o,y:p,movementX:s,movementY:t,offsetX:0,offsetY:0,layerX:0,layerY:0})}return a},sa=function(a){var b=a&&"string"==typeof a.type&&a.type||"";return!/^(?:(?:before)?copy|destroy)$/.test(b)},ta=function(a,b,c,d){d?i(function(){a.apply(b,c)},0):a.apply(b,c)},ua=function(a){if("object"==typeof a&&a&&a.type){var b=sa(a),c=Q["*"]||[],d=Q[a.type]||[],e=c.concat(d);if(e&&e.length){var g,h,i,j,k,l=this;for(g=0,h=e.length;h>g;g++)i=e[g],j=l,"string"==typeof i&&"function"==typeof f[i]&&(i=f[i]),"object"==typeof i&&i&&"function"==typeof i.handleEvent&&(j=i,i=i.handleEvent),"function"==typeof i&&(k=A({},a),ta(i,j,[k],b))}return this}},va=function(a){var b=null;return(N===!1||a&&"error"===a.type&&a.name&&-1!==W.indexOf(a.name))&&(b=!1),b},wa=function(a){var b=a.target||d||null,f="swf"===a._source;switch(delete a._source,a.type){case"error":var g="flash-sandboxed"===a.name||va(a);"boolean"==typeof g&&(O.sandboxed=g),-1!==X.indexOf(a.name)?A(O,{disabled:"flash-disabled"===a.name,outdated:"flash-outdated"===a.name,unavailable:"flash-unavailable"===a.name,degraded:"flash-degraded"===a.name,deactivated:"flash-deactivated"===a.name,overdue:"flash-overdue"===a.name,ready:!1}):"version-mismatch"===a.name&&(c=a.swfVersion,A(O,{disabled:!1,outdated:!1,unavailable:!1,degraded:!1,deactivated:!1,overdue:!1,ready:!1})),Qa();break;case"ready":c=a.swfVersion;var h=O.deactivated===!0;A(O,{disabled:!1,outdated:!1,sandboxed:!1,unavailable:!1,degraded:!1,deactivated:!1,overdue:h,ready:!h}),Qa();break;case"beforecopy":e=b;break;case"copy":var i,j,k=a.relatedTarget;!R["text/html"]&&!R["text/plain"]&&k&&(j=k.value||k.outerHTML||k.innerHTML)&&(i=k.value||k.textContent||k.innerText)?(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",i),j!==i&&a.clipboardData.setData("text/html",j)):!R["text/plain"]&&a.target&&(i=a.target.getAttribute("data-clipboard-text"))&&(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",i));break;case"aftercopy":xa(a),Xa.clearData(),b&&b!==Ka()&&b.focus&&b.focus();break;case"_mouseover":Xa.focus(b),$.bubbleEvents===!0&&f&&(b&&b!==a.relatedTarget&&!F(a.relatedTarget,b)&&ya(A({},a,{type:"mouseenter",bubbles:!1,cancelable:!1})),ya(A({},a,{type:"mouseover"})));break;case"_mouseout":Xa.blur(),$.bubbleEvents===!0&&f&&(b&&b!==a.relatedTarget&&!F(a.relatedTarget,b)&&ya(A({},a,{type:"mouseleave",bubbles:!1,cancelable:!1})),ya(A({},a,{type:"mouseout"})));break;case"_mousedown":La(b,$.activeClass),$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}));break;case"_mouseup":Ma(b,$.activeClass),$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}));break;case"_click":e=null,$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}));break;case"_mousemove":$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}))}return/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)?!0:void 0},xa=function(a){if(a.errors&&a.errors.length>0){var b=B(a);A(b,{type:"error",name:"clipboard-error"}),delete b.success,i(function(){Xa.emit(b)},0)}},ya=function(a){if(a&&"string"==typeof a.type&&a){var b,c=a.target||null,d=c&&c.ownerDocument||g,e={view:d.defaultView||f,canBubble:!0,cancelable:!0,detail:"click"===a.type?1:0,button:"number"==typeof a.which?a.which-1:"number"==typeof a.button?a.button:d.createEvent?0:1},h=A(e,a);c&&d.createEvent&&c.dispatchEvent&&(h=[h.type,h.canBubble,h.cancelable,h.view,h.detail,h.screenX,h.screenY,h.clientX,h.clientY,h.ctrlKey,h.altKey,h.shiftKey,h.metaKey,h.button,h.relatedTarget],b=d.createEvent("MouseEvents"),b.initMouseEvent&&(b.initMouseEvent.apply(b,h),b._source="js",c.dispatchEvent(b)))}},za=function(){var a=$.flashLoadTimeout;if("number"==typeof a&&a>=0){var b=Math.min(1e3,a/10),c=$.swfObjectId+"_fallbackContent";U=k(function(){var a=g.getElementById(c);Pa(a)&&(Qa(),O.deactivated=null,Xa.emit({type:"error",name:"swf-not-found"}))},b)}},Aa=function(){var a=g.createElement("div");return a.id=$.containerId,a.className=$.containerClass,a.style.position="absolute",a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px",a.style.zIndex=""+Ta($.zIndex),a},Ba=function(a){for(var b=a&&a.parentNode;b&&"OBJECT"===b.nodeName&&b.parentNode;)b=b.parentNode;return b||null},Ca=function(){var a,b=O.bridge,c=Ba(b);if(!b){var d=Ja(f.location.host,$),e="never"===d?"none":"all",h=Ha(A({jsVersion:Xa.version},$)),i=$.swfPath+Ga($.swfPath,$);c=Aa();var j=g.createElement("div");c.appendChild(j),g.body.appendChild(c);var k=g.createElement("div"),l="activex"===O.pluginType;k.innerHTML='"+(l?'':"")+'
     
    ',b=k.firstChild,k=null,y(b).ZeroClipboard=Xa,c.replaceChild(b,j),za()}return b||(b=g[$.swfObjectId],b&&(a=b.length)&&(b=b[a-1]),!b&&c&&(b=c.firstChild)),O.bridge=b||null,b},Da=function(){var a=O.bridge;if(a){var d=Ba(a);d&&("activex"===O.pluginType&&"readyState"in a?(a.style.display="none",function e(){if(4===a.readyState){for(var b in a)"function"==typeof a[b]&&(a[b]=null);a.parentNode&&a.parentNode.removeChild(a),d.parentNode&&d.parentNode.removeChild(d)}else i(e,10)}()):(a.parentNode&&a.parentNode.removeChild(a),d.parentNode&&d.parentNode.removeChild(d))),Qa(),O.ready=null,O.bridge=null,O.deactivated=null,c=b}},Ea=function(a){var b={},c={};if("object"==typeof a&&a){for(var d in a)if(d&&w.call(a,d)&&"string"==typeof a[d]&&a[d])switch(d.toLowerCase()){case"text/plain":case"text":case"air:text":case"flash:text":b.text=a[d],c.text=d;break;case"text/html":case"html":case"air:html":case"flash:html":b.html=a[d],c.html=d;break;case"application/rtf":case"text/rtf":case"rtf":case"richtext":case"air:rtf":case"flash:rtf":b.rtf=a[d],c.rtf=d}return{data:b,formatMap:c}}},Fa=function(a,b){if("object"!=typeof a||!a||"object"!=typeof b||!b)return a;var c={};for(var d in a)if(w.call(a,d))if("errors"===d){c[d]=a[d]?a[d].slice():[];for(var e=0,f=c[d].length;f>e;e++)c[d][e].format=b[c[d][e].format]}else if("success"!==d&&"data"!==d)c[d]=a[d];else{c[d]={};var g=a[d];for(var h in g)h&&w.call(g,h)&&w.call(b,h)&&(c[d][b[h]]=g[h])}return c},Ga=function(a,b){var c=null==b||b&&b.cacheBust===!0;return c?(-1===a.indexOf("?")?"?":"&")+"noCache="+t():""},Ha=function(a){var b,c,d,e,g="",h=[];if(a.trustedDomains&&("string"==typeof a.trustedDomains?e=[a.trustedDomains]:"object"==typeof a.trustedDomains&&"length"in a.trustedDomains&&(e=a.trustedDomains)),e&&e.length)for(b=0,c=e.length;c>b;b++)if(w.call(e,b)&&e[b]&&"string"==typeof e[b]){if(d=Ia(e[b]),!d)continue;if("*"===d){h.length=0,h.push(d);break}h.push.apply(h,[d,"//"+d,f.location.protocol+"//"+d])}return h.length&&(g+="trustedOrigins="+n(h.join(","))),a.forceEnhancedClipboard===!0&&(g+=(g?"&":"")+"forceEnhancedClipboard=true"),"string"==typeof a.swfObjectId&&a.swfObjectId&&(g+=(g?"&":"")+"swfObjectId="+n(a.swfObjectId)),"string"==typeof a.jsVersion&&a.jsVersion&&(g+=(g?"&":"")+"jsVersion="+n(a.jsVersion)),g},Ia=function(a){if(null==a||""===a)return null;if(a=a.replace(/^\s+|\s+$/g,""),""===a)return null;var b=a.indexOf("//");a=-1===b?a:a.slice(b+2);var c=a.indexOf("/");return a=-1===c?a:-1===b||0===c?null:a.slice(0,c),a&&".swf"===a.slice(-4).toLowerCase()?null:a||null},Ja=function(){var a=function(a){var b,c,d,e=[];if("string"==typeof a&&(a=[a]),"object"!=typeof a||!a||"number"!=typeof a.length)return e;for(b=0,c=a.length;c>b;b++)if(w.call(a,b)&&(d=Ia(a[b]))){if("*"===d){e.length=0,e.push("*");break}-1===e.indexOf(d)&&e.push(d)}return e};return function(b,c){var d=Ia(c.swfPath);null===d&&(d=b);var e=a(c.trustedDomains),f=e.length;if(f>0){if(1===f&&"*"===e[0])return"always";if(-1!==e.indexOf(b))return 1===f&&b===d?"sameDomain":"always"}return"never"}}(),Ka=function(){try{return g.activeElement}catch(a){return null}},La=function(a,b){var c,d,e,f=[];if("string"==typeof b&&b&&(f=b.split(/\s+/)),a&&1===a.nodeType&&f.length>0){for(e=(" "+(a.className||"")+" ").replace(/[\t\r\n\f]/g," "),c=0,d=f.length;d>c;c++)-1===e.indexOf(" "+f[c]+" ")&&(e+=f[c]+" ");e=e.replace(/^\s+|\s+$/g,""),e!==a.className&&(a.className=e)}return a},Ma=function(a,b){var c,d,e,f=[];if("string"==typeof b&&b&&(f=b.split(/\s+/)),a&&1===a.nodeType&&f.length>0&&a.className){for(e=(" "+a.className+" ").replace(/[\t\r\n\f]/g," "),c=0,d=f.length;d>c;c++)e=e.replace(" "+f[c]+" "," ");e=e.replace(/^\s+|\s+$/g,""),e!==a.className&&(a.className=e)}return a},Na=function(a,b){var c=m(a,null).getPropertyValue(b);return"cursor"!==b||c&&"auto"!==c||"A"!==a.nodeName?c:"pointer"},Oa=function(a){var b={left:0,top:0,width:0,height:0};if(a.getBoundingClientRect){var c=a.getBoundingClientRect(),d=f.pageXOffset,e=f.pageYOffset,h=g.documentElement.clientLeft||0,i=g.documentElement.clientTop||0,j=0,k=0;if("relative"===Na(g.body,"position")){var l=g.body.getBoundingClientRect(),m=g.documentElement.getBoundingClientRect();j=l.left-m.left||0,k=l.top-m.top||0}b.left=c.left+d-h-j,b.top=c.top+e-i-k,b.width="width"in c?c.width:c.right-c.left,b.height="height"in c?c.height:c.bottom-c.top}return b},Pa=function(a){if(!a)return!1;var b=m(a,null);if(!b)return!1;var c=r(b.height)>0,d=r(b.width)>0,e=r(b.top)>=0,f=r(b.left)>=0,g=c&&d&&e&&f,h=g?null:Oa(a),i="none"!==b.display&&"collapse"!==b.visibility&&(g||!!h&&(c||h.height>0)&&(d||h.width>0)&&(e||h.top>=0)&&(f||h.left>=0));return i},Qa=function(){j(T),T=0,l(U),U=0},Ra=function(){var a;if(d&&(a=Ba(O.bridge))){var b=Oa(d);A(a.style,{width:b.width+"px",height:b.height+"px",top:b.top+"px",left:b.left+"px",zIndex:""+Ta($.zIndex)})}},Sa=function(a){O.ready===!0&&(O.bridge&&"function"==typeof O.bridge.setHandCursor?O.bridge.setHandCursor(a):O.ready=!1)},Ta=function(a){if(/^(?:auto|inherit)$/.test(a))return a;var b;return"number"!=typeof a||s(a)?"string"==typeof a&&(b=Ta(q(a,10))):b=a,"number"==typeof b?b:"auto"},Ua=function(a){var b=/(\r\n|\r|\n)/g;return"string"==typeof a&&$.fixLineEndings===!0&&(M()?/((^|[^\r])\n|\r([^\n]|$))/.test(a)&&(a=a.replace(b,"\r\n")):/\r/.test(a)&&(a=a.replace(b,"\n"))),a},Va=function(b){var c,d,e,f=O.sandboxed,g=null;if(b=b===!0,N===!1)g=!1;else{try{d=a.frameElement||null}catch(h){e={name:h.name,message:h.message}}if(d&&1===d.nodeType&&"IFRAME"===d.nodeName)try{g=d.hasAttribute("sandbox")}catch(h){g=null}else{try{c=document.domain||null}catch(h){c=null}(null===c||e&&"SecurityError"===e.name&&/(^|[\s\(\[@])sandbox(es|ed|ing|[\s\.,!\)\]@]|$)/.test(e.message.toLowerCase()))&&(g=!0)}}return O.sandboxed=g,f===g||b||Wa(o),g},Wa=function(a){function b(a){var b=a.match(/[\d]+/g);return b.length=3,b.join(".")}function c(a){return!!a&&(a=a.toLowerCase())&&(/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(a)||"chrome.plugin"===a.slice(-13))}function d(a){a&&(i=!0,a.version&&(l=b(a.version)),!l&&a.description&&(l=b(a.description)),a.filename&&(k=c(a.filename)))}var e,f,g,i=!1,j=!1,k=!1,l="";if(h.plugins&&h.plugins.length)e=h.plugins["Shockwave Flash"],d(e),h.plugins["Shockwave Flash 2.0"]&&(i=!0,l="2.0.0.11");else if(h.mimeTypes&&h.mimeTypes.length)g=h.mimeTypes["application/x-shockwave-flash"],e=g&&g.enabledPlugin,d(e);else if("undefined"!=typeof a){j=!0;try{f=new a("ShockwaveFlash.ShockwaveFlash.7"),i=!0,l=b(f.GetVariable("$version"))}catch(m){try{f=new a("ShockwaveFlash.ShockwaveFlash.6"),i=!0,l="6.0.21"}catch(n){try{f=new a("ShockwaveFlash.ShockwaveFlash"),i=!0,l=b(f.GetVariable("$version"))}catch(o){j=!1}}}}O.disabled=i!==!0,O.outdated=l&&r(l)= 0) {\n _flashCheckTimeout = _setTimeout(function() {\n if (typeof _flashState.deactivated !== \"boolean\") {\n _flashState.deactivated = true;\n }\n if (_flashState.deactivated === true) {\n ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-deactivated\"\n });\n }\n }, maxWait);\n }\n _flashState.overdue = false;\n _embedSwf();\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.destroy`.\n * @private\n */\n var _destroy = function() {\n ZeroClipboard.clearData();\n ZeroClipboard.blur();\n ZeroClipboard.emit(\"destroy\");\n _unembedSwf();\n ZeroClipboard.off();\n };\n /**\n * The underlying implementation of `ZeroClipboard.setData`.\n * @private\n */\n var _setData = function(format, data) {\n var dataObj;\n if (typeof format === \"object\" && format && typeof data === \"undefined\") {\n dataObj = format;\n ZeroClipboard.clearData();\n } else if (typeof format === \"string\" && format) {\n dataObj = {};\n dataObj[format] = data;\n } else {\n return;\n }\n for (var dataFormat in dataObj) {\n if (typeof dataFormat === \"string\" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === \"string\" && dataObj[dataFormat]) {\n _clipData[dataFormat] = _fixLineEndings(dataObj[dataFormat]);\n }\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.clearData`.\n * @private\n */\n var _clearData = function(format) {\n if (typeof format === \"undefined\") {\n _deleteOwnProperties(_clipData);\n _clipDataFormatMap = null;\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n delete _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.getData`.\n * @private\n */\n var _getData = function(format) {\n if (typeof format === \"undefined\") {\n return _deepCopy(_clipData);\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n return _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.\n * @private\n */\n var _focus = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.activeClass);\n if (_currentElement !== element) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n }\n }\n _currentElement = element;\n _addClass(element, _globalConfig.hoverClass);\n var newTitle = element.getAttribute(\"title\") || _globalConfig.title;\n if (typeof newTitle === \"string\" && newTitle) {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.setAttribute(\"title\", newTitle);\n }\n }\n var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, \"cursor\") === \"pointer\";\n _setHandCursor(useHandCursor);\n _reposition();\n };\n /**\n * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.\n * @private\n */\n var _blur = function() {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.removeAttribute(\"title\");\n htmlBridge.style.left = \"0px\";\n htmlBridge.style.top = \"-9999px\";\n htmlBridge.style.width = \"1px\";\n htmlBridge.style.height = \"1px\";\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n _removeClass(_currentElement, _globalConfig.activeClass);\n _currentElement = null;\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.activeElement`.\n * @private\n */\n var _activeElement = function() {\n return _currentElement || null;\n };\n /**\n * Check if a value is a valid HTML4 `ID` or `Name` token.\n * @private\n */\n var _isValidHtml4Id = function(id) {\n return typeof id === \"string\" && id && /^[A-Za-z][A-Za-z0-9_:\\-\\.]*$/.test(id);\n };\n /**\n * Create or update an `event` object, based on the `eventType`.\n * @private\n */\n var _createEvent = function(event) {\n var eventType;\n if (typeof event === \"string\" && event) {\n eventType = event;\n event = {};\n } else if (typeof event === \"object\" && event && typeof event.type === \"string\" && event.type) {\n eventType = event.type;\n }\n if (!eventType) {\n return;\n }\n eventType = eventType.toLowerCase();\n if (!event.target && (/^(copy|aftercopy|_click)$/.test(eventType) || eventType === \"error\" && event.name === \"clipboard-error\")) {\n event.target = _copyTarget;\n }\n _extend(event, {\n type: eventType,\n target: event.target || _currentElement || null,\n relatedTarget: event.relatedTarget || null,\n currentTarget: _flashState && _flashState.bridge || null,\n timeStamp: event.timeStamp || _now() || null\n });\n var msg = _eventMessages[event.type];\n if (event.type === \"error\" && event.name && msg) {\n msg = msg[event.name];\n }\n if (msg) {\n event.message = msg;\n }\n if (event.type === \"ready\") {\n _extend(event, {\n target: null,\n version: _flashState.version\n });\n }\n if (event.type === \"error\") {\n if (_flashStateErrorNameMatchingRegex.test(event.name)) {\n _extend(event, {\n target: null,\n minimumVersion: _minimumFlashVersion\n });\n }\n if (_flashStateEnabledErrorNameMatchingRegex.test(event.name)) {\n _extend(event, {\n version: _flashState.version\n });\n }\n }\n if (event.type === \"copy\") {\n event.clipboardData = {\n setData: ZeroClipboard.setData,\n clearData: ZeroClipboard.clearData\n };\n }\n if (event.type === \"aftercopy\") {\n event = _mapClipResultsFromFlash(event, _clipDataFormatMap);\n }\n if (event.target && !event.relatedTarget) {\n event.relatedTarget = _getRelatedTarget(event.target);\n }\n return _addMouseData(event);\n };\n /**\n * Get a relatedTarget from the target's `data-clipboard-target` attribute\n * @private\n */\n var _getRelatedTarget = function(targetEl) {\n var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute(\"data-clipboard-target\");\n return relatedTargetId ? _document.getElementById(relatedTargetId) : null;\n };\n /**\n * Add element and position data to `MouseEvent` instances\n * @private\n */\n var _addMouseData = function(event) {\n if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n var srcElement = event.target;\n var fromElement = event.type === \"_mouseover\" && event.relatedTarget ? event.relatedTarget : undefined;\n var toElement = event.type === \"_mouseout\" && event.relatedTarget ? event.relatedTarget : undefined;\n var pos = _getElementPosition(srcElement);\n var screenLeft = _window.screenLeft || _window.screenX || 0;\n var screenTop = _window.screenTop || _window.screenY || 0;\n var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;\n var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;\n var pageX = pos.left + (typeof event._stageX === \"number\" ? event._stageX : 0);\n var pageY = pos.top + (typeof event._stageY === \"number\" ? event._stageY : 0);\n var clientX = pageX - scrollLeft;\n var clientY = pageY - scrollTop;\n var screenX = screenLeft + clientX;\n var screenY = screenTop + clientY;\n var moveX = typeof event.movementX === \"number\" ? event.movementX : 0;\n var moveY = typeof event.movementY === \"number\" ? event.movementY : 0;\n delete event._stageX;\n delete event._stageY;\n _extend(event, {\n srcElement: srcElement,\n fromElement: fromElement,\n toElement: toElement,\n screenX: screenX,\n screenY: screenY,\n pageX: pageX,\n pageY: pageY,\n clientX: clientX,\n clientY: clientY,\n x: clientX,\n y: clientY,\n movementX: moveX,\n movementY: moveY,\n offsetX: 0,\n offsetY: 0,\n layerX: 0,\n layerY: 0\n });\n }\n return event;\n };\n /**\n * Determine if an event's registered handlers should be execute synchronously or asynchronously.\n *\n * @returns {boolean}\n * @private\n */\n var _shouldPerformAsync = function(event) {\n var eventType = event && typeof event.type === \"string\" && event.type || \"\";\n return !/^(?:(?:before)?copy|destroy)$/.test(eventType);\n };\n /**\n * Control if a callback should be executed asynchronously or not.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallback = function(func, context, args, async) {\n if (async) {\n _setTimeout(function() {\n func.apply(context, args);\n }, 0);\n } else {\n func.apply(context, args);\n }\n };\n /**\n * Handle the actual dispatching of events to client instances.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallbacks = function(event) {\n if (!(typeof event === \"object\" && event && event.type)) {\n return;\n }\n var async = _shouldPerformAsync(event);\n var wildcardTypeHandlers = _handlers[\"*\"] || [];\n var specificTypeHandlers = _handlers[event.type] || [];\n var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);\n if (handlers && handlers.length) {\n var i, len, func, context, eventCopy, originalContext = this;\n for (i = 0, len = handlers.length; i < len; i++) {\n func = handlers[i];\n context = originalContext;\n if (typeof func === \"string\" && typeof _window[func] === \"function\") {\n func = _window[func];\n }\n if (typeof func === \"object\" && func && typeof func.handleEvent === \"function\") {\n context = func;\n func = func.handleEvent;\n }\n if (typeof func === \"function\") {\n eventCopy = _extend({}, event);\n _dispatchCallback(func, context, [ eventCopy ], async);\n }\n }\n }\n return this;\n };\n /**\n * Check an `error` event's `name` property to see if Flash has\n * already loaded, which rules out possible `iframe` sandboxing.\n * @private\n */\n var _getSandboxStatusFromErrorEvent = function(event) {\n var isSandboxed = null;\n if (_pageIsFramed === false || event && event.type === \"error\" && event.name && _errorsThatOnlyOccurAfterFlashLoads.indexOf(event.name) !== -1) {\n isSandboxed = false;\n }\n return isSandboxed;\n };\n /**\n * Preprocess any special behaviors, reactions, or state changes after receiving this event.\n * Executes only once per event emitted, NOT once per client.\n * @private\n */\n var _preprocessEvent = function(event) {\n var element = event.target || _currentElement || null;\n var sourceIsSwf = event._source === \"swf\";\n delete event._source;\n switch (event.type) {\n case \"error\":\n var isSandboxed = event.name === \"flash-sandboxed\" || _getSandboxStatusFromErrorEvent(event);\n if (typeof isSandboxed === \"boolean\") {\n _flashState.sandboxed = isSandboxed;\n }\n if (_flashStateErrorNames.indexOf(event.name) !== -1) {\n _extend(_flashState, {\n disabled: event.name === \"flash-disabled\",\n outdated: event.name === \"flash-outdated\",\n unavailable: event.name === \"flash-unavailable\",\n degraded: event.name === \"flash-degraded\",\n deactivated: event.name === \"flash-deactivated\",\n overdue: event.name === \"flash-overdue\",\n ready: false\n });\n } else if (event.name === \"version-mismatch\") {\n _zcSwfVersion = event.swfVersion;\n _extend(_flashState, {\n disabled: false,\n outdated: false,\n unavailable: false,\n degraded: false,\n deactivated: false,\n overdue: false,\n ready: false\n });\n }\n _clearTimeoutsAndPolling();\n break;\n\n case \"ready\":\n _zcSwfVersion = event.swfVersion;\n var wasDeactivated = _flashState.deactivated === true;\n _extend(_flashState, {\n disabled: false,\n outdated: false,\n sandboxed: false,\n unavailable: false,\n degraded: false,\n deactivated: false,\n overdue: wasDeactivated,\n ready: !wasDeactivated\n });\n _clearTimeoutsAndPolling();\n break;\n\n case \"beforecopy\":\n _copyTarget = element;\n break;\n\n case \"copy\":\n var textContent, htmlContent, targetEl = event.relatedTarget;\n if (!(_clipData[\"text/html\"] || _clipData[\"text/plain\"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n if (htmlContent !== textContent) {\n event.clipboardData.setData(\"text/html\", htmlContent);\n }\n } else if (!_clipData[\"text/plain\"] && event.target && (textContent = event.target.getAttribute(\"data-clipboard-text\"))) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n }\n break;\n\n case \"aftercopy\":\n _queueEmitClipboardErrors(event);\n ZeroClipboard.clearData();\n if (element && element !== _safeActiveElement() && element.focus) {\n element.focus();\n }\n break;\n\n case \"_mouseover\":\n ZeroClipboard.focus(element);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseenter\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseover\"\n }));\n }\n break;\n\n case \"_mouseout\":\n ZeroClipboard.blur();\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseleave\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseout\"\n }));\n }\n break;\n\n case \"_mousedown\":\n _addClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mouseup\":\n _removeClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_click\":\n _copyTarget = null;\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mousemove\":\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n }\n if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n return true;\n }\n };\n /**\n * Check an \"aftercopy\" event for clipboard errors and emit a corresponding \"error\" event.\n * @private\n */\n var _queueEmitClipboardErrors = function(aftercopyEvent) {\n if (aftercopyEvent.errors && aftercopyEvent.errors.length > 0) {\n var errorEvent = _deepCopy(aftercopyEvent);\n _extend(errorEvent, {\n type: \"error\",\n name: \"clipboard-error\"\n });\n delete errorEvent.success;\n _setTimeout(function() {\n ZeroClipboard.emit(errorEvent);\n }, 0);\n }\n };\n /**\n * Dispatch a synthetic MouseEvent.\n *\n * @returns `undefined`\n * @private\n */\n var _fireMouseEvent = function(event) {\n if (!(event && typeof event.type === \"string\" && event)) {\n return;\n }\n var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = {\n view: doc.defaultView || _window,\n canBubble: true,\n cancelable: true,\n detail: event.type === \"click\" ? 1 : 0,\n button: typeof event.which === \"number\" ? event.which - 1 : typeof event.button === \"number\" ? event.button : doc.createEvent ? 0 : 1\n }, args = _extend(defaults, event);\n if (!target) {\n return;\n }\n if (doc.createEvent && target.dispatchEvent) {\n args = [ args.type, args.canBubble, args.cancelable, args.view, args.detail, args.screenX, args.screenY, args.clientX, args.clientY, args.ctrlKey, args.altKey, args.shiftKey, args.metaKey, args.button, args.relatedTarget ];\n e = doc.createEvent(\"MouseEvents\");\n if (e.initMouseEvent) {\n e.initMouseEvent.apply(e, args);\n e._source = \"js\";\n target.dispatchEvent(e);\n }\n }\n };\n /**\n * Continuously poll the DOM until either:\n * (a) the fallback content becomes visible, or\n * (b) we receive an event from SWF (handled elsewhere)\n *\n * IMPORTANT:\n * This is NOT a necessary check but it can result in significantly faster\n * detection of bad `swfPath` configuration and/or network/server issues [in\n * supported browsers] than waiting for the entire `flashLoadTimeout` duration\n * to elapse before detecting that the SWF cannot be loaded. The detection\n * duration can be anywhere from 10-30 times faster [in supported browsers] by\n * using this approach.\n *\n * @returns `undefined`\n * @private\n */\n var _watchForSwfFallbackContent = function() {\n var maxWait = _globalConfig.flashLoadTimeout;\n if (typeof maxWait === \"number\" && maxWait >= 0) {\n var pollWait = Math.min(1e3, maxWait / 10);\n var fallbackContentId = _globalConfig.swfObjectId + \"_fallbackContent\";\n _swfFallbackCheckInterval = _setInterval(function() {\n var el = _document.getElementById(fallbackContentId);\n if (_isElementVisible(el)) {\n _clearTimeoutsAndPolling();\n _flashState.deactivated = null;\n ZeroClipboard.emit({\n type: \"error\",\n name: \"swf-not-found\"\n });\n }\n }, pollWait);\n }\n };\n /**\n * Create the HTML bridge element to embed the Flash object into.\n * @private\n */\n var _createHtmlBridge = function() {\n var container = _document.createElement(\"div\");\n container.id = _globalConfig.containerId;\n container.className = _globalConfig.containerClass;\n container.style.position = \"absolute\";\n container.style.left = \"0px\";\n container.style.top = \"-9999px\";\n container.style.width = \"1px\";\n container.style.height = \"1px\";\n container.style.zIndex = \"\" + _getSafeZIndex(_globalConfig.zIndex);\n return container;\n };\n /**\n * Get the HTML element container that wraps the Flash bridge object/element.\n * @private\n */\n var _getHtmlBridge = function(flashBridge) {\n var htmlBridge = flashBridge && flashBridge.parentNode;\n while (htmlBridge && htmlBridge.nodeName === \"OBJECT\" && htmlBridge.parentNode) {\n htmlBridge = htmlBridge.parentNode;\n }\n return htmlBridge || null;\n };\n /**\n * Create the SWF object.\n *\n * @returns The SWF object reference.\n * @private\n */\n var _embedSwf = function() {\n var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge);\n if (!flashBridge) {\n var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);\n var allowNetworking = allowScriptAccess === \"never\" ? \"none\" : \"all\";\n var flashvars = _vars(_extend({\n jsVersion: ZeroClipboard.version\n }, _globalConfig));\n var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);\n container = _createHtmlBridge();\n var divToBeReplaced = _document.createElement(\"div\");\n container.appendChild(divToBeReplaced);\n _document.body.appendChild(container);\n var tmpDiv = _document.createElement(\"div\");\n var usingActiveX = _flashState.pluginType === \"activex\";\n tmpDiv.innerHTML = '\" + (usingActiveX ? '' : \"\") + '' + '' + '' + '' + '' + '
     
    ' + \"
    \";\n flashBridge = tmpDiv.firstChild;\n tmpDiv = null;\n _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;\n container.replaceChild(flashBridge, divToBeReplaced);\n _watchForSwfFallbackContent();\n }\n if (!flashBridge) {\n flashBridge = _document[_globalConfig.swfObjectId];\n if (flashBridge && (len = flashBridge.length)) {\n flashBridge = flashBridge[len - 1];\n }\n if (!flashBridge && container) {\n flashBridge = container.firstChild;\n }\n }\n _flashState.bridge = flashBridge || null;\n return flashBridge;\n };\n /**\n * Destroy the SWF object.\n * @private\n */\n var _unembedSwf = function() {\n var flashBridge = _flashState.bridge;\n if (flashBridge) {\n var htmlBridge = _getHtmlBridge(flashBridge);\n if (htmlBridge) {\n if (_flashState.pluginType === \"activex\" && \"readyState\" in flashBridge) {\n flashBridge.style.display = \"none\";\n (function removeSwfFromIE() {\n if (flashBridge.readyState === 4) {\n for (var prop in flashBridge) {\n if (typeof flashBridge[prop] === \"function\") {\n flashBridge[prop] = null;\n }\n }\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n } else {\n _setTimeout(removeSwfFromIE, 10);\n }\n })();\n } else {\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n }\n }\n _clearTimeoutsAndPolling();\n _flashState.ready = null;\n _flashState.bridge = null;\n _flashState.deactivated = null;\n _zcSwfVersion = undefined;\n }\n };\n /**\n * Map the data format names of the \"clipData\" to Flash-friendly names.\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipDataToFlash = function(clipData) {\n var newClipData = {}, formatMap = {};\n if (!(typeof clipData === \"object\" && clipData)) {\n return;\n }\n for (var dataFormat in clipData) {\n if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === \"string\" && clipData[dataFormat]) {\n switch (dataFormat.toLowerCase()) {\n case \"text/plain\":\n case \"text\":\n case \"air:text\":\n case \"flash:text\":\n newClipData.text = clipData[dataFormat];\n formatMap.text = dataFormat;\n break;\n\n case \"text/html\":\n case \"html\":\n case \"air:html\":\n case \"flash:html\":\n newClipData.html = clipData[dataFormat];\n formatMap.html = dataFormat;\n break;\n\n case \"application/rtf\":\n case \"text/rtf\":\n case \"rtf\":\n case \"richtext\":\n case \"air:rtf\":\n case \"flash:rtf\":\n newClipData.rtf = clipData[dataFormat];\n formatMap.rtf = dataFormat;\n break;\n\n default:\n break;\n }\n }\n }\n return {\n data: newClipData,\n formatMap: formatMap\n };\n };\n /**\n * Map the data format names from Flash-friendly names back to their original \"clipData\" names (via a format mapping).\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipResultsFromFlash = function(clipResults, formatMap) {\n if (!(typeof clipResults === \"object\" && clipResults && typeof formatMap === \"object\" && formatMap)) {\n return clipResults;\n }\n var newResults = {};\n for (var prop in clipResults) {\n if (_hasOwn.call(clipResults, prop)) {\n if (prop === \"errors\") {\n newResults[prop] = clipResults[prop] ? clipResults[prop].slice() : [];\n for (var i = 0, len = newResults[prop].length; i < len; i++) {\n newResults[prop][i].format = formatMap[newResults[prop][i].format];\n }\n } else if (prop !== \"success\" && prop !== \"data\") {\n newResults[prop] = clipResults[prop];\n } else {\n newResults[prop] = {};\n var tmpHash = clipResults[prop];\n for (var dataFormat in tmpHash) {\n if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {\n newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];\n }\n }\n }\n }\n }\n return newResults;\n };\n /**\n * Will look at a path, and will create a \"?noCache={time}\" or \"&noCache={time}\"\n * query param string to return. Does NOT append that string to the original path.\n * This is useful because ExternalInterface often breaks when a Flash SWF is cached.\n *\n * @returns The `noCache` query param with necessary \"?\"/\"&\" prefix.\n * @private\n */\n var _cacheBust = function(path, options) {\n var cacheBust = options == null || options && options.cacheBust === true;\n if (cacheBust) {\n return (path.indexOf(\"?\") === -1 ? \"?\" : \"&\") + \"noCache=\" + _now();\n } else {\n return \"\";\n }\n };\n /**\n * Creates a query string for the FlashVars param.\n * Does NOT include the cache-busting query param.\n *\n * @returns FlashVars query string\n * @private\n */\n var _vars = function(options) {\n var i, len, domain, domains, str = \"\", trustedOriginsExpanded = [];\n if (options.trustedDomains) {\n if (typeof options.trustedDomains === \"string\") {\n domains = [ options.trustedDomains ];\n } else if (typeof options.trustedDomains === \"object\" && \"length\" in options.trustedDomains) {\n domains = options.trustedDomains;\n }\n }\n if (domains && domains.length) {\n for (i = 0, len = domains.length; i < len; i++) {\n if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === \"string\") {\n domain = _extractDomain(domains[i]);\n if (!domain) {\n continue;\n }\n if (domain === \"*\") {\n trustedOriginsExpanded.length = 0;\n trustedOriginsExpanded.push(domain);\n break;\n }\n trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, \"//\" + domain, _window.location.protocol + \"//\" + domain ]);\n }\n }\n }\n if (trustedOriginsExpanded.length) {\n str += \"trustedOrigins=\" + _encodeURIComponent(trustedOriginsExpanded.join(\",\"));\n }\n if (options.forceEnhancedClipboard === true) {\n str += (str ? \"&\" : \"\") + \"forceEnhancedClipboard=true\";\n }\n if (typeof options.swfObjectId === \"string\" && options.swfObjectId) {\n str += (str ? \"&\" : \"\") + \"swfObjectId=\" + _encodeURIComponent(options.swfObjectId);\n }\n if (typeof options.jsVersion === \"string\" && options.jsVersion) {\n str += (str ? \"&\" : \"\") + \"jsVersion=\" + _encodeURIComponent(options.jsVersion);\n }\n return str;\n };\n /**\n * Extract the domain (e.g. \"github.com\") from an origin (e.g. \"https://github.com\") or\n * URL (e.g. \"https://github.com/zeroclipboard/zeroclipboard/\").\n *\n * @returns the domain\n * @private\n */\n var _extractDomain = function(originOrUrl) {\n if (originOrUrl == null || originOrUrl === \"\") {\n return null;\n }\n originOrUrl = originOrUrl.replace(/^\\s+|\\s+$/g, \"\");\n if (originOrUrl === \"\") {\n return null;\n }\n var protocolIndex = originOrUrl.indexOf(\"//\");\n originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);\n var pathIndex = originOrUrl.indexOf(\"/\");\n originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);\n if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === \".swf\") {\n return null;\n }\n return originOrUrl || null;\n };\n /**\n * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.\n *\n * @returns The appropriate script access level.\n * @private\n */\n var _determineScriptAccess = function() {\n var _extractAllDomains = function(origins) {\n var i, len, tmp, resultsArray = [];\n if (typeof origins === \"string\") {\n origins = [ origins ];\n }\n if (!(typeof origins === \"object\" && origins && typeof origins.length === \"number\")) {\n return resultsArray;\n }\n for (i = 0, len = origins.length; i < len; i++) {\n if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {\n if (tmp === \"*\") {\n resultsArray.length = 0;\n resultsArray.push(\"*\");\n break;\n }\n if (resultsArray.indexOf(tmp) === -1) {\n resultsArray.push(tmp);\n }\n }\n }\n return resultsArray;\n };\n return function(currentDomain, configOptions) {\n var swfDomain = _extractDomain(configOptions.swfPath);\n if (swfDomain === null) {\n swfDomain = currentDomain;\n }\n var trustedDomains = _extractAllDomains(configOptions.trustedDomains);\n var len = trustedDomains.length;\n if (len > 0) {\n if (len === 1 && trustedDomains[0] === \"*\") {\n return \"always\";\n }\n if (trustedDomains.indexOf(currentDomain) !== -1) {\n if (len === 1 && currentDomain === swfDomain) {\n return \"sameDomain\";\n }\n return \"always\";\n }\n }\n return \"never\";\n };\n }();\n /**\n * Get the currently active/focused DOM element.\n *\n * @returns the currently active/focused element, or `null`\n * @private\n */\n var _safeActiveElement = function() {\n try {\n return _document.activeElement;\n } catch (err) {\n return null;\n }\n };\n /**\n * Add a class to an element, if it doesn't already have it.\n *\n * @returns The element, with its new class added.\n * @private\n */\n var _addClass = function(element, value) {\n var c, cl, className, classNames = [];\n if (typeof value === \"string\" && value) {\n classNames = value.split(/\\s+/);\n }\n if (element && element.nodeType === 1 && classNames.length > 0) {\n className = (\" \" + (element.className || \"\") + \" \").replace(/[\\t\\r\\n\\f]/g, \" \");\n for (c = 0, cl = classNames.length; c < cl; c++) {\n if (className.indexOf(\" \" + classNames[c] + \" \") === -1) {\n className += classNames[c] + \" \";\n }\n }\n className = className.replace(/^\\s+|\\s+$/g, \"\");\n if (className !== element.className) {\n element.className = className;\n }\n }\n return element;\n };\n /**\n * Remove a class from an element, if it has it.\n *\n * @returns The element, with its class removed.\n * @private\n */\n var _removeClass = function(element, value) {\n var c, cl, className, classNames = [];\n if (typeof value === \"string\" && value) {\n classNames = value.split(/\\s+/);\n }\n if (element && element.nodeType === 1 && classNames.length > 0) {\n if (element.className) {\n className = (\" \" + element.className + \" \").replace(/[\\t\\r\\n\\f]/g, \" \");\n for (c = 0, cl = classNames.length; c < cl; c++) {\n className = className.replace(\" \" + classNames[c] + \" \", \" \");\n }\n className = className.replace(/^\\s+|\\s+$/g, \"\");\n if (className !== element.className) {\n element.className = className;\n }\n }\n }\n return element;\n };\n /**\n * Attempt to interpret the element's CSS styling. If `prop` is `\"cursor\"`,\n * then we assume that it should be a hand (\"pointer\") cursor if the element\n * is an anchor element (\"a\" tag).\n *\n * @returns The computed style property.\n * @private\n */\n var _getStyle = function(el, prop) {\n var value = _getComputedStyle(el, null).getPropertyValue(prop);\n if (prop === \"cursor\") {\n if (!value || value === \"auto\") {\n if (el.nodeName === \"A\") {\n return \"pointer\";\n }\n }\n }\n return value;\n };\n /**\n * Get the absolutely positioned coordinates of a DOM element.\n *\n * @returns Object containing the element's position, width, and height.\n * @private\n */\n var _getElementPosition = function(el) {\n var pos = {\n left: 0,\n top: 0,\n width: 0,\n height: 0\n };\n if (el.getBoundingClientRect) {\n var elRect = el.getBoundingClientRect();\n var pageXOffset = _window.pageXOffset;\n var pageYOffset = _window.pageYOffset;\n var leftBorderWidth = _document.documentElement.clientLeft || 0;\n var topBorderWidth = _document.documentElement.clientTop || 0;\n var leftBodyOffset = 0;\n var topBodyOffset = 0;\n if (_getStyle(_document.body, \"position\") === \"relative\") {\n var bodyRect = _document.body.getBoundingClientRect();\n var htmlRect = _document.documentElement.getBoundingClientRect();\n leftBodyOffset = bodyRect.left - htmlRect.left || 0;\n topBodyOffset = bodyRect.top - htmlRect.top || 0;\n }\n pos.left = elRect.left + pageXOffset - leftBorderWidth - leftBodyOffset;\n pos.top = elRect.top + pageYOffset - topBorderWidth - topBodyOffset;\n pos.width = \"width\" in elRect ? elRect.width : elRect.right - elRect.left;\n pos.height = \"height\" in elRect ? elRect.height : elRect.bottom - elRect.top;\n }\n return pos;\n };\n /**\n * Determine is an element is visible somewhere within the document (page).\n *\n * @returns Boolean\n * @private\n */\n var _isElementVisible = function(el) {\n if (!el) {\n return false;\n }\n var styles = _getComputedStyle(el, null);\n if (!styles) {\n return false;\n }\n var hasCssHeight = _parseFloat(styles.height) > 0;\n var hasCssWidth = _parseFloat(styles.width) > 0;\n var hasCssTop = _parseFloat(styles.top) >= 0;\n var hasCssLeft = _parseFloat(styles.left) >= 0;\n var cssKnows = hasCssHeight && hasCssWidth && hasCssTop && hasCssLeft;\n var rect = cssKnows ? null : _getElementPosition(el);\n var isVisible = styles.display !== \"none\" && styles.visibility !== \"collapse\" && (cssKnows || !!rect && (hasCssHeight || rect.height > 0) && (hasCssWidth || rect.width > 0) && (hasCssTop || rect.top >= 0) && (hasCssLeft || rect.left >= 0));\n return isVisible;\n };\n /**\n * Clear all existing timeouts and interval polling delegates.\n *\n * @returns `undefined`\n * @private\n */\n var _clearTimeoutsAndPolling = function() {\n _clearTimeout(_flashCheckTimeout);\n _flashCheckTimeout = 0;\n _clearInterval(_swfFallbackCheckInterval);\n _swfFallbackCheckInterval = 0;\n };\n /**\n * Reposition the Flash object to cover the currently activated element.\n *\n * @returns `undefined`\n * @private\n */\n var _reposition = function() {\n var htmlBridge;\n if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {\n var pos = _getElementPosition(_currentElement);\n _extend(htmlBridge.style, {\n width: pos.width + \"px\",\n height: pos.height + \"px\",\n top: pos.top + \"px\",\n left: pos.left + \"px\",\n zIndex: \"\" + _getSafeZIndex(_globalConfig.zIndex)\n });\n }\n };\n /**\n * Sends a signal to the Flash object to display the hand cursor if `true`.\n *\n * @returns `undefined`\n * @private\n */\n var _setHandCursor = function(enabled) {\n if (_flashState.ready === true) {\n if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === \"function\") {\n _flashState.bridge.setHandCursor(enabled);\n } else {\n _flashState.ready = false;\n }\n }\n };\n /**\n * Get a safe value for `zIndex`\n *\n * @returns an integer, or \"auto\"\n * @private\n */\n var _getSafeZIndex = function(val) {\n if (/^(?:auto|inherit)$/.test(val)) {\n return val;\n }\n var zIndex;\n if (typeof val === \"number\" && !_isNaN(val)) {\n zIndex = val;\n } else if (typeof val === \"string\") {\n zIndex = _getSafeZIndex(_parseInt(val, 10));\n }\n return typeof zIndex === \"number\" ? zIndex : \"auto\";\n };\n /**\n * Ensure OS-compliant line endings, i.e. \"\\r\\n\" on Windows, \"\\n\" elsewhere\n *\n * @returns string\n * @private\n */\n var _fixLineEndings = function(content) {\n var replaceRegex = /(\\r\\n|\\r|\\n)/g;\n if (typeof content === \"string\" && _globalConfig.fixLineEndings === true) {\n if (_isWindows()) {\n if (/((^|[^\\r])\\n|\\r([^\\n]|$))/.test(content)) {\n content = content.replace(replaceRegex, \"\\r\\n\");\n }\n } else if (/\\r/.test(content)) {\n content = content.replace(replaceRegex, \"\\n\");\n }\n }\n return content;\n };\n /**\n * Attempt to detect if ZeroClipboard is executing inside of a sandboxed iframe.\n * If it is, Flash Player cannot be used, so ZeroClipboard is dead in the water.\n *\n * @see {@link http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Dec/0002.html}\n * @see {@link https://github.com/zeroclipboard/zeroclipboard/issues/511}\n * @see {@link http://zeroclipboard.org/test-iframes.html}\n *\n * @returns `true` (is sandboxed), `false` (is not sandboxed), or `null` (uncertain)\n * @private\n */\n var _detectSandbox = function(doNotReassessFlashSupport) {\n var effectiveScriptOrigin, frame, frameError, previousState = _flashState.sandboxed, isSandboxed = null;\n doNotReassessFlashSupport = doNotReassessFlashSupport === true;\n if (_pageIsFramed === false) {\n isSandboxed = false;\n } else {\n try {\n frame = window.frameElement || null;\n } catch (e) {\n frameError = {\n name: e.name,\n message: e.message\n };\n }\n if (frame && frame.nodeType === 1 && frame.nodeName === \"IFRAME\") {\n try {\n isSandboxed = frame.hasAttribute(\"sandbox\");\n } catch (e) {\n isSandboxed = null;\n }\n } else {\n try {\n effectiveScriptOrigin = document.domain || null;\n } catch (e) {\n effectiveScriptOrigin = null;\n }\n if (effectiveScriptOrigin === null || frameError && frameError.name === \"SecurityError\" && /(^|[\\s\\(\\[@])sandbox(es|ed|ing|[\\s\\.,!\\)\\]@]|$)/.test(frameError.message.toLowerCase())) {\n isSandboxed = true;\n }\n }\n }\n _flashState.sandboxed = isSandboxed;\n if (previousState !== isSandboxed && !doNotReassessFlashSupport) {\n _detectFlashSupport(_ActiveXObject);\n }\n return isSandboxed;\n };\n /**\n * Detect the Flash Player status, version, and plugin type.\n *\n * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}\n * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}\n *\n * @returns `undefined`\n * @private\n */\n var _detectFlashSupport = function(ActiveXObject) {\n var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = \"\";\n /**\n * Derived from Apple's suggested sniffer.\n * @param {String} desc e.g. \"Shockwave Flash 7.0 r61\"\n * @returns {String} \"7.0.61\"\n * @private\n */\n function parseFlashVersion(desc) {\n var matches = desc.match(/[\\d]+/g);\n matches.length = 3;\n return matches.join(\".\");\n }\n function isPepperFlash(flashPlayerFileName) {\n return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\\.dll|libpepflashplayer\\.so|pepperflashplayer\\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === \"chrome.plugin\");\n }\n function inspectPlugin(plugin) {\n if (plugin) {\n hasFlash = true;\n if (plugin.version) {\n flashVersion = parseFlashVersion(plugin.version);\n }\n if (!flashVersion && plugin.description) {\n flashVersion = parseFlashVersion(plugin.description);\n }\n if (plugin.filename) {\n isPPAPI = isPepperFlash(plugin.filename);\n }\n }\n }\n if (_navigator.plugins && _navigator.plugins.length) {\n plugin = _navigator.plugins[\"Shockwave Flash\"];\n inspectPlugin(plugin);\n if (_navigator.plugins[\"Shockwave Flash 2.0\"]) {\n hasFlash = true;\n flashVersion = \"2.0.0.11\";\n }\n } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {\n mimeType = _navigator.mimeTypes[\"application/x-shockwave-flash\"];\n plugin = mimeType && mimeType.enabledPlugin;\n inspectPlugin(plugin);\n } else if (typeof ActiveXObject !== \"undefined\") {\n isActiveX = true;\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.7\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e1) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.6\");\n hasFlash = true;\n flashVersion = \"6.0.21\";\n } catch (e2) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e3) {\n isActiveX = false;\n }\n }\n }\n }\n _flashState.disabled = hasFlash !== true;\n _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion);\n _flashState.version = flashVersion || \"0.0.0\";\n _flashState.pluginType = isPPAPI ? \"pepper\" : isActiveX ? \"activex\" : hasFlash ? \"netscape\" : \"unknown\";\n };\n /**\n * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.\n */\n _detectFlashSupport(_ActiveXObject);\n /**\n * Always assess the `sandboxed` state of the page at important Flash-related moments.\n */\n _detectSandbox(true);\n /**\n * A shell constructor for `ZeroClipboard` client instances.\n *\n * @constructor\n */\n var ZeroClipboard = function() {\n if (!(this instanceof ZeroClipboard)) {\n return new ZeroClipboard();\n }\n if (typeof ZeroClipboard._createClient === \"function\") {\n ZeroClipboard._createClient.apply(this, _args(arguments));\n }\n };\n /**\n * The ZeroClipboard library's version number.\n *\n * @static\n * @readonly\n * @property {string}\n */\n _defineProperty(ZeroClipboard, \"version\", {\n value: \"2.3.0-beta.1\",\n writable: false,\n configurable: true,\n enumerable: true\n });\n /**\n * Update or get a copy of the ZeroClipboard global configuration.\n * Returns a copy of the current/updated configuration.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.config = function() {\n return _config.apply(this, _args(arguments));\n };\n /**\n * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.state = function() {\n return _state.apply(this, _args(arguments));\n };\n /**\n * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.\n *\n * @returns Boolean\n * @static\n */\n ZeroClipboard.isFlashUnusable = function() {\n return _isFlashUnusable.apply(this, _args(arguments));\n };\n /**\n * Register an event listener.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.on = function() {\n return _on.apply(this, _args(arguments));\n };\n /**\n * Unregister an event listener.\n * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.\n * If no `eventType` is provided, it will unregister all listeners for every event type.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.off = function() {\n return _off.apply(this, _args(arguments));\n };\n /**\n * Retrieve event listeners for an `eventType`.\n * If no `eventType` is provided, it will retrieve all listeners for every event type.\n *\n * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`\n */\n ZeroClipboard.handlers = function() {\n return _listeners.apply(this, _args(arguments));\n };\n /**\n * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.\n *\n * @returns For the \"copy\" event, returns the Flash-friendly \"clipData\" object; otherwise `undefined`.\n * @static\n */\n ZeroClipboard.emit = function() {\n return _emit.apply(this, _args(arguments));\n };\n /**\n * Create and embed the Flash object.\n *\n * @returns The Flash object\n * @static\n */\n ZeroClipboard.create = function() {\n return _create.apply(this, _args(arguments));\n };\n /**\n * Self-destruct and clean up everything, including the embedded Flash object.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.destroy = function() {\n return _destroy.apply(this, _args(arguments));\n };\n /**\n * Set the pending data for clipboard injection.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.setData = function() {\n return _setData.apply(this, _args(arguments));\n };\n /**\n * Clear the pending data for clipboard injection.\n * If no `format` is provided, all pending data formats will be cleared.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.clearData = function() {\n return _clearData.apply(this, _args(arguments));\n };\n /**\n * Get a copy of the pending data for clipboard injection.\n * If no `format` is provided, a copy of ALL pending data formats will be returned.\n *\n * @returns `String` or `Object`\n * @static\n */\n ZeroClipboard.getData = function() {\n return _getData.apply(this, _args(arguments));\n };\n /**\n * Sets the current HTML object that the Flash object should overlay. This will put the global\n * Flash object on top of the current element; depending on the setup, this may also set the\n * pending clipboard text data as well as the Flash object's wrapping element's title attribute\n * based on the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.focus = ZeroClipboard.activate = function() {\n return _focus.apply(this, _args(arguments));\n };\n /**\n * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on\n * the setup, this may also unset the Flash object's wrapping element's title attribute based on\n * the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.blur = ZeroClipboard.deactivate = function() {\n return _blur.apply(this, _args(arguments));\n };\n /**\n * Returns the currently focused/\"activated\" HTML element that the Flash object is wrapping.\n *\n * @returns `HTMLElement` or `null`\n * @static\n */\n ZeroClipboard.activeElement = function() {\n return _activeElement.apply(this, _args(arguments));\n };\n if (typeof define === \"function\" && define.amd) {\n define(function() {\n return ZeroClipboard;\n });\n } else if (typeof module === \"object\" && module && typeof module.exports === \"object\" && module.exports) {\n module.exports = ZeroClipboard;\n } else {\n window.ZeroClipboard = ZeroClipboard;\n }\n})(function() {\n return this || window;\n}());"]} \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.js b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.js new file mode 100644 index 000000000..014cd004d --- /dev/null +++ b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.js @@ -0,0 +1,2611 @@ +/*! + * ZeroClipboard + * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. + * Copyright (c) 2009-2015 Jon Rohan, James M. Greene + * Licensed MIT + * http://zeroclipboard.org/ + * v2.3.0-beta.1 + */ +(function(window, undefined) { + "use strict"; + /** + * Store references to critically important global functions that may be + * overridden on certain web pages. + */ + var _window = window, _document = _window.document, _navigator = _window.navigator, _setTimeout = _window.setTimeout, _clearTimeout = _window.clearTimeout, _setInterval = _window.setInterval, _clearInterval = _window.clearInterval, _getComputedStyle = _window.getComputedStyle, _encodeURIComponent = _window.encodeURIComponent, _ActiveXObject = _window.ActiveXObject, _Error = _window.Error, _parseInt = _window.Number.parseInt || _window.parseInt, _parseFloat = _window.Number.parseFloat || _window.parseFloat, _isNaN = _window.Number.isNaN || _window.isNaN, _now = _window.Date.now, _keys = _window.Object.keys, _defineProperty = _window.Object.defineProperty, _hasOwn = _window.Object.prototype.hasOwnProperty, _slice = _window.Array.prototype.slice, _unwrap = function() { + var unwrapper = function(el) { + return el; + }; + if (typeof _window.wrap === "function" && typeof _window.unwrap === "function") { + try { + var div = _document.createElement("div"); + var unwrappedDiv = _window.unwrap(div); + if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) { + unwrapper = _window.unwrap; + } + } catch (e) {} + } + return unwrapper; + }(); + /** + * Convert an `arguments` object into an Array. + * + * @returns The arguments as an Array + * @private + */ + var _args = function(argumentsObj) { + return _slice.call(argumentsObj, 0); + }; + /** + * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`. + * + * @returns The target object, augmented + * @private + */ + var _extend = function() { + var i, len, arg, prop, src, copy, args = _args(arguments), target = args[0] || {}; + for (i = 1, len = args.length; i < len; i++) { + if ((arg = args[i]) != null) { + for (prop in arg) { + if (_hasOwn.call(arg, prop)) { + src = target[prop]; + copy = arg[prop]; + if (target !== copy && copy !== undefined) { + target[prop] = copy; + } + } + } + } + } + return target; + }; + /** + * Return a deep copy of the source object or array. + * + * @returns Object or Array + * @private + */ + var _deepCopy = function(source) { + var copy, i, len, prop; + if (typeof source !== "object" || source == null || typeof source.nodeType === "number") { + copy = source; + } else if (typeof source.length === "number") { + copy = []; + for (i = 0, len = source.length; i < len; i++) { + if (_hasOwn.call(source, i)) { + copy[i] = _deepCopy(source[i]); + } + } + } else { + copy = {}; + for (prop in source) { + if (_hasOwn.call(source, prop)) { + copy[prop] = _deepCopy(source[prop]); + } + } + } + return copy; + }; + /** + * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep. + * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to + * be kept. + * + * @returns A new filtered object. + * @private + */ + var _pick = function(obj, keys) { + var newObj = {}; + for (var i = 0, len = keys.length; i < len; i++) { + if (keys[i] in obj) { + newObj[keys[i]] = obj[keys[i]]; + } + } + return newObj; + }; + /** + * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit. + * The inverse of `_pick`. + * + * @returns A new filtered object. + * @private + */ + var _omit = function(obj, keys) { + var newObj = {}; + for (var prop in obj) { + if (keys.indexOf(prop) === -1) { + newObj[prop] = obj[prop]; + } + } + return newObj; + }; + /** + * Remove all owned, enumerable properties from an object. + * + * @returns The original object without its owned, enumerable properties. + * @private + */ + var _deleteOwnProperties = function(obj) { + if (obj) { + for (var prop in obj) { + if (_hasOwn.call(obj, prop)) { + delete obj[prop]; + } + } + } + return obj; + }; + /** + * Determine if an element is contained within another element. + * + * @returns Boolean + * @private + */ + var _containedBy = function(el, ancestorEl) { + if (el && el.nodeType === 1 && el.ownerDocument && ancestorEl && (ancestorEl.nodeType === 1 && ancestorEl.ownerDocument && ancestorEl.ownerDocument === el.ownerDocument || ancestorEl.nodeType === 9 && !ancestorEl.ownerDocument && ancestorEl === el.ownerDocument)) { + do { + if (el === ancestorEl) { + return true; + } + el = el.parentNode; + } while (el); + } + return false; + }; + /** + * Get the URL path's parent directory. + * + * @returns String or `undefined` + * @private + */ + var _getDirPathOfUrl = function(url) { + var dir; + if (typeof url === "string" && url) { + dir = url.split("#")[0].split("?")[0]; + dir = url.slice(0, url.lastIndexOf("/") + 1); + } + return dir; + }; + /** + * Get the current script's URL by throwing an `Error` and analyzing it. + * + * @returns String or `undefined` + * @private + */ + var _getCurrentScriptUrlFromErrorStack = function(stack) { + var url, matches; + if (typeof stack === "string" && stack) { + matches = stack.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/); + if (matches && matches[1]) { + url = matches[1]; + } else { + matches = stack.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/); + if (matches && matches[1]) { + url = matches[1]; + } + } + } + return url; + }; + /** + * Get the current script's URL by throwing an `Error` and analyzing it. + * + * @returns String or `undefined` + * @private + */ + var _getCurrentScriptUrlFromError = function() { + var url, err; + try { + throw new _Error(); + } catch (e) { + err = e; + } + if (err) { + url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack); + } + return url; + }; + /** + * Get the current script's URL. + * + * @returns String or `undefined` + * @private + */ + var _getCurrentScriptUrl = function() { + var jsPath, scripts, i; + if (_document.currentScript && (jsPath = _document.currentScript.src)) { + return jsPath; + } + scripts = _document.getElementsByTagName("script"); + if (scripts.length === 1) { + return scripts[0].src || undefined; + } + if ("readyState" in scripts[0]) { + for (i = scripts.length; i--; ) { + if (scripts[i].readyState === "interactive" && (jsPath = scripts[i].src)) { + return jsPath; + } + } + } + if (_document.readyState === "loading" && (jsPath = scripts[scripts.length - 1].src)) { + return jsPath; + } + if (jsPath = _getCurrentScriptUrlFromError()) { + return jsPath; + } + return undefined; + }; + /** + * Get the unanimous parent directory of ALL script tags. + * If any script tags are either (a) inline or (b) from differing parent + * directories, this method must return `undefined`. + * + * @returns String or `undefined` + * @private + */ + var _getUnanimousScriptParentDir = function() { + var i, jsDir, jsPath, scripts = _document.getElementsByTagName("script"); + for (i = scripts.length; i--; ) { + if (!(jsPath = scripts[i].src)) { + jsDir = null; + break; + } + jsPath = _getDirPathOfUrl(jsPath); + if (jsDir == null) { + jsDir = jsPath; + } else if (jsDir !== jsPath) { + jsDir = null; + break; + } + } + return jsDir || undefined; + }; + /** + * Get the presumed location of the "ZeroClipboard.swf" file, based on the location + * of the executing JavaScript file (e.g. "ZeroClipboard.js", etc.). + * + * @returns String + * @private + */ + var _getDefaultSwfPath = function() { + var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || ""; + //return jsDir + "ZeroClipboard.swf"; + return "/js/zeroclipboard/ZeroClipboard.swf"; + }; + /** + * Is the client's operating system some version of Windows? + * + * @returns Boolean + * @private + */ + var _isWindows = function() { + var isWindowsRegex = /win(dows|[\s]?(nt|me|ce|xp|vista|[\d]+))/i; + return !!_navigator && (isWindowsRegex.test(_navigator.appVersion || "") || isWindowsRegex.test(_navigator.platform || "") || (_navigator.userAgent || "").indexOf("Windows") !== -1); + }; + /** + * Keep track of if the page is framed (in an `iframe`). This can never change. + * @private + */ + var _pageIsFramed = function() { + return window.opener == null && (!!window.top && window != window.top || !!window.parent && window != window.parent); + }(); + /** + * Keep track of the state of the Flash object. + * @private + */ + var _flashState = { + bridge: null, + version: "0.0.0", + pluginType: "unknown", + disabled: null, + outdated: null, + sandboxed: null, + unavailable: null, + degraded: null, + deactivated: null, + overdue: null, + ready: null + }; + /** + * The minimum Flash Player version required to use ZeroClipboard completely. + * @readonly + * @private + */ + var _minimumFlashVersion = "11.0.0"; + /** + * The ZeroClipboard library version number, as reported by Flash, at the time the SWF was compiled. + */ + var _zcSwfVersion; + /** + * Keep track of all event listener registrations. + * @private + */ + var _handlers = {}; + /** + * Keep track of the currently activated element. + * @private + */ + var _currentElement; + /** + * Keep track of the element that was activated when a `copy` process started. + * @private + */ + var _copyTarget; + /** + * Keep track of data for the pending clipboard transaction. + * @private + */ + var _clipData = {}; + /** + * Keep track of data formats for the pending clipboard transaction. + * @private + */ + var _clipDataFormatMap = null; + /** + * Keep track of the Flash availability check timeout. + * @private + */ + var _flashCheckTimeout = 0; + /** + * Keep track of SWF network errors interval polling. + * @private + */ + var _swfFallbackCheckInterval = 0; + /** + * The `message` store for events + * @private + */ + var _eventMessages = { + ready: "Flash communication is established", + error: { + "flash-disabled": "Flash is disabled or not installed. May also be attempting to run Flash in a sandboxed iframe, which is impossible.", + "flash-outdated": "Flash is too outdated to support ZeroClipboard", + "flash-sandboxed": "Attempting to run Flash in a sandboxed iframe, which is impossible", + "flash-unavailable": "Flash is unable to communicate bidirectionally with JavaScript", + "flash-degraded": "Flash is unable to preserve data fidelity when communicating with JavaScript", + "flash-deactivated": "Flash is too outdated for your browser and/or is configured as click-to-activate.\nThis may also mean that the ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity.\nMay also be attempting to run Flash in a sandboxed iframe, which is impossible.", + "flash-overdue": "Flash communication was established but NOT within the acceptable time limit", + "version-mismatch": "ZeroClipboard JS version number does not match ZeroClipboard SWF version number", + "clipboard-error": "At least one error was thrown while ZeroClipboard was attempting to inject your data into the clipboard", + "config-mismatch": "ZeroClipboard configuration does not match Flash's reality", + "swf-not-found": "The ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity" + } + }; + /** + * The `name`s of `error` events that can only occur is Flash has at least + * been able to load the SWF successfully. + * @private + */ + var _errorsThatOnlyOccurAfterFlashLoads = [ "flash-unavailable", "flash-degraded", "flash-overdue", "version-mismatch", "config-mismatch", "clipboard-error" ]; + /** + * The `name`s of `error` events that should likely result in the `_flashState` + * variable's property values being updated. + * @private + */ + var _flashStateErrorNames = [ "flash-disabled", "flash-outdated", "flash-sandboxed", "flash-unavailable", "flash-degraded", "flash-deactivated", "flash-overdue" ]; + /** + * A RegExp to match the `name` property of `error` events related to Flash. + * @private + */ + var _flashStateErrorNameMatchingRegex = new RegExp("^flash-(" + _flashStateErrorNames.map(function(errorName) { + return errorName.replace(/^flash-/, ""); + }).join("|") + ")$"); + /** + * A RegExp to match the `name` property of `error` events related to Flash, + * which is enabled. + * @private + */ + var _flashStateEnabledErrorNameMatchingRegex = new RegExp("^flash-(" + _flashStateErrorNames.slice(1).map(function(errorName) { + return errorName.replace(/^flash-/, ""); + }).join("|") + ")$"); + /** + * ZeroClipboard configuration defaults for the Core module. + * @private + */ + var _globalConfig = { + swfPath: _getDefaultSwfPath(), + trustedDomains: window.location.host ? [ window.location.host ] : [], + cacheBust: true, + forceEnhancedClipboard: false, + flashLoadTimeout: 3e4, + autoActivate: true, + bubbleEvents: true, + fixLineEndings: true, + containerId: "global-zeroclipboard-html-bridge", + containerClass: "global-zeroclipboard-container", + swfObjectId: "global-zeroclipboard-flash-bridge", + hoverClass: "zeroclipboard-is-hover", + activeClass: "zeroclipboard-is-active", + forceHandCursor: false, + title: null, + zIndex: 999999999 + }; + /** + * The underlying implementation of `ZeroClipboard.config`. + * @private + */ + var _config = function(options) { + if (typeof options === "object" && options !== null) { + for (var prop in options) { + if (_hasOwn.call(options, prop)) { + if (/^(?:forceHandCursor|title|zIndex|bubbleEvents|fixLineEndings)$/.test(prop)) { + _globalConfig[prop] = options[prop]; + } else if (_flashState.bridge == null) { + if (prop === "containerId" || prop === "swfObjectId") { + if (_isValidHtml4Id(options[prop])) { + _globalConfig[prop] = options[prop]; + } else { + throw new Error("The specified `" + prop + "` value is not valid as an HTML4 Element ID"); + } + } else { + _globalConfig[prop] = options[prop]; + } + } + } + } + } + if (typeof options === "string" && options) { + if (_hasOwn.call(_globalConfig, options)) { + return _globalConfig[options]; + } + return; + } + return _deepCopy(_globalConfig); + }; + /** + * The underlying implementation of `ZeroClipboard.state`. + * @private + */ + var _state = function() { + _detectSandbox(); + return { + browser: _pick(_navigator, [ "userAgent", "platform", "appName", "appVersion" ]), + flash: _omit(_flashState, [ "bridge" ]), + zeroclipboard: { + version: ZeroClipboard.version, + config: ZeroClipboard.config() + } + }; + }; + /** + * The underlying implementation of `ZeroClipboard.isFlashUnusable`. + * @private + */ + var _isFlashUnusable = function() { + return !!(_flashState.disabled || _flashState.outdated || _flashState.sandboxed || _flashState.unavailable || _flashState.degraded || _flashState.deactivated); + }; + /** + * The underlying implementation of `ZeroClipboard.on`. + * @private + */ + var _on = function(eventType, listener) { + var i, len, events, added = {}; + if (typeof eventType === "string" && eventType) { + events = eventType.toLowerCase().split(/\s+/); + } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") { + for (i in eventType) { + if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") { + ZeroClipboard.on(i, eventType[i]); + } + } + } + if (events && events.length) { + for (i = 0, len = events.length; i < len; i++) { + eventType = events[i].replace(/^on/, ""); + added[eventType] = true; + if (!_handlers[eventType]) { + _handlers[eventType] = []; + } + _handlers[eventType].push(listener); + } + if (added.ready && _flashState.ready) { + ZeroClipboard.emit({ + type: "ready" + }); + } + if (added.error) { + for (i = 0, len = _flashStateErrorNames.length; i < len; i++) { + if (_flashState[_flashStateErrorNames[i].replace(/^flash-/, "")] === true) { + ZeroClipboard.emit({ + type: "error", + name: _flashStateErrorNames[i] + }); + break; + } + } + if (_zcSwfVersion !== undefined && ZeroClipboard.version !== _zcSwfVersion) { + ZeroClipboard.emit({ + type: "error", + name: "version-mismatch", + jsVersion: ZeroClipboard.version, + swfVersion: _zcSwfVersion + }); + } + } + } + return ZeroClipboard; + }; + /** + * The underlying implementation of `ZeroClipboard.off`. + * @private + */ + var _off = function(eventType, listener) { + var i, len, foundIndex, events, perEventHandlers; + if (arguments.length === 0) { + events = _keys(_handlers); + } else if (typeof eventType === "string" && eventType) { + events = eventType.split(/\s+/); + } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") { + for (i in eventType) { + if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") { + ZeroClipboard.off(i, eventType[i]); + } + } + } + if (events && events.length) { + for (i = 0, len = events.length; i < len; i++) { + eventType = events[i].toLowerCase().replace(/^on/, ""); + perEventHandlers = _handlers[eventType]; + if (perEventHandlers && perEventHandlers.length) { + if (listener) { + foundIndex = perEventHandlers.indexOf(listener); + while (foundIndex !== -1) { + perEventHandlers.splice(foundIndex, 1); + foundIndex = perEventHandlers.indexOf(listener, foundIndex); + } + } else { + perEventHandlers.length = 0; + } + } + } + } + return ZeroClipboard; + }; + /** + * The underlying implementation of `ZeroClipboard.handlers`. + * @private + */ + var _listeners = function(eventType) { + var copy; + if (typeof eventType === "string" && eventType) { + copy = _deepCopy(_handlers[eventType]) || null; + } else { + copy = _deepCopy(_handlers); + } + return copy; + }; + /** + * The underlying implementation of `ZeroClipboard.emit`. + * @private + */ + var _emit = function(event) { + var eventCopy, returnVal, tmp; + event = _createEvent(event); + if (!event) { + return; + } + if (_preprocessEvent(event)) { + return; + } + if (event.type === "ready" && _flashState.overdue === true) { + return ZeroClipboard.emit({ + type: "error", + name: "flash-overdue" + }); + } + eventCopy = _extend({}, event); + _dispatchCallbacks.call(this, eventCopy); + if (event.type === "copy") { + tmp = _mapClipDataToFlash(_clipData); + returnVal = tmp.data; + _clipDataFormatMap = tmp.formatMap; + } + return returnVal; + }; + /** + * The underlying implementation of `ZeroClipboard.create`. + * @private + */ + var _create = function() { + var previousState = _flashState.sandboxed; + _detectSandbox(); + if (typeof _flashState.ready !== "boolean") { + _flashState.ready = false; + } + if (_flashState.sandboxed !== previousState && _flashState.sandboxed === true) { + _flashState.ready = false; + ZeroClipboard.emit({ + type: "error", + name: "flash-sandboxed" + }); + } else if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) { + var maxWait = _globalConfig.flashLoadTimeout; + if (typeof maxWait === "number" && maxWait >= 0) { + _flashCheckTimeout = _setTimeout(function() { + if (typeof _flashState.deactivated !== "boolean") { + _flashState.deactivated = true; + } + if (_flashState.deactivated === true) { + ZeroClipboard.emit({ + type: "error", + name: "flash-deactivated" + }); + } + }, maxWait); + } + _flashState.overdue = false; + _embedSwf(); + } + }; + /** + * The underlying implementation of `ZeroClipboard.destroy`. + * @private + */ + var _destroy = function() { + ZeroClipboard.clearData(); + ZeroClipboard.blur(); + ZeroClipboard.emit("destroy"); + _unembedSwf(); + ZeroClipboard.off(); + }; + /** + * The underlying implementation of `ZeroClipboard.setData`. + * @private + */ + var _setData = function(format, data) { + var dataObj; + if (typeof format === "object" && format && typeof data === "undefined") { + dataObj = format; + ZeroClipboard.clearData(); + } else if (typeof format === "string" && format) { + dataObj = {}; + dataObj[format] = data; + } else { + return; + } + for (var dataFormat in dataObj) { + if (typeof dataFormat === "string" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === "string" && dataObj[dataFormat]) { + _clipData[dataFormat] = _fixLineEndings(dataObj[dataFormat]); + } + } + }; + /** + * The underlying implementation of `ZeroClipboard.clearData`. + * @private + */ + var _clearData = function(format) { + if (typeof format === "undefined") { + _deleteOwnProperties(_clipData); + _clipDataFormatMap = null; + } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) { + delete _clipData[format]; + } + }; + /** + * The underlying implementation of `ZeroClipboard.getData`. + * @private + */ + var _getData = function(format) { + if (typeof format === "undefined") { + return _deepCopy(_clipData); + } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) { + return _clipData[format]; + } + }; + /** + * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`. + * @private + */ + var _focus = function(element) { + if (!(element && element.nodeType === 1)) { + return; + } + if (_currentElement) { + _removeClass(_currentElement, _globalConfig.activeClass); + if (_currentElement !== element) { + _removeClass(_currentElement, _globalConfig.hoverClass); + } + } + _currentElement = element; + _addClass(element, _globalConfig.hoverClass); + var newTitle = element.getAttribute("title") || _globalConfig.title; + if (typeof newTitle === "string" && newTitle) { + var htmlBridge = _getHtmlBridge(_flashState.bridge); + if (htmlBridge) { + htmlBridge.setAttribute("title", newTitle); + } + } + var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, "cursor") === "pointer"; + _setHandCursor(useHandCursor); + _reposition(); + }; + /** + * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`. + * @private + */ + var _blur = function() { + var htmlBridge = _getHtmlBridge(_flashState.bridge); + if (htmlBridge) { + htmlBridge.removeAttribute("title"); + htmlBridge.style.left = "0px"; + htmlBridge.style.top = "-9999px"; + htmlBridge.style.width = "1px"; + htmlBridge.style.height = "1px"; + } + if (_currentElement) { + _removeClass(_currentElement, _globalConfig.hoverClass); + _removeClass(_currentElement, _globalConfig.activeClass); + _currentElement = null; + } + }; + /** + * The underlying implementation of `ZeroClipboard.activeElement`. + * @private + */ + var _activeElement = function() { + return _currentElement || null; + }; + /** + * Check if a value is a valid HTML4 `ID` or `Name` token. + * @private + */ + var _isValidHtml4Id = function(id) { + return typeof id === "string" && id && /^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(id); + }; + /** + * Create or update an `event` object, based on the `eventType`. + * @private + */ + var _createEvent = function(event) { + var eventType; + if (typeof event === "string" && event) { + eventType = event; + event = {}; + } else if (typeof event === "object" && event && typeof event.type === "string" && event.type) { + eventType = event.type; + } + if (!eventType) { + return; + } + eventType = eventType.toLowerCase(); + if (!event.target && (/^(copy|aftercopy|_click)$/.test(eventType) || eventType === "error" && event.name === "clipboard-error")) { + event.target = _copyTarget; + } + _extend(event, { + type: eventType, + target: event.target || _currentElement || null, + relatedTarget: event.relatedTarget || null, + currentTarget: _flashState && _flashState.bridge || null, + timeStamp: event.timeStamp || _now() || null + }); + var msg = _eventMessages[event.type]; + if (event.type === "error" && event.name && msg) { + msg = msg[event.name]; + } + if (msg) { + event.message = msg; + } + if (event.type === "ready") { + _extend(event, { + target: null, + version: _flashState.version + }); + } + if (event.type === "error") { + if (_flashStateErrorNameMatchingRegex.test(event.name)) { + _extend(event, { + target: null, + minimumVersion: _minimumFlashVersion + }); + } + if (_flashStateEnabledErrorNameMatchingRegex.test(event.name)) { + _extend(event, { + version: _flashState.version + }); + } + } + if (event.type === "copy") { + event.clipboardData = { + setData: ZeroClipboard.setData, + clearData: ZeroClipboard.clearData + }; + } + if (event.type === "aftercopy") { + event = _mapClipResultsFromFlash(event, _clipDataFormatMap); + } + if (event.target && !event.relatedTarget) { + event.relatedTarget = _getRelatedTarget(event.target); + } + return _addMouseData(event); + }; + /** + * Get a relatedTarget from the target's `data-clipboard-target` attribute + * @private + */ + var _getRelatedTarget = function(targetEl) { + var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute("data-clipboard-target"); + return relatedTargetId ? _document.getElementById(relatedTargetId) : null; + }; + /** + * Add element and position data to `MouseEvent` instances + * @private + */ + var _addMouseData = function(event) { + if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) { + var srcElement = event.target; + var fromElement = event.type === "_mouseover" && event.relatedTarget ? event.relatedTarget : undefined; + var toElement = event.type === "_mouseout" && event.relatedTarget ? event.relatedTarget : undefined; + var pos = _getElementPosition(srcElement); + var screenLeft = _window.screenLeft || _window.screenX || 0; + var screenTop = _window.screenTop || _window.screenY || 0; + var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft; + var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop; + var pageX = pos.left + (typeof event._stageX === "number" ? event._stageX : 0); + var pageY = pos.top + (typeof event._stageY === "number" ? event._stageY : 0); + var clientX = pageX - scrollLeft; + var clientY = pageY - scrollTop; + var screenX = screenLeft + clientX; + var screenY = screenTop + clientY; + var moveX = typeof event.movementX === "number" ? event.movementX : 0; + var moveY = typeof event.movementY === "number" ? event.movementY : 0; + delete event._stageX; + delete event._stageY; + _extend(event, { + srcElement: srcElement, + fromElement: fromElement, + toElement: toElement, + screenX: screenX, + screenY: screenY, + pageX: pageX, + pageY: pageY, + clientX: clientX, + clientY: clientY, + x: clientX, + y: clientY, + movementX: moveX, + movementY: moveY, + offsetX: 0, + offsetY: 0, + layerX: 0, + layerY: 0 + }); + } + return event; + }; + /** + * Determine if an event's registered handlers should be execute synchronously or asynchronously. + * + * @returns {boolean} + * @private + */ + var _shouldPerformAsync = function(event) { + var eventType = event && typeof event.type === "string" && event.type || ""; + return !/^(?:(?:before)?copy|destroy)$/.test(eventType); + }; + /** + * Control if a callback should be executed asynchronously or not. + * + * @returns `undefined` + * @private + */ + var _dispatchCallback = function(func, context, args, async) { + if (async) { + _setTimeout(function() { + func.apply(context, args); + }, 0); + } else { + func.apply(context, args); + } + }; + /** + * Handle the actual dispatching of events to client instances. + * + * @returns `undefined` + * @private + */ + var _dispatchCallbacks = function(event) { + if (!(typeof event === "object" && event && event.type)) { + return; + } + var async = _shouldPerformAsync(event); + var wildcardTypeHandlers = _handlers["*"] || []; + var specificTypeHandlers = _handlers[event.type] || []; + var handlers = wildcardTypeHandlers.concat(specificTypeHandlers); + if (handlers && handlers.length) { + var i, len, func, context, eventCopy, originalContext = this; + for (i = 0, len = handlers.length; i < len; i++) { + func = handlers[i]; + context = originalContext; + if (typeof func === "string" && typeof _window[func] === "function") { + func = _window[func]; + } + if (typeof func === "object" && func && typeof func.handleEvent === "function") { + context = func; + func = func.handleEvent; + } + if (typeof func === "function") { + eventCopy = _extend({}, event); + _dispatchCallback(func, context, [ eventCopy ], async); + } + } + } + return this; + }; + /** + * Check an `error` event's `name` property to see if Flash has + * already loaded, which rules out possible `iframe` sandboxing. + * @private + */ + var _getSandboxStatusFromErrorEvent = function(event) { + var isSandboxed = null; + if (_pageIsFramed === false || event && event.type === "error" && event.name && _errorsThatOnlyOccurAfterFlashLoads.indexOf(event.name) !== -1) { + isSandboxed = false; + } + return isSandboxed; + }; + /** + * Preprocess any special behaviors, reactions, or state changes after receiving this event. + * Executes only once per event emitted, NOT once per client. + * @private + */ + var _preprocessEvent = function(event) { + var element = event.target || _currentElement || null; + var sourceIsSwf = event._source === "swf"; + delete event._source; + switch (event.type) { + case "error": + var isSandboxed = event.name === "flash-sandboxed" || _getSandboxStatusFromErrorEvent(event); + if (typeof isSandboxed === "boolean") { + _flashState.sandboxed = isSandboxed; + } + if (_flashStateErrorNames.indexOf(event.name) !== -1) { + _extend(_flashState, { + disabled: event.name === "flash-disabled", + outdated: event.name === "flash-outdated", + unavailable: event.name === "flash-unavailable", + degraded: event.name === "flash-degraded", + deactivated: event.name === "flash-deactivated", + overdue: event.name === "flash-overdue", + ready: false + }); + } else if (event.name === "version-mismatch") { + _zcSwfVersion = event.swfVersion; + _extend(_flashState, { + disabled: false, + outdated: false, + unavailable: false, + degraded: false, + deactivated: false, + overdue: false, + ready: false + }); + } + _clearTimeoutsAndPolling(); + break; + + case "ready": + _zcSwfVersion = event.swfVersion; + var wasDeactivated = _flashState.deactivated === true; + _extend(_flashState, { + disabled: false, + outdated: false, + sandboxed: false, + unavailable: false, + degraded: false, + deactivated: false, + overdue: wasDeactivated, + ready: !wasDeactivated + }); + _clearTimeoutsAndPolling(); + break; + + case "beforecopy": + _copyTarget = element; + break; + + case "copy": + var textContent, htmlContent, targetEl = event.relatedTarget; + if (!(_clipData["text/html"] || _clipData["text/plain"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) { + event.clipboardData.clearData(); + event.clipboardData.setData("text/plain", textContent); + if (htmlContent !== textContent) { + event.clipboardData.setData("text/html", htmlContent); + } + } else if (!_clipData["text/plain"] && event.target && (textContent = event.target.getAttribute("data-clipboard-text"))) { + event.clipboardData.clearData(); + event.clipboardData.setData("text/plain", textContent); + } + break; + + case "aftercopy": + _queueEmitClipboardErrors(event); + ZeroClipboard.clearData(); + if (element && element !== _safeActiveElement() && element.focus) { + element.focus(); + } + break; + + case "_mouseover": + ZeroClipboard.focus(element); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) { + _fireMouseEvent(_extend({}, event, { + type: "mouseenter", + bubbles: false, + cancelable: false + })); + } + _fireMouseEvent(_extend({}, event, { + type: "mouseover" + })); + } + break; + + case "_mouseout": + ZeroClipboard.blur(); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) { + _fireMouseEvent(_extend({}, event, { + type: "mouseleave", + bubbles: false, + cancelable: false + })); + } + _fireMouseEvent(_extend({}, event, { + type: "mouseout" + })); + } + break; + + case "_mousedown": + _addClass(element, _globalConfig.activeClass); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + + case "_mouseup": + _removeClass(element, _globalConfig.activeClass); + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + + case "_click": + _copyTarget = null; + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + + case "_mousemove": + if (_globalConfig.bubbleEvents === true && sourceIsSwf) { + _fireMouseEvent(_extend({}, event, { + type: event.type.slice(1) + })); + } + break; + } + if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) { + return true; + } + }; + /** + * Check an "aftercopy" event for clipboard errors and emit a corresponding "error" event. + * @private + */ + var _queueEmitClipboardErrors = function(aftercopyEvent) { + if (aftercopyEvent.errors && aftercopyEvent.errors.length > 0) { + var errorEvent = _deepCopy(aftercopyEvent); + _extend(errorEvent, { + type: "error", + name: "clipboard-error" + }); + delete errorEvent.success; + _setTimeout(function() { + ZeroClipboard.emit(errorEvent); + }, 0); + } + }; + /** + * Dispatch a synthetic MouseEvent. + * + * @returns `undefined` + * @private + */ + var _fireMouseEvent = function(event) { + if (!(event && typeof event.type === "string" && event)) { + return; + } + var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = { + view: doc.defaultView || _window, + canBubble: true, + cancelable: true, + detail: event.type === "click" ? 1 : 0, + button: typeof event.which === "number" ? event.which - 1 : typeof event.button === "number" ? event.button : doc.createEvent ? 0 : 1 + }, args = _extend(defaults, event); + if (!target) { + return; + } + if (doc.createEvent && target.dispatchEvent) { + args = [ args.type, args.canBubble, args.cancelable, args.view, args.detail, args.screenX, args.screenY, args.clientX, args.clientY, args.ctrlKey, args.altKey, args.shiftKey, args.metaKey, args.button, args.relatedTarget ]; + e = doc.createEvent("MouseEvents"); + if (e.initMouseEvent) { + e.initMouseEvent.apply(e, args); + e._source = "js"; + target.dispatchEvent(e); + } + } + }; + /** + * Continuously poll the DOM until either: + * (a) the fallback content becomes visible, or + * (b) we receive an event from SWF (handled elsewhere) + * + * IMPORTANT: + * This is NOT a necessary check but it can result in significantly faster + * detection of bad `swfPath` configuration and/or network/server issues [in + * supported browsers] than waiting for the entire `flashLoadTimeout` duration + * to elapse before detecting that the SWF cannot be loaded. The detection + * duration can be anywhere from 10-30 times faster [in supported browsers] by + * using this approach. + * + * @returns `undefined` + * @private + */ + var _watchForSwfFallbackContent = function() { + var maxWait = _globalConfig.flashLoadTimeout; + if (typeof maxWait === "number" && maxWait >= 0) { + var pollWait = Math.min(1e3, maxWait / 10); + var fallbackContentId = _globalConfig.swfObjectId + "_fallbackContent"; + _swfFallbackCheckInterval = _setInterval(function() { + var el = _document.getElementById(fallbackContentId); + if (_isElementVisible(el)) { + _clearTimeoutsAndPolling(); + _flashState.deactivated = null; + ZeroClipboard.emit({ + type: "error", + name: "swf-not-found" + }); + } + }, pollWait); + } + }; + /** + * Create the HTML bridge element to embed the Flash object into. + * @private + */ + var _createHtmlBridge = function() { + var container = _document.createElement("div"); + container.id = _globalConfig.containerId; + container.className = _globalConfig.containerClass; + container.style.position = "absolute"; + container.style.left = "0px"; + container.style.top = "-9999px"; + container.style.width = "1px"; + container.style.height = "1px"; + container.style.zIndex = "" + _getSafeZIndex(_globalConfig.zIndex); + return container; + }; + /** + * Get the HTML element container that wraps the Flash bridge object/element. + * @private + */ + var _getHtmlBridge = function(flashBridge) { + var htmlBridge = flashBridge && flashBridge.parentNode; + while (htmlBridge && htmlBridge.nodeName === "OBJECT" && htmlBridge.parentNode) { + htmlBridge = htmlBridge.parentNode; + } + return htmlBridge || null; + }; + /** + * Create the SWF object. + * + * @returns The SWF object reference. + * @private + */ + var _embedSwf = function() { + var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge); + if (!flashBridge) { + var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig); + var allowNetworking = allowScriptAccess === "never" ? "none" : "all"; + var flashvars = _vars(_extend({ + jsVersion: ZeroClipboard.version + }, _globalConfig)); + var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig); + container = _createHtmlBridge(); + var divToBeReplaced = _document.createElement("div"); + container.appendChild(divToBeReplaced); + _document.body.appendChild(container); + var tmpDiv = _document.createElement("div"); + var usingActiveX = _flashState.pluginType === "activex"; + tmpDiv.innerHTML = '" + (usingActiveX ? '' : "") + '' + '' + '' + '' + '' + '
     
    ' + "
    "; + flashBridge = tmpDiv.firstChild; + tmpDiv = null; + _unwrap(flashBridge).ZeroClipboard = ZeroClipboard; + container.replaceChild(flashBridge, divToBeReplaced); + _watchForSwfFallbackContent(); + } + if (!flashBridge) { + flashBridge = _document[_globalConfig.swfObjectId]; + if (flashBridge && (len = flashBridge.length)) { + flashBridge = flashBridge[len - 1]; + } + if (!flashBridge && container) { + flashBridge = container.firstChild; + } + } + _flashState.bridge = flashBridge || null; + return flashBridge; + }; + /** + * Destroy the SWF object. + * @private + */ + var _unembedSwf = function() { + var flashBridge = _flashState.bridge; + if (flashBridge) { + var htmlBridge = _getHtmlBridge(flashBridge); + if (htmlBridge) { + if (_flashState.pluginType === "activex" && "readyState" in flashBridge) { + flashBridge.style.display = "none"; + (function removeSwfFromIE() { + if (flashBridge.readyState === 4) { + for (var prop in flashBridge) { + if (typeof flashBridge[prop] === "function") { + flashBridge[prop] = null; + } + } + if (flashBridge.parentNode) { + flashBridge.parentNode.removeChild(flashBridge); + } + if (htmlBridge.parentNode) { + htmlBridge.parentNode.removeChild(htmlBridge); + } + } else { + _setTimeout(removeSwfFromIE, 10); + } + })(); + } else { + if (flashBridge.parentNode) { + flashBridge.parentNode.removeChild(flashBridge); + } + if (htmlBridge.parentNode) { + htmlBridge.parentNode.removeChild(htmlBridge); + } + } + } + _clearTimeoutsAndPolling(); + _flashState.ready = null; + _flashState.bridge = null; + _flashState.deactivated = null; + _zcSwfVersion = undefined; + } + }; + /** + * Map the data format names of the "clipData" to Flash-friendly names. + * + * @returns A new transformed object. + * @private + */ + var _mapClipDataToFlash = function(clipData) { + var newClipData = {}, formatMap = {}; + if (!(typeof clipData === "object" && clipData)) { + return; + } + for (var dataFormat in clipData) { + if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === "string" && clipData[dataFormat]) { + switch (dataFormat.toLowerCase()) { + case "text/plain": + case "text": + case "air:text": + case "flash:text": + newClipData.text = clipData[dataFormat]; + formatMap.text = dataFormat; + break; + + case "text/html": + case "html": + case "air:html": + case "flash:html": + newClipData.html = clipData[dataFormat]; + formatMap.html = dataFormat; + break; + + case "application/rtf": + case "text/rtf": + case "rtf": + case "richtext": + case "air:rtf": + case "flash:rtf": + newClipData.rtf = clipData[dataFormat]; + formatMap.rtf = dataFormat; + break; + + default: + break; + } + } + } + return { + data: newClipData, + formatMap: formatMap + }; + }; + /** + * Map the data format names from Flash-friendly names back to their original "clipData" names (via a format mapping). + * + * @returns A new transformed object. + * @private + */ + var _mapClipResultsFromFlash = function(clipResults, formatMap) { + if (!(typeof clipResults === "object" && clipResults && typeof formatMap === "object" && formatMap)) { + return clipResults; + } + var newResults = {}; + for (var prop in clipResults) { + if (_hasOwn.call(clipResults, prop)) { + if (prop === "errors") { + newResults[prop] = clipResults[prop] ? clipResults[prop].slice() : []; + for (var i = 0, len = newResults[prop].length; i < len; i++) { + newResults[prop][i].format = formatMap[newResults[prop][i].format]; + } + } else if (prop !== "success" && prop !== "data") { + newResults[prop] = clipResults[prop]; + } else { + newResults[prop] = {}; + var tmpHash = clipResults[prop]; + for (var dataFormat in tmpHash) { + if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) { + newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat]; + } + } + } + } + } + return newResults; + }; + /** + * Will look at a path, and will create a "?noCache={time}" or "&noCache={time}" + * query param string to return. Does NOT append that string to the original path. + * This is useful because ExternalInterface often breaks when a Flash SWF is cached. + * + * @returns The `noCache` query param with necessary "?"/"&" prefix. + * @private + */ + var _cacheBust = function(path, options) { + var cacheBust = options == null || options && options.cacheBust === true; + if (cacheBust) { + return (path.indexOf("?") === -1 ? "?" : "&") + "noCache=" + _now(); + } else { + return ""; + } + }; + /** + * Creates a query string for the FlashVars param. + * Does NOT include the cache-busting query param. + * + * @returns FlashVars query string + * @private + */ + var _vars = function(options) { + var i, len, domain, domains, str = "", trustedOriginsExpanded = []; + if (options.trustedDomains) { + if (typeof options.trustedDomains === "string") { + domains = [ options.trustedDomains ]; + } else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) { + domains = options.trustedDomains; + } + } + if (domains && domains.length) { + for (i = 0, len = domains.length; i < len; i++) { + if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === "string") { + domain = _extractDomain(domains[i]); + if (!domain) { + continue; + } + if (domain === "*") { + trustedOriginsExpanded.length = 0; + trustedOriginsExpanded.push(domain); + break; + } + trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, "//" + domain, _window.location.protocol + "//" + domain ]); + } + } + } + if (trustedOriginsExpanded.length) { + str += "trustedOrigins=" + _encodeURIComponent(trustedOriginsExpanded.join(",")); + } + if (options.forceEnhancedClipboard === true) { + str += (str ? "&" : "") + "forceEnhancedClipboard=true"; + } + if (typeof options.swfObjectId === "string" && options.swfObjectId) { + str += (str ? "&" : "") + "swfObjectId=" + _encodeURIComponent(options.swfObjectId); + } + if (typeof options.jsVersion === "string" && options.jsVersion) { + str += (str ? "&" : "") + "jsVersion=" + _encodeURIComponent(options.jsVersion); + } + return str; + }; + /** + * Extract the domain (e.g. "github.com") from an origin (e.g. "https://github.com") or + * URL (e.g. "https://github.com/zeroclipboard/zeroclipboard/"). + * + * @returns the domain + * @private + */ + var _extractDomain = function(originOrUrl) { + if (originOrUrl == null || originOrUrl === "") { + return null; + } + originOrUrl = originOrUrl.replace(/^\s+|\s+$/g, ""); + if (originOrUrl === "") { + return null; + } + var protocolIndex = originOrUrl.indexOf("//"); + originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2); + var pathIndex = originOrUrl.indexOf("/"); + originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex); + if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === ".swf") { + return null; + } + return originOrUrl || null; + }; + /** + * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`. + * + * @returns The appropriate script access level. + * @private + */ + var _determineScriptAccess = function() { + var _extractAllDomains = function(origins) { + var i, len, tmp, resultsArray = []; + if (typeof origins === "string") { + origins = [ origins ]; + } + if (!(typeof origins === "object" && origins && typeof origins.length === "number")) { + return resultsArray; + } + for (i = 0, len = origins.length; i < len; i++) { + if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) { + if (tmp === "*") { + resultsArray.length = 0; + resultsArray.push("*"); + break; + } + if (resultsArray.indexOf(tmp) === -1) { + resultsArray.push(tmp); + } + } + } + return resultsArray; + }; + return function(currentDomain, configOptions) { + var swfDomain = _extractDomain(configOptions.swfPath); + if (swfDomain === null) { + swfDomain = currentDomain; + } + var trustedDomains = _extractAllDomains(configOptions.trustedDomains); + var len = trustedDomains.length; + if (len > 0) { + if (len === 1 && trustedDomains[0] === "*") { + return "always"; + } + if (trustedDomains.indexOf(currentDomain) !== -1) { + if (len === 1 && currentDomain === swfDomain) { + return "sameDomain"; + } + return "always"; + } + } + return "never"; + }; + }(); + /** + * Get the currently active/focused DOM element. + * + * @returns the currently active/focused element, or `null` + * @private + */ + var _safeActiveElement = function() { + try { + return _document.activeElement; + } catch (err) { + return null; + } + }; + /** + * Add a class to an element, if it doesn't already have it. + * + * @returns The element, with its new class added. + * @private + */ + var _addClass = function(element, value) { + var c, cl, className, classNames = []; + if (typeof value === "string" && value) { + classNames = value.split(/\s+/); + } + if (element && element.nodeType === 1 && classNames.length > 0) { + className = (" " + (element.className || "") + " ").replace(/[\t\r\n\f]/g, " "); + for (c = 0, cl = classNames.length; c < cl; c++) { + if (className.indexOf(" " + classNames[c] + " ") === -1) { + className += classNames[c] + " "; + } + } + className = className.replace(/^\s+|\s+$/g, ""); + if (className !== element.className) { + element.className = className; + } + } + return element; + }; + /** + * Remove a class from an element, if it has it. + * + * @returns The element, with its class removed. + * @private + */ + var _removeClass = function(element, value) { + var c, cl, className, classNames = []; + if (typeof value === "string" && value) { + classNames = value.split(/\s+/); + } + if (element && element.nodeType === 1 && classNames.length > 0) { + if (element.className) { + className = (" " + element.className + " ").replace(/[\t\r\n\f]/g, " "); + for (c = 0, cl = classNames.length; c < cl; c++) { + className = className.replace(" " + classNames[c] + " ", " "); + } + className = className.replace(/^\s+|\s+$/g, ""); + if (className !== element.className) { + element.className = className; + } + } + } + return element; + }; + /** + * Attempt to interpret the element's CSS styling. If `prop` is `"cursor"`, + * then we assume that it should be a hand ("pointer") cursor if the element + * is an anchor element ("a" tag). + * + * @returns The computed style property. + * @private + */ + var _getStyle = function(el, prop) { + var value = _getComputedStyle(el, null).getPropertyValue(prop); + if (prop === "cursor") { + if (!value || value === "auto") { + if (el.nodeName === "A") { + return "pointer"; + } + } + } + return value; + }; + /** + * Get the absolutely positioned coordinates of a DOM element. + * + * @returns Object containing the element's position, width, and height. + * @private + */ + var _getElementPosition = function(el) { + var pos = { + left: 0, + top: 0, + width: 0, + height: 0 + }; + if (el.getBoundingClientRect) { + var elRect = el.getBoundingClientRect(); + var pageXOffset = _window.pageXOffset; + var pageYOffset = _window.pageYOffset; + var leftBorderWidth = _document.documentElement.clientLeft || 0; + var topBorderWidth = _document.documentElement.clientTop || 0; + var leftBodyOffset = 0; + var topBodyOffset = 0; + if (_getStyle(_document.body, "position") === "relative") { + var bodyRect = _document.body.getBoundingClientRect(); + var htmlRect = _document.documentElement.getBoundingClientRect(); + leftBodyOffset = bodyRect.left - htmlRect.left || 0; + topBodyOffset = bodyRect.top - htmlRect.top || 0; + } + pos.left = elRect.left + pageXOffset - leftBorderWidth - leftBodyOffset; + pos.top = elRect.top + pageYOffset - topBorderWidth - topBodyOffset; + pos.width = "width" in elRect ? elRect.width : elRect.right - elRect.left; + pos.height = "height" in elRect ? elRect.height : elRect.bottom - elRect.top; + } + return pos; + }; + /** + * Determine is an element is visible somewhere within the document (page). + * + * @returns Boolean + * @private + */ + var _isElementVisible = function(el) { + if (!el) { + return false; + } + var styles = _getComputedStyle(el, null); + if (!styles) { + return false; + } + var hasCssHeight = _parseFloat(styles.height) > 0; + var hasCssWidth = _parseFloat(styles.width) > 0; + var hasCssTop = _parseFloat(styles.top) >= 0; + var hasCssLeft = _parseFloat(styles.left) >= 0; + var cssKnows = hasCssHeight && hasCssWidth && hasCssTop && hasCssLeft; + var rect = cssKnows ? null : _getElementPosition(el); + var isVisible = styles.display !== "none" && styles.visibility !== "collapse" && (cssKnows || !!rect && (hasCssHeight || rect.height > 0) && (hasCssWidth || rect.width > 0) && (hasCssTop || rect.top >= 0) && (hasCssLeft || rect.left >= 0)); + return isVisible; + }; + /** + * Clear all existing timeouts and interval polling delegates. + * + * @returns `undefined` + * @private + */ + var _clearTimeoutsAndPolling = function() { + _clearTimeout(_flashCheckTimeout); + _flashCheckTimeout = 0; + _clearInterval(_swfFallbackCheckInterval); + _swfFallbackCheckInterval = 0; + }; + /** + * Reposition the Flash object to cover the currently activated element. + * + * @returns `undefined` + * @private + */ + var _reposition = function() { + var htmlBridge; + if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) { + var pos = _getElementPosition(_currentElement); + _extend(htmlBridge.style, { + width: pos.width + "px", + height: pos.height + "px", + top: pos.top + "px", + left: pos.left + "px", + zIndex: "" + _getSafeZIndex(_globalConfig.zIndex) + }); + } + }; + /** + * Sends a signal to the Flash object to display the hand cursor if `true`. + * + * @returns `undefined` + * @private + */ + var _setHandCursor = function(enabled) { + if (_flashState.ready === true) { + if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === "function") { + _flashState.bridge.setHandCursor(enabled); + } else { + _flashState.ready = false; + } + } + }; + /** + * Get a safe value for `zIndex` + * + * @returns an integer, or "auto" + * @private + */ + var _getSafeZIndex = function(val) { + if (/^(?:auto|inherit)$/.test(val)) { + return val; + } + var zIndex; + if (typeof val === "number" && !_isNaN(val)) { + zIndex = val; + } else if (typeof val === "string") { + zIndex = _getSafeZIndex(_parseInt(val, 10)); + } + return typeof zIndex === "number" ? zIndex : "auto"; + }; + /** + * Ensure OS-compliant line endings, i.e. "\r\n" on Windows, "\n" elsewhere + * + * @returns string + * @private + */ + var _fixLineEndings = function(content) { + var replaceRegex = /(\r\n|\r|\n)/g; + if (typeof content === "string" && _globalConfig.fixLineEndings === true) { + if (_isWindows()) { + if (/((^|[^\r])\n|\r([^\n]|$))/.test(content)) { + content = content.replace(replaceRegex, "\r\n"); + } + } else if (/\r/.test(content)) { + content = content.replace(replaceRegex, "\n"); + } + } + return content; + }; + /** + * Attempt to detect if ZeroClipboard is executing inside of a sandboxed iframe. + * If it is, Flash Player cannot be used, so ZeroClipboard is dead in the water. + * + * @see {@link http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Dec/0002.html} + * @see {@link https://github.com/zeroclipboard/zeroclipboard/issues/511} + * @see {@link http://zeroclipboard.org/test-iframes.html} + * + * @returns `true` (is sandboxed), `false` (is not sandboxed), or `null` (uncertain) + * @private + */ + var _detectSandbox = function(doNotReassessFlashSupport) { + var effectiveScriptOrigin, frame, frameError, previousState = _flashState.sandboxed, isSandboxed = null; + doNotReassessFlashSupport = doNotReassessFlashSupport === true; + if (_pageIsFramed === false) { + isSandboxed = false; + } else { + try { + frame = window.frameElement || null; + } catch (e) { + frameError = { + name: e.name, + message: e.message + }; + } + if (frame && frame.nodeType === 1 && frame.nodeName === "IFRAME") { + try { + isSandboxed = frame.hasAttribute("sandbox"); + } catch (e) { + isSandboxed = null; + } + } else { + try { + effectiveScriptOrigin = document.domain || null; + } catch (e) { + effectiveScriptOrigin = null; + } + if (effectiveScriptOrigin === null || frameError && frameError.name === "SecurityError" && /(^|[\s\(\[@])sandbox(es|ed|ing|[\s\.,!\)\]@]|$)/.test(frameError.message.toLowerCase())) { + isSandboxed = true; + } + } + } + _flashState.sandboxed = isSandboxed; + if (previousState !== isSandboxed && !doNotReassessFlashSupport) { + _detectFlashSupport(_ActiveXObject); + } + return isSandboxed; + }; + /** + * Detect the Flash Player status, version, and plugin type. + * + * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code} + * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript} + * + * @returns `undefined` + * @private + */ + var _detectFlashSupport = function(ActiveXObject) { + var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = ""; + /** + * Derived from Apple's suggested sniffer. + * @param {String} desc e.g. "Shockwave Flash 7.0 r61" + * @returns {String} "7.0.61" + * @private + */ + function parseFlashVersion(desc) { + var matches = desc.match(/[\d]+/g); + matches.length = 3; + return matches.join("."); + } + function isPepperFlash(flashPlayerFileName) { + return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === "chrome.plugin"); + } + function inspectPlugin(plugin) { + if (plugin) { + hasFlash = true; + if (plugin.version) { + flashVersion = parseFlashVersion(plugin.version); + } + if (!flashVersion && plugin.description) { + flashVersion = parseFlashVersion(plugin.description); + } + if (plugin.filename) { + isPPAPI = isPepperFlash(plugin.filename); + } + } + } + if (_navigator.plugins && _navigator.plugins.length) { + plugin = _navigator.plugins["Shockwave Flash"]; + inspectPlugin(plugin); + if (_navigator.plugins["Shockwave Flash 2.0"]) { + hasFlash = true; + flashVersion = "2.0.0.11"; + } + } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) { + mimeType = _navigator.mimeTypes["application/x-shockwave-flash"]; + plugin = mimeType && mimeType.enabledPlugin; + inspectPlugin(plugin); + } else if (typeof ActiveXObject !== "undefined") { + isActiveX = true; + try { + ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); + hasFlash = true; + flashVersion = parseFlashVersion(ax.GetVariable("$version")); + } catch (e1) { + try { + ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); + hasFlash = true; + flashVersion = "6.0.21"; + } catch (e2) { + try { + ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); + hasFlash = true; + flashVersion = parseFlashVersion(ax.GetVariable("$version")); + } catch (e3) { + isActiveX = false; + } + } + } + } + _flashState.disabled = hasFlash !== true; + _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion); + _flashState.version = flashVersion || "0.0.0"; + _flashState.pluginType = isPPAPI ? "pepper" : isActiveX ? "activex" : hasFlash ? "netscape" : "unknown"; + }; + /** + * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later. + */ + _detectFlashSupport(_ActiveXObject); + /** + * Always assess the `sandboxed` state of the page at important Flash-related moments. + */ + _detectSandbox(true); + /** + * A shell constructor for `ZeroClipboard` client instances. + * + * @constructor + */ + var ZeroClipboard = function() { + if (!(this instanceof ZeroClipboard)) { + return new ZeroClipboard(); + } + if (typeof ZeroClipboard._createClient === "function") { + ZeroClipboard._createClient.apply(this, _args(arguments)); + } + }; + /** + * The ZeroClipboard library's version number. + * + * @static + * @readonly + * @property {string} + */ + _defineProperty(ZeroClipboard, "version", { + value: "2.3.0-beta.1", + writable: false, + configurable: true, + enumerable: true + }); + /** + * Update or get a copy of the ZeroClipboard global configuration. + * Returns a copy of the current/updated configuration. + * + * @returns Object + * @static + */ + ZeroClipboard.config = function() { + return _config.apply(this, _args(arguments)); + }; + /** + * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard. + * + * @returns Object + * @static + */ + ZeroClipboard.state = function() { + return _state.apply(this, _args(arguments)); + }; + /** + * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc. + * + * @returns Boolean + * @static + */ + ZeroClipboard.isFlashUnusable = function() { + return _isFlashUnusable.apply(this, _args(arguments)); + }; + /** + * Register an event listener. + * + * @returns `ZeroClipboard` + * @static + */ + ZeroClipboard.on = function() { + return _on.apply(this, _args(arguments)); + }; + /** + * Unregister an event listener. + * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`. + * If no `eventType` is provided, it will unregister all listeners for every event type. + * + * @returns `ZeroClipboard` + * @static + */ + ZeroClipboard.off = function() { + return _off.apply(this, _args(arguments)); + }; + /** + * Retrieve event listeners for an `eventType`. + * If no `eventType` is provided, it will retrieve all listeners for every event type. + * + * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null` + */ + ZeroClipboard.handlers = function() { + return _listeners.apply(this, _args(arguments)); + }; + /** + * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners. + * + * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`. + * @static + */ + ZeroClipboard.emit = function() { + return _emit.apply(this, _args(arguments)); + }; + /** + * Create and embed the Flash object. + * + * @returns The Flash object + * @static + */ + ZeroClipboard.create = function() { + return _create.apply(this, _args(arguments)); + }; + /** + * Self-destruct and clean up everything, including the embedded Flash object. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.destroy = function() { + return _destroy.apply(this, _args(arguments)); + }; + /** + * Set the pending data for clipboard injection. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.setData = function() { + return _setData.apply(this, _args(arguments)); + }; + /** + * Clear the pending data for clipboard injection. + * If no `format` is provided, all pending data formats will be cleared. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.clearData = function() { + return _clearData.apply(this, _args(arguments)); + }; + /** + * Get a copy of the pending data for clipboard injection. + * If no `format` is provided, a copy of ALL pending data formats will be returned. + * + * @returns `String` or `Object` + * @static + */ + ZeroClipboard.getData = function() { + return _getData.apply(this, _args(arguments)); + }; + /** + * Sets the current HTML object that the Flash object should overlay. This will put the global + * Flash object on top of the current element; depending on the setup, this may also set the + * pending clipboard text data as well as the Flash object's wrapping element's title attribute + * based on the underlying HTML element and ZeroClipboard configuration. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.focus = ZeroClipboard.activate = function() { + return _focus.apply(this, _args(arguments)); + }; + /** + * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on + * the setup, this may also unset the Flash object's wrapping element's title attribute based on + * the underlying HTML element and ZeroClipboard configuration. + * + * @returns `undefined` + * @static + */ + ZeroClipboard.blur = ZeroClipboard.deactivate = function() { + return _blur.apply(this, _args(arguments)); + }; + /** + * Returns the currently focused/"activated" HTML element that the Flash object is wrapping. + * + * @returns `HTMLElement` or `null` + * @static + */ + ZeroClipboard.activeElement = function() { + return _activeElement.apply(this, _args(arguments)); + }; + /** + * Keep track of the ZeroClipboard client instance counter. + */ + var _clientIdCounter = 0; + /** + * Keep track of the state of the client instances. + * + * Entry structure: + * _clientMeta[client.id] = { + * instance: client, + * elements: [], + * handlers: {} + * }; + */ + var _clientMeta = {}; + /** + * Keep track of the ZeroClipboard clipped elements counter. + */ + var _elementIdCounter = 0; + /** + * Keep track of the state of the clipped element relationships to clients. + * + * Entry structure: + * _elementMeta[element.zcClippingId] = [client1.id, client2.id]; + */ + var _elementMeta = {}; + /** + * Keep track of the state of the mouse event handlers for clipped elements. + * + * Entry structure: + * _mouseHandlers[element.zcClippingId] = { + * mouseover: function(event) {}, + * mouseout: function(event) {}, + * mouseenter: function(event) {}, + * mouseleave: function(event) {}, + * mousemove: function(event) {} + * }; + */ + var _mouseHandlers = {}; + /** + * Extending the ZeroClipboard configuration defaults for the Client module. + */ + _extend(_globalConfig, { + autoActivate: true + }); + /** + * The real constructor for `ZeroClipboard` client instances. + * @private + */ + var _clientConstructor = function(elements) { + var client = this; + client.id = "" + _clientIdCounter++; + _clientMeta[client.id] = { + instance: client, + elements: [], + handlers: {} + }; + if (elements) { + client.clip(elements); + } + ZeroClipboard.on("*", function(event) { + return client.emit(event); + }); + ZeroClipboard.on("destroy", function() { + client.destroy(); + }); + ZeroClipboard.create(); + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.on`. + * @private + */ + var _clientOn = function(eventType, listener) { + var i, len, events, added = {}, meta = _clientMeta[this.id], handlers = meta && meta.handlers; + if (!meta) { + throw new Error("Attempted to add new listener(s) to a destroyed ZeroClipboard client instance"); + } + if (typeof eventType === "string" && eventType) { + events = eventType.toLowerCase().split(/\s+/); + } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") { + for (i in eventType) { + if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") { + this.on(i, eventType[i]); + } + } + } + if (events && events.length) { + for (i = 0, len = events.length; i < len; i++) { + eventType = events[i].replace(/^on/, ""); + added[eventType] = true; + if (!handlers[eventType]) { + handlers[eventType] = []; + } + handlers[eventType].push(listener); + } + if (added.ready && _flashState.ready) { + this.emit({ + type: "ready", + client: this + }); + } + if (added.error) { + for (i = 0, len = _flashStateErrorNames.length; i < len; i++) { + if (_flashState[_flashStateErrorNames[i].replace(/^flash-/, "")]) { + this.emit({ + type: "error", + name: _flashStateErrorNames[i], + client: this + }); + break; + } + } + if (_zcSwfVersion !== undefined && ZeroClipboard.version !== _zcSwfVersion) { + this.emit({ + type: "error", + name: "version-mismatch", + jsVersion: ZeroClipboard.version, + swfVersion: _zcSwfVersion + }); + } + } + } + return this; + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.off`. + * @private + */ + var _clientOff = function(eventType, listener) { + var i, len, foundIndex, events, perEventHandlers, meta = _clientMeta[this.id], handlers = meta && meta.handlers; + if (!handlers) { + return this; + } + if (arguments.length === 0) { + events = _keys(handlers); + } else if (typeof eventType === "string" && eventType) { + events = eventType.split(/\s+/); + } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") { + for (i in eventType) { + if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") { + this.off(i, eventType[i]); + } + } + } + if (events && events.length) { + for (i = 0, len = events.length; i < len; i++) { + eventType = events[i].toLowerCase().replace(/^on/, ""); + perEventHandlers = handlers[eventType]; + if (perEventHandlers && perEventHandlers.length) { + if (listener) { + foundIndex = perEventHandlers.indexOf(listener); + while (foundIndex !== -1) { + perEventHandlers.splice(foundIndex, 1); + foundIndex = perEventHandlers.indexOf(listener, foundIndex); + } + } else { + perEventHandlers.length = 0; + } + } + } + } + return this; + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.handlers`. + * @private + */ + var _clientListeners = function(eventType) { + var copy = null, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers; + if (handlers) { + if (typeof eventType === "string" && eventType) { + copy = handlers[eventType] ? handlers[eventType].slice(0) : []; + } else { + copy = _deepCopy(handlers); + } + } + return copy; + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.emit`. + * @private + */ + var _clientEmit = function(event) { + if (_clientShouldEmit.call(this, event)) { + if (typeof event === "object" && event && typeof event.type === "string" && event.type) { + event = _extend({}, event); + } + var eventCopy = _extend({}, _createEvent(event), { + client: this + }); + _clientDispatchCallbacks.call(this, eventCopy); + } + return this; + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.clip`. + * @private + */ + var _clientClip = function(elements) { + if (!_clientMeta[this.id]) { + throw new Error("Attempted to clip element(s) to a destroyed ZeroClipboard client instance"); + } + elements = _prepClip(elements); + for (var i = 0; i < elements.length; i++) { + if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) { + if (!elements[i].zcClippingId) { + elements[i].zcClippingId = "zcClippingId_" + _elementIdCounter++; + _elementMeta[elements[i].zcClippingId] = [ this.id ]; + if (_globalConfig.autoActivate === true) { + _addMouseHandlers(elements[i]); + } + } else if (_elementMeta[elements[i].zcClippingId].indexOf(this.id) === -1) { + _elementMeta[elements[i].zcClippingId].push(this.id); + } + var clippedElements = _clientMeta[this.id] && _clientMeta[this.id].elements; + if (clippedElements.indexOf(elements[i]) === -1) { + clippedElements.push(elements[i]); + } + } + } + return this; + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.unclip`. + * @private + */ + var _clientUnclip = function(elements) { + var meta = _clientMeta[this.id]; + if (!meta) { + return this; + } + var clippedElements = meta.elements; + var arrayIndex; + if (typeof elements === "undefined") { + elements = clippedElements.slice(0); + } else { + elements = _prepClip(elements); + } + for (var i = elements.length; i--; ) { + if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) { + arrayIndex = 0; + while ((arrayIndex = clippedElements.indexOf(elements[i], arrayIndex)) !== -1) { + clippedElements.splice(arrayIndex, 1); + } + var clientIds = _elementMeta[elements[i].zcClippingId]; + if (clientIds) { + arrayIndex = 0; + while ((arrayIndex = clientIds.indexOf(this.id, arrayIndex)) !== -1) { + clientIds.splice(arrayIndex, 1); + } + if (clientIds.length === 0) { + if (_globalConfig.autoActivate === true) { + _removeMouseHandlers(elements[i]); + } + delete elements[i].zcClippingId; + } + } + } + } + return this; + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.elements`. + * @private + */ + var _clientElements = function() { + var meta = _clientMeta[this.id]; + return meta && meta.elements ? meta.elements.slice(0) : []; + }; + /** + * The underlying implementation of `ZeroClipboard.Client.prototype.destroy`. + * @private + */ + var _clientDestroy = function() { + if (!_clientMeta[this.id]) { + return; + } + this.unclip(); + this.off(); + delete _clientMeta[this.id]; + }; + /** + * Inspect an Event to see if the Client (`this`) should honor it for emission. + * @private + */ + var _clientShouldEmit = function(event) { + if (!(event && event.type)) { + return false; + } + if (event.client && event.client !== this) { + return false; + } + var meta = _clientMeta[this.id]; + var clippedEls = meta && meta.elements; + var hasClippedEls = !!clippedEls && clippedEls.length > 0; + var goodTarget = !event.target || hasClippedEls && clippedEls.indexOf(event.target) !== -1; + var goodRelTarget = event.relatedTarget && hasClippedEls && clippedEls.indexOf(event.relatedTarget) !== -1; + var goodClient = event.client && event.client === this; + if (!meta || !(goodTarget || goodRelTarget || goodClient)) { + return false; + } + return true; + }; + /** + * Handle the actual dispatching of events to a client instance. + * + * @returns `undefined` + * @private + */ + var _clientDispatchCallbacks = function(event) { + var meta = _clientMeta[this.id]; + if (!(typeof event === "object" && event && event.type && meta)) { + return; + } + var async = _shouldPerformAsync(event); + var wildcardTypeHandlers = meta && meta.handlers["*"] || []; + var specificTypeHandlers = meta && meta.handlers[event.type] || []; + var handlers = wildcardTypeHandlers.concat(specificTypeHandlers); + if (handlers && handlers.length) { + var i, len, func, context, eventCopy, originalContext = this; + for (i = 0, len = handlers.length; i < len; i++) { + func = handlers[i]; + context = originalContext; + if (typeof func === "string" && typeof _window[func] === "function") { + func = _window[func]; + } + if (typeof func === "object" && func && typeof func.handleEvent === "function") { + context = func; + func = func.handleEvent; + } + if (typeof func === "function") { + eventCopy = _extend({}, event); + _dispatchCallback(func, context, [ eventCopy ], async); + } + } + } + }; + /** + * Prepares the elements for clipping/unclipping. + * + * @returns An Array of elements. + * @private + */ + var _prepClip = function(elements) { + if (typeof elements === "string") { + elements = []; + } + return typeof elements.length !== "number" ? [ elements ] : elements; + }; + /** + * Add a `mouseover` handler function for a clipped element. + * + * @returns `undefined` + * @private + */ + var _addMouseHandlers = function(element) { + if (!(element && element.nodeType === 1)) { + return; + } + var _suppressMouseEvents = function(event) { + if (!(event || (event = _window.event))) { + return; + } + if (event._source !== "js") { + event.stopImmediatePropagation(); + event.preventDefault(); + } + delete event._source; + }; + var _elementMouseOver = function(event) { + if (!(event || (event = _window.event))) { + return; + } + _suppressMouseEvents(event); + ZeroClipboard.focus(element); + }; + element.addEventListener("mouseover", _elementMouseOver, false); + element.addEventListener("mouseout", _suppressMouseEvents, false); + element.addEventListener("mouseenter", _suppressMouseEvents, false); + element.addEventListener("mouseleave", _suppressMouseEvents, false); + element.addEventListener("mousemove", _suppressMouseEvents, false); + _mouseHandlers[element.zcClippingId] = { + mouseover: _elementMouseOver, + mouseout: _suppressMouseEvents, + mouseenter: _suppressMouseEvents, + mouseleave: _suppressMouseEvents, + mousemove: _suppressMouseEvents + }; + }; + /** + * Remove a `mouseover` handler function for a clipped element. + * + * @returns `undefined` + * @private + */ + var _removeMouseHandlers = function(element) { + if (!(element && element.nodeType === 1)) { + return; + } + var mouseHandlers = _mouseHandlers[element.zcClippingId]; + if (!(typeof mouseHandlers === "object" && mouseHandlers)) { + return; + } + var key, val, mouseEvents = [ "move", "leave", "enter", "out", "over" ]; + for (var i = 0, len = mouseEvents.length; i < len; i++) { + key = "mouse" + mouseEvents[i]; + val = mouseHandlers[key]; + if (typeof val === "function") { + element.removeEventListener(key, val, false); + } + } + delete _mouseHandlers[element.zcClippingId]; + }; + /** + * Creates a new ZeroClipboard client instance. + * Optionally, auto-`clip` an element or collection of elements. + * + * @constructor + */ + ZeroClipboard._createClient = function() { + _clientConstructor.apply(this, _args(arguments)); + }; + /** + * Register an event listener to the client. + * + * @returns `this` + */ + ZeroClipboard.prototype.on = function() { + return _clientOn.apply(this, _args(arguments)); + }; + /** + * Unregister an event handler from the client. + * If no `listener` function/object is provided, it will unregister all handlers for the provided `eventType`. + * If no `eventType` is provided, it will unregister all handlers for every event type. + * + * @returns `this` + */ + ZeroClipboard.prototype.off = function() { + return _clientOff.apply(this, _args(arguments)); + }; + /** + * Retrieve event listeners for an `eventType` from the client. + * If no `eventType` is provided, it will retrieve all listeners for every event type. + * + * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null` + */ + ZeroClipboard.prototype.handlers = function() { + return _clientListeners.apply(this, _args(arguments)); + }; + /** + * Event emission receiver from the Flash object for this client's registered JavaScript event listeners. + * + * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`. + */ + ZeroClipboard.prototype.emit = function() { + return _clientEmit.apply(this, _args(arguments)); + }; + /** + * Register clipboard actions for new element(s) to the client. + * + * @returns `this` + */ + ZeroClipboard.prototype.clip = function() { + return _clientClip.apply(this, _args(arguments)); + }; + /** + * Unregister the clipboard actions of previously registered element(s) on the page. + * If no elements are provided, ALL registered elements will be unregistered. + * + * @returns `this` + */ + ZeroClipboard.prototype.unclip = function() { + return _clientUnclip.apply(this, _args(arguments)); + }; + /** + * Get all of the elements to which this client is clipped. + * + * @returns array of clipped elements + */ + ZeroClipboard.prototype.elements = function() { + return _clientElements.apply(this, _args(arguments)); + }; + /** + * Self-destruct and clean up everything for a single client. + * This will NOT destroy the embedded Flash object. + * + * @returns `undefined` + */ + ZeroClipboard.prototype.destroy = function() { + return _clientDestroy.apply(this, _args(arguments)); + }; + /** + * Stores the pending plain text to inject into the clipboard. + * + * @returns `this` + */ + ZeroClipboard.prototype.setText = function(text) { + if (!_clientMeta[this.id]) { + throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance"); + } + ZeroClipboard.setData("text/plain", text); + return this; + }; + /** + * Stores the pending HTML text to inject into the clipboard. + * + * @returns `this` + */ + ZeroClipboard.prototype.setHtml = function(html) { + if (!_clientMeta[this.id]) { + throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance"); + } + ZeroClipboard.setData("text/html", html); + return this; + }; + /** + * Stores the pending rich text (RTF) to inject into the clipboard. + * + * @returns `this` + */ + ZeroClipboard.prototype.setRichText = function(richText) { + if (!_clientMeta[this.id]) { + throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance"); + } + ZeroClipboard.setData("application/rtf", richText); + return this; + }; + /** + * Stores the pending data to inject into the clipboard. + * + * @returns `this` + */ + ZeroClipboard.prototype.setData = function() { + if (!_clientMeta[this.id]) { + throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance"); + } + ZeroClipboard.setData.apply(this, _args(arguments)); + return this; + }; + /** + * Clears the pending data to inject into the clipboard. + * If no `format` is provided, all pending data formats will be cleared. + * + * @returns `this` + */ + ZeroClipboard.prototype.clearData = function() { + if (!_clientMeta[this.id]) { + throw new Error("Attempted to clear pending clipboard data from a destroyed ZeroClipboard client instance"); + } + ZeroClipboard.clearData.apply(this, _args(arguments)); + return this; + }; + /** + * Gets a copy of the pending data to inject into the clipboard. + * If no `format` is provided, a copy of ALL pending data formats will be returned. + * + * @returns `String` or `Object` + */ + ZeroClipboard.prototype.getData = function() { + if (!_clientMeta[this.id]) { + throw new Error("Attempted to get pending clipboard data from a destroyed ZeroClipboard client instance"); + } + return ZeroClipboard.getData.apply(this, _args(arguments)); + }; + if (typeof define === "function" && define.amd) { + define(function() { + return ZeroClipboard; + }); + } else if (typeof module === "object" && module && typeof module.exports === "object" && module.exports) { + module.exports = ZeroClipboard; + } else { + window.ZeroClipboard = ZeroClipboard; + } +})(function() { + return this || window; +}()); \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.js b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.js new file mode 100644 index 000000000..dcf5b9f6c --- /dev/null +++ b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.js @@ -0,0 +1,10 @@ +/*! + * ZeroClipboard + * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. + * Copyright (c) 2009-2015 Jon Rohan, James M. Greene + * Licensed MIT + * http://zeroclipboard.org/ + * v2.3.0-beta.1 + */ +!function(a,b){"use strict";var c,d,e,f=a,g=f.document,h=f.navigator,i=f.setTimeout,j=f.clearTimeout,k=f.setInterval,l=f.clearInterval,m=f.getComputedStyle,n=f.encodeURIComponent,o=f.ActiveXObject,p=f.Error,q=f.Number.parseInt||f.parseInt,r=f.Number.parseFloat||f.parseFloat,s=f.Number.isNaN||f.isNaN,t=f.Date.now,u=f.Object.keys,v=f.Object.defineProperty,w=f.Object.prototype.hasOwnProperty,x=f.Array.prototype.slice,y=function(){var a=function(a){return a};if("function"==typeof f.wrap&&"function"==typeof f.unwrap)try{var b=g.createElement("div"),c=f.unwrap(b);1===b.nodeType&&c&&1===c.nodeType&&(a=f.unwrap)}catch(d){}return a}(),z=function(a){return x.call(a,0)},A=function(){var a,c,d,e,f,g,h=z(arguments),i=h[0]||{};for(a=1,c=h.length;c>a;a++)if(null!=(d=h[a]))for(e in d)w.call(d,e)&&(f=i[e],g=d[e],i!==g&&g!==b&&(i[e]=g));return i},B=function(a){var b,c,d,e;if("object"!=typeof a||null==a||"number"==typeof a.nodeType)b=a;else if("number"==typeof a.length)for(b=[],c=0,d=a.length;d>c;c++)w.call(a,c)&&(b[c]=B(a[c]));else{b={};for(e in a)w.call(a,e)&&(b[e]=B(a[e]))}return b},C=function(a,b){for(var c={},d=0,e=b.length;e>d;d++)b[d]in a&&(c[b[d]]=a[b[d]]);return c},D=function(a,b){var c={};for(var d in a)-1===b.indexOf(d)&&(c[d]=a[d]);return c},E=function(a){if(a)for(var b in a)w.call(a,b)&&delete a[b];return a},F=function(a,b){if(a&&1===a.nodeType&&a.ownerDocument&&b&&(1===b.nodeType&&b.ownerDocument&&b.ownerDocument===a.ownerDocument||9===b.nodeType&&!b.ownerDocument&&b===a.ownerDocument))do{if(a===b)return!0;a=a.parentNode}while(a);return!1},G=function(a){var b;return"string"==typeof a&&a&&(b=a.split("#")[0].split("?")[0],b=a.slice(0,a.lastIndexOf("/")+1)),b},H=function(a){var b,c;return"string"==typeof a&&a&&(c=a.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]?b=c[1]:(c=a.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]&&(b=c[1]))),b},I=function(){var a,b;try{throw new p}catch(c){b=c}return b&&(a=b.sourceURL||b.fileName||H(b.stack)),a},J=function(){var a,c,d;if(g.currentScript&&(a=g.currentScript.src))return a;if(c=g.getElementsByTagName("script"),1===c.length)return c[0].src||b;if("readyState"in c[0])for(d=c.length;d--;)if("interactive"===c[d].readyState&&(a=c[d].src))return a;return"loading"===g.readyState&&(a=c[c.length-1].src)?a:(a=I())?a:b},K=function(){var a,c,d,e=g.getElementsByTagName("script");for(a=e.length;a--;){if(!(d=e[a].src)){c=null;break}if(d=G(d),null==c)c=d;else if(c!==d){c=null;break}}return c||b},L=function(){var a=G(J())||K()||"";return a+"ZeroClipboard.swf"},M=function(){var a=/win(dows|[\s]?(nt|me|ce|xp|vista|[\d]+))/i;return!!h&&(a.test(h.appVersion||"")||a.test(h.platform||"")||-1!==(h.userAgent||"").indexOf("Windows"))},N=function(){return null==a.opener&&(!!a.top&&a!=a.top||!!a.parent&&a!=a.parent)}(),O={bridge:null,version:"0.0.0",pluginType:"unknown",disabled:null,outdated:null,sandboxed:null,unavailable:null,degraded:null,deactivated:null,overdue:null,ready:null},P="11.0.0",Q={},R={},S=null,T=0,U=0,V={ready:"Flash communication is established",error:{"flash-disabled":"Flash is disabled or not installed. May also be attempting to run Flash in a sandboxed iframe, which is impossible.","flash-outdated":"Flash is too outdated to support ZeroClipboard","flash-sandboxed":"Attempting to run Flash in a sandboxed iframe, which is impossible","flash-unavailable":"Flash is unable to communicate bidirectionally with JavaScript","flash-degraded":"Flash is unable to preserve data fidelity when communicating with JavaScript","flash-deactivated":"Flash is too outdated for your browser and/or is configured as click-to-activate.\nThis may also mean that the ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity.\nMay also be attempting to run Flash in a sandboxed iframe, which is impossible.","flash-overdue":"Flash communication was established but NOT within the acceptable time limit","version-mismatch":"ZeroClipboard JS version number does not match ZeroClipboard SWF version number","clipboard-error":"At least one error was thrown while ZeroClipboard was attempting to inject your data into the clipboard","config-mismatch":"ZeroClipboard configuration does not match Flash's reality","swf-not-found":"The ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity"}},W=["flash-unavailable","flash-degraded","flash-overdue","version-mismatch","config-mismatch","clipboard-error"],X=["flash-disabled","flash-outdated","flash-sandboxed","flash-unavailable","flash-degraded","flash-deactivated","flash-overdue"],Y=new RegExp("^flash-("+X.map(function(a){return a.replace(/^flash-/,"")}).join("|")+")$"),Z=new RegExp("^flash-("+X.slice(1).map(function(a){return a.replace(/^flash-/,"")}).join("|")+")$"),$={swfPath:L(),trustedDomains:a.location.host?[a.location.host]:[],cacheBust:!0,forceEnhancedClipboard:!1,flashLoadTimeout:3e4,autoActivate:!0,bubbleEvents:!0,fixLineEndings:!0,containerId:"global-zeroclipboard-html-bridge",containerClass:"global-zeroclipboard-container",swfObjectId:"global-zeroclipboard-flash-bridge",hoverClass:"zeroclipboard-is-hover",activeClass:"zeroclipboard-is-active",forceHandCursor:!1,title:null,zIndex:999999999},_=function(a){if("object"==typeof a&&null!==a)for(var b in a)if(w.call(a,b))if(/^(?:forceHandCursor|title|zIndex|bubbleEvents|fixLineEndings)$/.test(b))$[b]=a[b];else if(null==O.bridge)if("containerId"===b||"swfObjectId"===b){if(!oa(a[b]))throw new Error("The specified `"+b+"` value is not valid as an HTML4 Element ID");$[b]=a[b]}else $[b]=a[b];{if("string"!=typeof a||!a)return B($);if(w.call($,a))return $[a]}},aa=function(){return Va(),{browser:C(h,["userAgent","platform","appName","appVersion"]),flash:D(O,["bridge"]),zeroclipboard:{version:Xa.version,config:Xa.config()}}},ba=function(){return!!(O.disabled||O.outdated||O.sandboxed||O.unavailable||O.degraded||O.deactivated)},ca=function(a,d){var e,f,g,h={};if("string"==typeof a&&a)g=a.toLowerCase().split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof d)for(e in a)w.call(a,e)&&"string"==typeof e&&e&&"function"==typeof a[e]&&Xa.on(e,a[e]);if(g&&g.length){for(e=0,f=g.length;f>e;e++)a=g[e].replace(/^on/,""),h[a]=!0,Q[a]||(Q[a]=[]),Q[a].push(d);if(h.ready&&O.ready&&Xa.emit({type:"ready"}),h.error){for(e=0,f=X.length;f>e;e++)if(O[X[e].replace(/^flash-/,"")]===!0){Xa.emit({type:"error",name:X[e]});break}c!==b&&Xa.version!==c&&Xa.emit({type:"error",name:"version-mismatch",jsVersion:Xa.version,swfVersion:c})}}return Xa},da=function(a,b){var c,d,e,f,g;if(0===arguments.length)f=u(Q);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)w.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&Xa.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=Q[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return Xa},ea=function(a){var b;return b="string"==typeof a&&a?B(Q[a])||null:B(Q)},fa=function(a){var b,c,d;return a=pa(a),a&&!wa(a)?"ready"===a.type&&O.overdue===!0?Xa.emit({type:"error",name:"flash-overdue"}):(b=A({},a),ua.call(this,b),"copy"===a.type&&(d=Ea(R),c=d.data,S=d.formatMap),c):void 0},ga=function(){var a=O.sandboxed;if(Va(),"boolean"!=typeof O.ready&&(O.ready=!1),O.sandboxed!==a&&O.sandboxed===!0)O.ready=!1,Xa.emit({type:"error",name:"flash-sandboxed"});else if(!Xa.isFlashUnusable()&&null===O.bridge){var b=$.flashLoadTimeout;"number"==typeof b&&b>=0&&(T=i(function(){"boolean"!=typeof O.deactivated&&(O.deactivated=!0),O.deactivated===!0&&Xa.emit({type:"error",name:"flash-deactivated"})},b)),O.overdue=!1,Ca()}},ha=function(){Xa.clearData(),Xa.blur(),Xa.emit("destroy"),Da(),Xa.off()},ia=function(a,b){var c;if("object"==typeof a&&a&&"undefined"==typeof b)c=a,Xa.clearData();else{if("string"!=typeof a||!a)return;c={},c[a]=b}for(var d in c)"string"==typeof d&&d&&w.call(c,d)&&"string"==typeof c[d]&&c[d]&&(R[d]=Ua(c[d]))},ja=function(a){"undefined"==typeof a?(E(R),S=null):"string"==typeof a&&w.call(R,a)&&delete R[a]},ka=function(a){return"undefined"==typeof a?B(R):"string"==typeof a&&w.call(R,a)?R[a]:void 0},la=function(a){if(a&&1===a.nodeType){d&&(Ma(d,$.activeClass),d!==a&&Ma(d,$.hoverClass)),d=a,La(a,$.hoverClass);var b=a.getAttribute("title")||$.title;if("string"==typeof b&&b){var c=Ba(O.bridge);c&&c.setAttribute("title",b)}var e=$.forceHandCursor===!0||"pointer"===Na(a,"cursor");Sa(e),Ra()}},ma=function(){var a=Ba(O.bridge);a&&(a.removeAttribute("title"),a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px"),d&&(Ma(d,$.hoverClass),Ma(d,$.activeClass),d=null)},na=function(){return d||null},oa=function(a){return"string"==typeof a&&a&&/^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(a)},pa=function(a){var b;if("string"==typeof a&&a?(b=a,a={}):"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(b=a.type),b){b=b.toLowerCase(),!a.target&&(/^(copy|aftercopy|_click)$/.test(b)||"error"===b&&"clipboard-error"===a.name)&&(a.target=e),A(a,{type:b,target:a.target||d||null,relatedTarget:a.relatedTarget||null,currentTarget:O&&O.bridge||null,timeStamp:a.timeStamp||t()||null});var c=V[a.type];return"error"===a.type&&a.name&&c&&(c=c[a.name]),c&&(a.message=c),"ready"===a.type&&A(a,{target:null,version:O.version}),"error"===a.type&&(Y.test(a.name)&&A(a,{target:null,minimumVersion:P}),Z.test(a.name)&&A(a,{version:O.version})),"copy"===a.type&&(a.clipboardData={setData:Xa.setData,clearData:Xa.clearData}),"aftercopy"===a.type&&(a=Fa(a,S)),a.target&&!a.relatedTarget&&(a.relatedTarget=qa(a.target)),ra(a)}},qa=function(a){var b=a&&a.getAttribute&&a.getAttribute("data-clipboard-target");return b?g.getElementById(b):null},ra=function(a){if(a&&/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)){var c=a.target,d="_mouseover"===a.type&&a.relatedTarget?a.relatedTarget:b,e="_mouseout"===a.type&&a.relatedTarget?a.relatedTarget:b,h=Oa(c),i=f.screenLeft||f.screenX||0,j=f.screenTop||f.screenY||0,k=g.body.scrollLeft+g.documentElement.scrollLeft,l=g.body.scrollTop+g.documentElement.scrollTop,m=h.left+("number"==typeof a._stageX?a._stageX:0),n=h.top+("number"==typeof a._stageY?a._stageY:0),o=m-k,p=n-l,q=i+o,r=j+p,s="number"==typeof a.movementX?a.movementX:0,t="number"==typeof a.movementY?a.movementY:0;delete a._stageX,delete a._stageY,A(a,{srcElement:c,fromElement:d,toElement:e,screenX:q,screenY:r,pageX:m,pageY:n,clientX:o,clientY:p,x:o,y:p,movementX:s,movementY:t,offsetX:0,offsetY:0,layerX:0,layerY:0})}return a},sa=function(a){var b=a&&"string"==typeof a.type&&a.type||"";return!/^(?:(?:before)?copy|destroy)$/.test(b)},ta=function(a,b,c,d){d?i(function(){a.apply(b,c)},0):a.apply(b,c)},ua=function(a){if("object"==typeof a&&a&&a.type){var b=sa(a),c=Q["*"]||[],d=Q[a.type]||[],e=c.concat(d);if(e&&e.length){var g,h,i,j,k,l=this;for(g=0,h=e.length;h>g;g++)i=e[g],j=l,"string"==typeof i&&"function"==typeof f[i]&&(i=f[i]),"object"==typeof i&&i&&"function"==typeof i.handleEvent&&(j=i,i=i.handleEvent),"function"==typeof i&&(k=A({},a),ta(i,j,[k],b))}return this}},va=function(a){var b=null;return(N===!1||a&&"error"===a.type&&a.name&&-1!==W.indexOf(a.name))&&(b=!1),b},wa=function(a){var b=a.target||d||null,f="swf"===a._source;switch(delete a._source,a.type){case"error":var g="flash-sandboxed"===a.name||va(a);"boolean"==typeof g&&(O.sandboxed=g),-1!==X.indexOf(a.name)?A(O,{disabled:"flash-disabled"===a.name,outdated:"flash-outdated"===a.name,unavailable:"flash-unavailable"===a.name,degraded:"flash-degraded"===a.name,deactivated:"flash-deactivated"===a.name,overdue:"flash-overdue"===a.name,ready:!1}):"version-mismatch"===a.name&&(c=a.swfVersion,A(O,{disabled:!1,outdated:!1,unavailable:!1,degraded:!1,deactivated:!1,overdue:!1,ready:!1})),Qa();break;case"ready":c=a.swfVersion;var h=O.deactivated===!0;A(O,{disabled:!1,outdated:!1,sandboxed:!1,unavailable:!1,degraded:!1,deactivated:!1,overdue:h,ready:!h}),Qa();break;case"beforecopy":e=b;break;case"copy":var i,j,k=a.relatedTarget;!R["text/html"]&&!R["text/plain"]&&k&&(j=k.value||k.outerHTML||k.innerHTML)&&(i=k.value||k.textContent||k.innerText)?(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",i),j!==i&&a.clipboardData.setData("text/html",j)):!R["text/plain"]&&a.target&&(i=a.target.getAttribute("data-clipboard-text"))&&(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",i));break;case"aftercopy":xa(a),Xa.clearData(),b&&b!==Ka()&&b.focus&&b.focus();break;case"_mouseover":Xa.focus(b),$.bubbleEvents===!0&&f&&(b&&b!==a.relatedTarget&&!F(a.relatedTarget,b)&&ya(A({},a,{type:"mouseenter",bubbles:!1,cancelable:!1})),ya(A({},a,{type:"mouseover"})));break;case"_mouseout":Xa.blur(),$.bubbleEvents===!0&&f&&(b&&b!==a.relatedTarget&&!F(a.relatedTarget,b)&&ya(A({},a,{type:"mouseleave",bubbles:!1,cancelable:!1})),ya(A({},a,{type:"mouseout"})));break;case"_mousedown":La(b,$.activeClass),$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}));break;case"_mouseup":Ma(b,$.activeClass),$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}));break;case"_click":e=null,$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}));break;case"_mousemove":$.bubbleEvents===!0&&f&&ya(A({},a,{type:a.type.slice(1)}))}return/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)?!0:void 0},xa=function(a){if(a.errors&&a.errors.length>0){var b=B(a);A(b,{type:"error",name:"clipboard-error"}),delete b.success,i(function(){Xa.emit(b)},0)}},ya=function(a){if(a&&"string"==typeof a.type&&a){var b,c=a.target||null,d=c&&c.ownerDocument||g,e={view:d.defaultView||f,canBubble:!0,cancelable:!0,detail:"click"===a.type?1:0,button:"number"==typeof a.which?a.which-1:"number"==typeof a.button?a.button:d.createEvent?0:1},h=A(e,a);c&&d.createEvent&&c.dispatchEvent&&(h=[h.type,h.canBubble,h.cancelable,h.view,h.detail,h.screenX,h.screenY,h.clientX,h.clientY,h.ctrlKey,h.altKey,h.shiftKey,h.metaKey,h.button,h.relatedTarget],b=d.createEvent("MouseEvents"),b.initMouseEvent&&(b.initMouseEvent.apply(b,h),b._source="js",c.dispatchEvent(b)))}},za=function(){var a=$.flashLoadTimeout;if("number"==typeof a&&a>=0){var b=Math.min(1e3,a/10),c=$.swfObjectId+"_fallbackContent";U=k(function(){var a=g.getElementById(c);Pa(a)&&(Qa(),O.deactivated=null,Xa.emit({type:"error",name:"swf-not-found"}))},b)}},Aa=function(){var a=g.createElement("div");return a.id=$.containerId,a.className=$.containerClass,a.style.position="absolute",a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px",a.style.zIndex=""+Ta($.zIndex),a},Ba=function(a){for(var b=a&&a.parentNode;b&&"OBJECT"===b.nodeName&&b.parentNode;)b=b.parentNode;return b||null},Ca=function(){var a,b=O.bridge,c=Ba(b);if(!b){var d=Ja(f.location.host,$),e="never"===d?"none":"all",h=Ha(A({jsVersion:Xa.version},$)),i=$.swfPath+Ga($.swfPath,$);c=Aa();var j=g.createElement("div");c.appendChild(j),g.body.appendChild(c);var k=g.createElement("div"),l="activex"===O.pluginType;k.innerHTML='"+(l?'':"")+'
     
    ',b=k.firstChild,k=null,y(b).ZeroClipboard=Xa,c.replaceChild(b,j),za()}return b||(b=g[$.swfObjectId],b&&(a=b.length)&&(b=b[a-1]),!b&&c&&(b=c.firstChild)),O.bridge=b||null,b},Da=function(){var a=O.bridge;if(a){var d=Ba(a);d&&("activex"===O.pluginType&&"readyState"in a?(a.style.display="none",function e(){if(4===a.readyState){for(var b in a)"function"==typeof a[b]&&(a[b]=null);a.parentNode&&a.parentNode.removeChild(a),d.parentNode&&d.parentNode.removeChild(d)}else i(e,10)}()):(a.parentNode&&a.parentNode.removeChild(a),d.parentNode&&d.parentNode.removeChild(d))),Qa(),O.ready=null,O.bridge=null,O.deactivated=null,c=b}},Ea=function(a){var b={},c={};if("object"==typeof a&&a){for(var d in a)if(d&&w.call(a,d)&&"string"==typeof a[d]&&a[d])switch(d.toLowerCase()){case"text/plain":case"text":case"air:text":case"flash:text":b.text=a[d],c.text=d;break;case"text/html":case"html":case"air:html":case"flash:html":b.html=a[d],c.html=d;break;case"application/rtf":case"text/rtf":case"rtf":case"richtext":case"air:rtf":case"flash:rtf":b.rtf=a[d],c.rtf=d}return{data:b,formatMap:c}}},Fa=function(a,b){if("object"!=typeof a||!a||"object"!=typeof b||!b)return a;var c={};for(var d in a)if(w.call(a,d))if("errors"===d){c[d]=a[d]?a[d].slice():[];for(var e=0,f=c[d].length;f>e;e++)c[d][e].format=b[c[d][e].format]}else if("success"!==d&&"data"!==d)c[d]=a[d];else{c[d]={};var g=a[d];for(var h in g)h&&w.call(g,h)&&w.call(b,h)&&(c[d][b[h]]=g[h])}return c},Ga=function(a,b){var c=null==b||b&&b.cacheBust===!0;return c?(-1===a.indexOf("?")?"?":"&")+"noCache="+t():""},Ha=function(a){var b,c,d,e,g="",h=[];if(a.trustedDomains&&("string"==typeof a.trustedDomains?e=[a.trustedDomains]:"object"==typeof a.trustedDomains&&"length"in a.trustedDomains&&(e=a.trustedDomains)),e&&e.length)for(b=0,c=e.length;c>b;b++)if(w.call(e,b)&&e[b]&&"string"==typeof e[b]){if(d=Ia(e[b]),!d)continue;if("*"===d){h.length=0,h.push(d);break}h.push.apply(h,[d,"//"+d,f.location.protocol+"//"+d])}return h.length&&(g+="trustedOrigins="+n(h.join(","))),a.forceEnhancedClipboard===!0&&(g+=(g?"&":"")+"forceEnhancedClipboard=true"),"string"==typeof a.swfObjectId&&a.swfObjectId&&(g+=(g?"&":"")+"swfObjectId="+n(a.swfObjectId)),"string"==typeof a.jsVersion&&a.jsVersion&&(g+=(g?"&":"")+"jsVersion="+n(a.jsVersion)),g},Ia=function(a){if(null==a||""===a)return null;if(a=a.replace(/^\s+|\s+$/g,""),""===a)return null;var b=a.indexOf("//");a=-1===b?a:a.slice(b+2);var c=a.indexOf("/");return a=-1===c?a:-1===b||0===c?null:a.slice(0,c),a&&".swf"===a.slice(-4).toLowerCase()?null:a||null},Ja=function(){var a=function(a){var b,c,d,e=[];if("string"==typeof a&&(a=[a]),"object"!=typeof a||!a||"number"!=typeof a.length)return e;for(b=0,c=a.length;c>b;b++)if(w.call(a,b)&&(d=Ia(a[b]))){if("*"===d){e.length=0,e.push("*");break}-1===e.indexOf(d)&&e.push(d)}return e};return function(b,c){var d=Ia(c.swfPath);null===d&&(d=b);var e=a(c.trustedDomains),f=e.length;if(f>0){if(1===f&&"*"===e[0])return"always";if(-1!==e.indexOf(b))return 1===f&&b===d?"sameDomain":"always"}return"never"}}(),Ka=function(){try{return g.activeElement}catch(a){return null}},La=function(a,b){var c,d,e,f=[];if("string"==typeof b&&b&&(f=b.split(/\s+/)),a&&1===a.nodeType&&f.length>0){for(e=(" "+(a.className||"")+" ").replace(/[\t\r\n\f]/g," "),c=0,d=f.length;d>c;c++)-1===e.indexOf(" "+f[c]+" ")&&(e+=f[c]+" ");e=e.replace(/^\s+|\s+$/g,""),e!==a.className&&(a.className=e)}return a},Ma=function(a,b){var c,d,e,f=[];if("string"==typeof b&&b&&(f=b.split(/\s+/)),a&&1===a.nodeType&&f.length>0&&a.className){for(e=(" "+a.className+" ").replace(/[\t\r\n\f]/g," "),c=0,d=f.length;d>c;c++)e=e.replace(" "+f[c]+" "," ");e=e.replace(/^\s+|\s+$/g,""),e!==a.className&&(a.className=e)}return a},Na=function(a,b){var c=m(a,null).getPropertyValue(b);return"cursor"!==b||c&&"auto"!==c||"A"!==a.nodeName?c:"pointer"},Oa=function(a){var b={left:0,top:0,width:0,height:0};if(a.getBoundingClientRect){var c=a.getBoundingClientRect(),d=f.pageXOffset,e=f.pageYOffset,h=g.documentElement.clientLeft||0,i=g.documentElement.clientTop||0,j=0,k=0;if("relative"===Na(g.body,"position")){var l=g.body.getBoundingClientRect(),m=g.documentElement.getBoundingClientRect();j=l.left-m.left||0,k=l.top-m.top||0}b.left=c.left+d-h-j,b.top=c.top+e-i-k,b.width="width"in c?c.width:c.right-c.left,b.height="height"in c?c.height:c.bottom-c.top}return b},Pa=function(a){if(!a)return!1;var b=m(a,null);if(!b)return!1;var c=r(b.height)>0,d=r(b.width)>0,e=r(b.top)>=0,f=r(b.left)>=0,g=c&&d&&e&&f,h=g?null:Oa(a),i="none"!==b.display&&"collapse"!==b.visibility&&(g||!!h&&(c||h.height>0)&&(d||h.width>0)&&(e||h.top>=0)&&(f||h.left>=0));return i},Qa=function(){j(T),T=0,l(U),U=0},Ra=function(){var a;if(d&&(a=Ba(O.bridge))){var b=Oa(d);A(a.style,{width:b.width+"px",height:b.height+"px",top:b.top+"px",left:b.left+"px",zIndex:""+Ta($.zIndex)})}},Sa=function(a){O.ready===!0&&(O.bridge&&"function"==typeof O.bridge.setHandCursor?O.bridge.setHandCursor(a):O.ready=!1)},Ta=function(a){if(/^(?:auto|inherit)$/.test(a))return a;var b;return"number"!=typeof a||s(a)?"string"==typeof a&&(b=Ta(q(a,10))):b=a,"number"==typeof b?b:"auto"},Ua=function(a){var b=/(\r\n|\r|\n)/g;return"string"==typeof a&&$.fixLineEndings===!0&&(M()?/((^|[^\r])\n|\r([^\n]|$))/.test(a)&&(a=a.replace(b,"\r\n")):/\r/.test(a)&&(a=a.replace(b,"\n"))),a},Va=function(b){var c,d,e,f=O.sandboxed,g=null;if(b=b===!0,N===!1)g=!1;else{try{d=a.frameElement||null}catch(h){e={name:h.name,message:h.message}}if(d&&1===d.nodeType&&"IFRAME"===d.nodeName)try{g=d.hasAttribute("sandbox")}catch(h){g=null}else{try{c=document.domain||null}catch(h){c=null}(null===c||e&&"SecurityError"===e.name&&/(^|[\s\(\[@])sandbox(es|ed|ing|[\s\.,!\)\]@]|$)/.test(e.message.toLowerCase()))&&(g=!0)}}return O.sandboxed=g,f===g||b||Wa(o),g},Wa=function(a){function b(a){var b=a.match(/[\d]+/g);return b.length=3,b.join(".")}function c(a){return!!a&&(a=a.toLowerCase())&&(/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(a)||"chrome.plugin"===a.slice(-13))}function d(a){a&&(i=!0,a.version&&(l=b(a.version)),!l&&a.description&&(l=b(a.description)),a.filename&&(k=c(a.filename)))}var e,f,g,i=!1,j=!1,k=!1,l="";if(h.plugins&&h.plugins.length)e=h.plugins["Shockwave Flash"],d(e),h.plugins["Shockwave Flash 2.0"]&&(i=!0,l="2.0.0.11");else if(h.mimeTypes&&h.mimeTypes.length)g=h.mimeTypes["application/x-shockwave-flash"],e=g&&g.enabledPlugin,d(e);else if("undefined"!=typeof a){j=!0;try{f=new a("ShockwaveFlash.ShockwaveFlash.7"),i=!0,l=b(f.GetVariable("$version"))}catch(m){try{f=new a("ShockwaveFlash.ShockwaveFlash.6"),i=!0,l="6.0.21"}catch(n){try{f=new a("ShockwaveFlash.ShockwaveFlash"),i=!0,l=b(f.GetVariable("$version"))}catch(o){j=!1}}}}O.disabled=i!==!0,O.outdated=l&&r(l)e;e++)a=g[e].replace(/^on/,""),h[a]=!0,j[a]||(j[a]=[]),j[a].push(d);if(h.ready&&O.ready&&this.emit({type:"ready",client:this}),h.error){for(e=0,f=X.length;f>e;e++)if(O[X[e].replace(/^flash-/,"")]){this.emit({type:"error",name:X[e],client:this});break}c!==b&&Xa.version!==c&&this.emit({type:"error",name:"version-mismatch",jsVersion:Xa.version,swfVersion:c})}}return this},db=function(a,b){var c,d,e,f,g,h=Za[this.id],i=h&&h.handlers;if(!i)return this;if(0===arguments.length)f=u(i);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)w.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&this.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=i[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return this},eb=function(a){var b=null,c=Za[this.id]&&Za[this.id].handlers;return c&&(b="string"==typeof a&&a?c[a]?c[a].slice(0):[]:B(c)),b},fb=function(a){if(kb.call(this,a)){"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(a=A({},a));var b=A({},pa(a),{client:this});lb.call(this,b)}return this},gb=function(a){if(!Za[this.id])throw new Error("Attempted to clip element(s) to a destroyed ZeroClipboard client instance");a=mb(a);for(var b=0;b0,e=!a.target||d&&-1!==c.indexOf(a.target),f=a.relatedTarget&&d&&-1!==c.indexOf(a.relatedTarget),g=a.client&&a.client===this;return b&&(e||f||g)?!0:!1},lb=function(a){var b=Za[this.id];if("object"==typeof a&&a&&a.type&&b){var c=sa(a),d=b&&b.handlers["*"]||[],e=b&&b.handlers[a.type]||[],g=d.concat(e);if(g&&g.length){var h,i,j,k,l,m=this;for(h=0,i=g.length;i>h;h++)j=g[h],k=m,"string"==typeof j&&"function"==typeof f[j]&&(j=f[j]),"object"==typeof j&&j&&"function"==typeof j.handleEvent&&(k=j,j=j.handleEvent),"function"==typeof j&&(l=A({},a),ta(j,k,[l],c))}}},mb=function(a){return"string"==typeof a&&(a=[]),"number"!=typeof a.length?[a]:a},nb=function(a){if(a&&1===a.nodeType){var b=function(a){(a||(a=f.event))&&("js"!==a._source&&(a.stopImmediatePropagation(),a.preventDefault()),delete a._source)},c=function(c){(c||(c=f.event))&&(b(c),Xa.focus(a))};a.addEventListener("mouseover",c,!1),a.addEventListener("mouseout",b,!1),a.addEventListener("mouseenter",b,!1),a.addEventListener("mouseleave",b,!1),a.addEventListener("mousemove",b,!1),ab[a.zcClippingId]={mouseover:c,mouseout:b,mouseenter:b,mouseleave:b,mousemove:b}}},ob=function(a){if(a&&1===a.nodeType){var b=ab[a.zcClippingId];if("object"==typeof b&&b){for(var c,d,e=["move","leave","enter","out","over"],f=0,g=e.length;g>f;f++)c="mouse"+e[f],d=b[c],"function"==typeof d&&a.removeEventListener(c,d,!1);delete ab[a.zcClippingId]}}};Xa._createClient=function(){bb.apply(this,z(arguments))},Xa.prototype.on=function(){return cb.apply(this,z(arguments))},Xa.prototype.off=function(){return db.apply(this,z(arguments))},Xa.prototype.handlers=function(){return eb.apply(this,z(arguments))},Xa.prototype.emit=function(){return fb.apply(this,z(arguments))},Xa.prototype.clip=function(){return gb.apply(this,z(arguments))},Xa.prototype.unclip=function(){return hb.apply(this,z(arguments))},Xa.prototype.elements=function(){return ib.apply(this,z(arguments))},Xa.prototype.destroy=function(){return jb.apply(this,z(arguments))},Xa.prototype.setText=function(a){if(!Za[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Xa.setData("text/plain",a),this},Xa.prototype.setHtml=function(a){if(!Za[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Xa.setData("text/html",a),this},Xa.prototype.setRichText=function(a){if(!Za[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Xa.setData("application/rtf",a),this},Xa.prototype.setData=function(){if(!Za[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Xa.setData.apply(this,z(arguments)),this},Xa.prototype.clearData=function(){if(!Za[this.id])throw new Error("Attempted to clear pending clipboard data from a destroyed ZeroClipboard client instance");return Xa.clearData.apply(this,z(arguments)),this},Xa.prototype.getData=function(){if(!Za[this.id])throw new Error("Attempted to get pending clipboard data from a destroyed ZeroClipboard client instance");return Xa.getData.apply(this,z(arguments))},"function"==typeof define&&define.amd?define(function(){return Xa}):"object"==typeof module&&module&&"object"==typeof module.exports&&module.exports?module.exports=Xa:a.ZeroClipboard=Xa}(function(){return this||window}()); +//# sourceMappingURL=ZeroClipboard.min.map \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.map b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.map new file mode 100644 index 000000000..22d52f5df --- /dev/null +++ b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.min.map @@ -0,0 +1 @@ +{"version":3,"file":"ZeroClipboard.min.js","sources":["ZeroClipboard.js"],"names":["window","undefined","_zcSwfVersion","_currentElement","_copyTarget","_window","_document","document","_navigator","navigator","_setTimeout","setTimeout","_clearTimeout","clearTimeout","_setInterval","setInterval","_clearInterval","clearInterval","_getComputedStyle","getComputedStyle","_encodeURIComponent","encodeURIComponent","_ActiveXObject","ActiveXObject","_Error","Error","_parseInt","Number","parseInt","_parseFloat","parseFloat","_isNaN","isNaN","_now","Date","now","_keys","Object","keys","_defineProperty","defineProperty","_hasOwn","prototype","hasOwnProperty","_slice","Array","slice","_unwrap","unwrapper","el","wrap","unwrap","div","createElement","unwrappedDiv","nodeType","e","_args","argumentsObj","call","_extend","i","len","arg","prop","src","copy","args","arguments","target","length","_deepCopy","source","_pick","obj","newObj","_omit","indexOf","_deleteOwnProperties","_containedBy","ancestorEl","ownerDocument","parentNode","_getDirPathOfUrl","url","dir","split","lastIndexOf","_getCurrentScriptUrlFromErrorStack","stack","matches","match","_getCurrentScriptUrlFromError","err","sourceURL","fileName","_getCurrentScriptUrl","jsPath","scripts","currentScript","getElementsByTagName","readyState","_getUnanimousScriptParentDir","jsDir","_getDefaultSwfPath","_isWindows","isWindowsRegex","test","appVersion","platform","userAgent","_pageIsFramed","opener","top","parent","_flashState","bridge","version","pluginType","disabled","outdated","sandboxed","unavailable","degraded","deactivated","overdue","ready","_minimumFlashVersion","_handlers","_clipData","_clipDataFormatMap","_flashCheckTimeout","_swfFallbackCheckInterval","_eventMessages","error","flash-disabled","flash-outdated","flash-sandboxed","flash-unavailable","flash-degraded","flash-deactivated","flash-overdue","version-mismatch","clipboard-error","config-mismatch","swf-not-found","_errorsThatOnlyOccurAfterFlashLoads","_flashStateErrorNames","_flashStateErrorNameMatchingRegex","RegExp","map","errorName","replace","join","_flashStateEnabledErrorNameMatchingRegex","_globalConfig","swfPath","trustedDomains","location","host","cacheBust","forceEnhancedClipboard","flashLoadTimeout","autoActivate","bubbleEvents","fixLineEndings","containerId","containerClass","swfObjectId","hoverClass","activeClass","forceHandCursor","title","zIndex","_config","options","_isValidHtml4Id","_state","_detectSandbox","browser","flash","zeroclipboard","ZeroClipboard","config","_isFlashUnusable","_on","eventType","listener","events","added","toLowerCase","on","push","emit","type","name","jsVersion","swfVersion","_off","foundIndex","perEventHandlers","off","splice","_listeners","_emit","event","eventCopy","returnVal","tmp","_createEvent","_preprocessEvent","_dispatchCallbacks","this","_mapClipDataToFlash","data","formatMap","_create","previousState","isFlashUnusable","maxWait","_embedSwf","_destroy","clearData","blur","_unembedSwf","_setData","format","dataObj","dataFormat","_fixLineEndings","_clearData","_getData","_focus","element","_removeClass","_addClass","newTitle","getAttribute","htmlBridge","_getHtmlBridge","setAttribute","useHandCursor","_getStyle","_setHandCursor","_reposition","_blur","removeAttribute","style","left","width","height","_activeElement","id","relatedTarget","currentTarget","timeStamp","msg","message","minimumVersion","clipboardData","setData","_mapClipResultsFromFlash","_getRelatedTarget","_addMouseData","targetEl","relatedTargetId","getElementById","srcElement","fromElement","toElement","pos","_getElementPosition","screenLeft","screenX","screenTop","screenY","scrollLeft","body","documentElement","scrollTop","pageX","_stageX","pageY","_stageY","clientX","clientY","moveX","movementX","moveY","movementY","x","y","offsetX","offsetY","layerX","layerY","_shouldPerformAsync","_dispatchCallback","func","context","async","apply","wildcardTypeHandlers","specificTypeHandlers","handlers","concat","originalContext","handleEvent","_getSandboxStatusFromErrorEvent","isSandboxed","sourceIsSwf","_source","_clearTimeoutsAndPolling","wasDeactivated","textContent","htmlContent","value","outerHTML","innerHTML","innerText","_queueEmitClipboardErrors","_safeActiveElement","focus","_fireMouseEvent","bubbles","cancelable","aftercopyEvent","errors","errorEvent","success","doc","defaults","view","defaultView","canBubble","detail","button","which","createEvent","dispatchEvent","ctrlKey","altKey","shiftKey","metaKey","initMouseEvent","_watchForSwfFallbackContent","pollWait","Math","min","fallbackContentId","_isElementVisible","_createHtmlBridge","container","className","position","_getSafeZIndex","flashBridge","nodeName","allowScriptAccess","_determineScriptAccess","allowNetworking","flashvars","_vars","swfUrl","_cacheBust","divToBeReplaced","appendChild","tmpDiv","usingActiveX","firstChild","replaceChild","display","removeSwfFromIE","removeChild","clipData","newClipData","text","html","rtf","clipResults","newResults","tmpHash","path","domain","domains","str","trustedOriginsExpanded","_extractDomain","protocol","originOrUrl","protocolIndex","pathIndex","_extractAllDomains","origins","resultsArray","currentDomain","configOptions","swfDomain","activeElement","c","cl","classNames","getPropertyValue","getBoundingClientRect","elRect","pageXOffset","pageYOffset","leftBorderWidth","clientLeft","topBorderWidth","clientTop","leftBodyOffset","topBodyOffset","bodyRect","htmlRect","right","bottom","styles","hasCssHeight","hasCssWidth","hasCssTop","hasCssLeft","cssKnows","rect","isVisible","visibility","enabled","setHandCursor","val","content","replaceRegex","doNotReassessFlashSupport","effectiveScriptOrigin","frame","frameError","frameElement","hasAttribute","_detectFlashSupport","parseFlashVersion","desc","isPepperFlash","flashPlayerFileName","inspectPlugin","plugin","hasFlash","flashVersion","description","filename","isPPAPI","ax","mimeType","isActiveX","plugins","mimeTypes","enabledPlugin","GetVariable","e1","e2","e3","_createClient","writable","configurable","enumerable","state","create","destroy","getData","activate","deactivate","_clientIdCounter","_clientMeta","_elementIdCounter","_elementMeta","_mouseHandlers","_clientConstructor","elements","client","instance","clip","_clientOn","meta","_clientOff","_clientListeners","_clientEmit","_clientShouldEmit","_clientDispatchCallbacks","_clientClip","_prepClip","zcClippingId","_addMouseHandlers","clippedElements","_clientUnclip","arrayIndex","clientIds","_removeMouseHandlers","_clientElements","_clientDestroy","unclip","clippedEls","hasClippedEls","goodTarget","goodRelTarget","goodClient","_suppressMouseEvents","stopImmediatePropagation","preventDefault","_elementMouseOver","addEventListener","mouseover","mouseout","mouseenter","mouseleave","mousemove","mouseHandlers","key","mouseEvents","removeEventListener","setText","setHtml","setRichText","richText","define","amd","module","exports"],"mappings":";;;;;;;;CAQA,SAAUA,EAAQC,GAChB,YAKA,IA8SIC,GAUAC,EAKAC,EA7TAC,EAAUL,EAAQM,EAAYD,EAAQE,SAAUC,EAAaH,EAAQI,UAAWC,EAAcL,EAAQM,WAAYC,EAAgBP,EAAQQ,aAAcC,EAAeT,EAAQU,YAAaC,EAAiBX,EAAQY,cAAeC,EAAoBb,EAAQc,iBAAkBC,EAAsBf,EAAQgB,mBAAoBC,EAAiBjB,EAAQkB,cAAeC,EAASnB,EAAQoB,MAAOC,EAAYrB,EAAQsB,OAAOC,UAAYvB,EAAQuB,SAAUC,EAAcxB,EAAQsB,OAAOG,YAAczB,EAAQyB,WAAYC,EAAS1B,EAAQsB,OAAOK,OAAS3B,EAAQ2B,MAAOC,EAAO5B,EAAQ6B,KAAKC,IAAKC,EAAQ/B,EAAQgC,OAAOC,KAAMC,EAAkBlC,EAAQgC,OAAOG,eAAgBC,EAAUpC,EAAQgC,OAAOK,UAAUC,eAAgBC,EAASvC,EAAQwC,MAAMH,UAAUI,MAAOC,EAAU,WAC1vB,GAAIC,GAAY,SAASC,GACvB,MAAOA,GAET,IAA4B,kBAAjB5C,GAAQ6C,MAAiD,kBAAnB7C,GAAQ8C,OACvD,IACE,GAAIC,GAAM9C,EAAU+C,cAAc,OAC9BC,EAAejD,EAAQ8C,OAAOC,EACb,KAAjBA,EAAIG,UAAkBD,GAA0C,IAA1BA,EAAaC,WACrDP,EAAY3C,EAAQ8C,QAEtB,MAAOK,IAEX,MAAOR,MAQLS,EAAQ,SAASC,GACnB,MAAOd,GAAOe,KAAKD,EAAc,IAQ/BE,EAAU,WACZ,GAAIC,GAAGC,EAAKC,EAAKC,EAAMC,EAAKC,EAAMC,EAAOV,EAAMW,WAAYC,EAASF,EAAK,MACzE,KAAKN,EAAI,EAAGC,EAAMK,EAAKG,OAAYR,EAAJD,EAASA,IACtC,GAAuB,OAAlBE,EAAMI,EAAKN,IACd,IAAKG,IAAQD,GACPtB,EAAQkB,KAAKI,EAAKC,KACpBC,EAAMI,EAAOL,GACbE,EAAOH,EAAIC,GACPK,IAAWH,GAAQA,IAASjE,IAC9BoE,EAAOL,GAAQE,GAMzB,OAAOG,IAQLE,EAAY,SAASC,GACvB,GAAIN,GAAML,EAAGC,EAAKE,CAClB,IAAsB,gBAAXQ,IAAiC,MAAVA,GAA6C,gBAApBA,GAAOjB,SAChEW,EAAOM,MACF,IAA6B,gBAAlBA,GAAOF,OAEvB,IADAJ,KACKL,EAAI,EAAGC,EAAMU,EAAOF,OAAYR,EAAJD,EAASA,IACpCpB,EAAQkB,KAAKa,EAAQX,KACvBK,EAAKL,GAAKU,EAAUC,EAAOX,SAG1B,CACLK,IACA,KAAKF,IAAQQ,GACP/B,EAAQkB,KAAKa,EAAQR,KACvBE,EAAKF,GAAQO,EAAUC,EAAOR,KAIpC,MAAOE,IAULO,EAAQ,SAASC,EAAKpC,GAExB,IAAK,GADDqC,MACKd,EAAI,EAAGC,EAAMxB,EAAKgC,OAAYR,EAAJD,EAASA,IACtCvB,EAAKuB,IAAMa,KACbC,EAAOrC,EAAKuB,IAAMa,EAAIpC,EAAKuB,IAG/B,OAAOc,IASLC,EAAQ,SAASF,EAAKpC,GACxB,GAAIqC,KACJ,KAAK,GAAIX,KAAQU,GACY,KAAvBpC,EAAKuC,QAAQb,KACfW,EAAOX,GAAQU,EAAIV,GAGvB,OAAOW,IAQLG,EAAuB,SAASJ,GAClC,GAAIA,EACF,IAAK,GAAIV,KAAQU,GACXjC,EAAQkB,KAAKe,EAAKV,UACbU,GAAIV,EAIjB,OAAOU,IAQLK,EAAe,SAAS9B,EAAI+B,GAC9B,GAAI/B,GAAsB,IAAhBA,EAAGM,UAAkBN,EAAGgC,eAAiBD,IAAuC,IAAxBA,EAAWzB,UAAkByB,EAAWC,eAAiBD,EAAWC,gBAAkBhC,EAAGgC,eAAyC,IAAxBD,EAAWzB,WAAmByB,EAAWC,eAAiBD,IAAe/B,EAAGgC,eACtP,EAAG,CACD,GAAIhC,IAAO+B,EACT,OAAO,CAET/B,GAAKA,EAAGiC,iBACDjC,EAEX,QAAO,GAQLkC,EAAmB,SAASC,GAC9B,GAAIC,EAKJ,OAJmB,gBAARD,IAAoBA,IAC7BC,EAAMD,EAAIE,MAAM,KAAK,GAAGA,MAAM,KAAK,GACnCD,EAAMD,EAAItC,MAAM,EAAGsC,EAAIG,YAAY,KAAO,IAErCF,GAQLG,EAAqC,SAASC,GAChD,GAAIL,GAAKM,CAYT,OAXqB,gBAAVD,IAAsBA,IAC/BC,EAAUD,EAAME,MAAM,sIAClBD,GAAWA,EAAQ,GACrBN,EAAMM,EAAQ,IAEdA,EAAUD,EAAME,MAAM,kEAClBD,GAAWA,EAAQ,KACrBN,EAAMM,EAAQ,MAIbN,GAQLQ,EAAgC,WAClC,GAAIR,GAAKS,CACT,KACE,KAAM,IAAIrE,GACV,MAAOgC,GACPqC,EAAMrC,EAKR,MAHIqC,KACFT,EAAMS,EAAIC,WAAaD,EAAIE,UAAYP,EAAmCK,EAAIJ,QAEzEL,GAQLY,EAAuB,WACzB,GAAIC,GAAQC,EAASrC,CACrB,IAAIvD,EAAU6F,gBAAkBF,EAAS3F,EAAU6F,cAAclC,KAC/D,MAAOgC,EAGT,IADAC,EAAU5F,EAAU8F,qBAAqB,UAClB,IAAnBF,EAAQ5B,OACV,MAAO4B,GAAQ,GAAGjC,KAAOhE,CAE3B,IAAI,cAAgBiG,GAAQ,GAC1B,IAAKrC,EAAIqC,EAAQ5B,OAAQT,KACvB,GAA8B,gBAA1BqC,EAAQrC,GAAGwC,aAAiCJ,EAASC,EAAQrC,GAAGI,KAClE,MAAOgC,EAIb,OAA6B,YAAzB3F,EAAU+F,aAA6BJ,EAASC,EAAQA,EAAQ5B,OAAS,GAAGL,KACvEgC,GAELA,EAASL,KACJK,EAEFhG,GAULqG,EAA+B,WACjC,GAAIzC,GAAG0C,EAAON,EAAQC,EAAU5F,EAAU8F,qBAAqB,SAC/D,KAAKvC,EAAIqC,EAAQ5B,OAAQT,KAAO,CAC9B,KAAMoC,EAASC,EAAQrC,GAAGI,KAAM,CAC9BsC,EAAQ,IACR,OAGF,GADAN,EAASd,EAAiBc,GACb,MAATM,EACFA,EAAQN,MACH,IAAIM,IAAUN,EAAQ,CAC3BM,EAAQ,IACR,QAGJ,MAAOA,IAAStG,GASduG,EAAqB,WACvB,GAAID,GAAQpB,EAAiBa,MAA2BM,KAAkC,EAC1F,OAAOC,GAAQ,qBAQbE,EAAa,WACf,GAAIC,GAAiB,2CACrB,SAASlG,IAAekG,EAAeC,KAAKnG,EAAWoG,YAAc,KAAOF,EAAeC,KAAKnG,EAAWqG,UAAY,KAA2D,MAAnDrG,EAAWsG,WAAa,IAAIjC,QAAQ,aAMjKkC,EAAgB,WAClB,MAAwB,OAAjB/G,EAAOgH,WAAqBhH,EAAOiH,KAAOjH,GAAUA,EAAOiH,OAASjH,EAAOkH,QAAUlH,GAAUA,EAAOkH,WAM3GC,GACFC,OAAQ,KACRC,QAAS,QACTC,WAAY,UACZC,SAAU,KACVC,SAAU,KACVC,UAAW,KACXC,YAAa,KACbC,SAAU,KACVC,YAAa,KACbC,QAAS,KACTC,MAAO,MAOLC,EAAuB,SASvBC,KAeAC,KAKAC,EAAqB,KAKrBC,EAAqB,EAKrBC,EAA4B,EAK5BC,GACFP,MAAO,qCACPQ,OACEC,iBAAkB,sHAClBC,iBAAkB,iDAClBC,kBAAmB,qEACnBC,oBAAqB,iEACrBC,iBAAkB,+EAClBC,oBAAqB,0TACrBC,gBAAiB,+EACjBC,mBAAoB,kFACpBC,kBAAmB,0GACnBC,kBAAmB,6DACnBC,gBAAiB,+HAQjBC,GAAwC,oBAAqB,iBAAkB,gBAAiB,mBAAoB,kBAAmB,mBAMvIC,GAA0B,iBAAkB,iBAAkB,kBAAmB,oBAAqB,iBAAkB,oBAAqB,iBAK7IC,EAAoC,GAAIC,QAAO,WAAaF,EAAsBG,IAAI,SAASC,GACjG,MAAOA,GAAUC,QAAQ,UAAW,MACnCC,KAAK,KAAO,MAMXC,EAA2C,GAAIL,QAAO,WAAaF,EAAsBrG,MAAM,GAAGwG,IAAI,SAASC,GACjH,MAAOA,GAAUC,QAAQ,UAAW,MACnCC,KAAK,KAAO,MAKXE,GACFC,QAASpD,IACTqD,eAAgB7J,EAAO8J,SAASC,MAAS/J,EAAO8J,SAASC,SACzDC,WAAW,EACXC,wBAAwB,EACxBC,iBAAkB,IAClBC,cAAc,EACdC,cAAc,EACdC,gBAAgB,EAChBC,YAAa,mCACbC,eAAgB,iCAChBC,YAAa,oCACbC,WAAY,yBACZC,YAAa,0BACbC,iBAAiB,EACjBC,MAAO,KACPC,OAAQ,WAMNC,EAAU,SAASC,GACrB,GAAuB,gBAAZA,IAAoC,OAAZA,EACjC,IAAK,GAAI/G,KAAQ+G,GACf,GAAItI,EAAQkB,KAAKoH,EAAS/G,GACxB,GAAI,iEAAiE2C,KAAK3C,GACxE2F,EAAc3F,GAAQ+G,EAAQ/G,OACzB,IAA0B,MAAtBmD,EAAYC,OACrB,GAAa,gBAATpD,GAAmC,gBAATA,EAAwB,CACpD,IAAIgH,GAAgBD,EAAQ/G,IAG1B,KAAM,IAAIvC,OAAM,kBAAoBuC,EAAO,8CAF3C2F,GAAc3F,GAAQ+G,EAAQ/G,OAKhC2F,GAAc3F,GAAQ+G,EAAQ/G,EAMxC,EAAA,GAAuB,gBAAZ+G,KAAwBA,EAMnC,MAAOxG,GAAUoF,EALf,IAAIlH,EAAQkB,KAAKgG,EAAeoB,GAC9B,MAAOpB,GAAcoB,KAUvBE,GAAS,WAEX,MADAC,OAEEC,QAAS1G,EAAMjE,GAAc,YAAa,WAAY,UAAW,eACjE4K,MAAOxG,EAAMuC,GAAe,WAC5BkE,eACEhE,QAASiE,GAAcjE,QACvBkE,OAAQD,GAAcC,YAQxBC,GAAmB,WACrB,SAAUrE,EAAYI,UAAYJ,EAAYK,UAAYL,EAAYM,WAAaN,EAAYO,aAAeP,EAAYQ,UAAYR,EAAYS,cAMhJ6D,GAAM,SAASC,EAAWC,GAC5B,GAAI9H,GAAGC,EAAK8H,EAAQC,IACpB,IAAyB,gBAAdH,IAA0BA,EACnCE,EAASF,EAAUI,cAAcxG,MAAM,WAClC,IAAyB,gBAAdoG,IAA0BA,GAAiC,mBAAbC,GAC9D,IAAK9H,IAAK6H,GACJjJ,EAAQkB,KAAK+H,EAAW7H,IAAmB,gBAANA,IAAkBA,GAA6B,kBAAjB6H,GAAU7H,IAC/EyH,GAAcS,GAAGlI,EAAG6H,EAAU7H,GAIpC,IAAI+H,GAAUA,EAAOtH,OAAQ,CAC3B,IAAKT,EAAI,EAAGC,EAAM8H,EAAOtH,OAAYR,EAAJD,EAASA,IACxC6H,EAAYE,EAAO/H,GAAG2F,QAAQ,MAAO,IACrCqC,EAAMH,IAAa,EACd1D,EAAU0D,KACb1D,EAAU0D,OAEZ1D,EAAU0D,GAAWM,KAAKL,EAO5B,IALIE,EAAM/D,OAASX,EAAYW,OAC7BwD,GAAcW,MACZC,KAAM,UAGNL,EAAMvD,MAAO,CACf,IAAKzE,EAAI,EAAGC,EAAMqF,EAAsB7E,OAAYR,EAAJD,EAASA,IACvD,GAAIsD,EAAYgC,EAAsBtF,GAAG2F,QAAQ,UAAW,QAAS,EAAM,CACzE8B,GAAcW,MACZC,KAAM,QACNC,KAAMhD,EAAsBtF,IAE9B,OAGA3D,IAAkBD,GAAaqL,GAAcjE,UAAYnH,GAC3DoL,GAAcW,MACZC,KAAM,QACNC,KAAM,mBACNC,UAAWd,GAAcjE,QACzBgF,WAAYnM,KAKpB,MAAOoL,KAMLgB,GAAO,SAASZ,EAAWC,GAC7B,GAAI9H,GAAGC,EAAKyI,EAAYX,EAAQY,CAChC,IAAyB,IAArBpI,UAAUE,OACZsH,EAASxJ,EAAM4F,OACV,IAAyB,gBAAd0D,IAA0BA,EAC1CE,EAASF,EAAUpG,MAAM,WACpB,IAAyB,gBAAdoG,IAA0BA,GAAiC,mBAAbC,GAC9D,IAAK9H,IAAK6H,GACJjJ,EAAQkB,KAAK+H,EAAW7H,IAAmB,gBAANA,IAAkBA,GAA6B,kBAAjB6H,GAAU7H,IAC/EyH,GAAcmB,IAAI5I,EAAG6H,EAAU7H,GAIrC,IAAI+H,GAAUA,EAAOtH,OACnB,IAAKT,EAAI,EAAGC,EAAM8H,EAAOtH,OAAYR,EAAJD,EAASA,IAGxC,GAFA6H,EAAYE,EAAO/H,GAAGiI,cAActC,QAAQ,MAAO,IACnDgD,EAAmBxE,EAAU0D,GACzBc,GAAoBA,EAAiBlI,OACvC,GAAIqH,EAEF,IADAY,EAAaC,EAAiB3H,QAAQ8G,GAChB,KAAfY,GACLC,EAAiBE,OAAOH,EAAY,GACpCA,EAAaC,EAAiB3H,QAAQ8G,EAAUY,OAGlDC,GAAiBlI,OAAS,CAKlC,OAAOgH,KAMLqB,GAAa,SAASjB,GACxB,GAAIxH,EAMJ,OAJEA,GADuB,gBAAdwH,IAA0BA,EAC5BnH,EAAUyD,EAAU0D,KAAe,KAEnCnH,EAAUyD,IAQjB4E,GAAQ,SAASC,GACnB,GAAIC,GAAWC,EAAWC,CAE1B,OADAH,GAAQI,GAAaJ,GAChBA,IAGDK,GAAiBL,GAGF,UAAfA,EAAMX,MAAoB/E,EAAYU,WAAY,EAC7CyD,GAAcW,MACnBC,KAAM,QACNC,KAAM,mBAGVW,EAAYlJ,KAAYiJ,GACxBM,GAAmBxJ,KAAKyJ,KAAMN,GACX,SAAfD,EAAMX,OACRc,EAAMK,GAAoBpF,GAC1B8E,EAAYC,EAAIM,KAChBpF,EAAqB8E,EAAIO,WAEpBR,GAnBP,QAyBES,GAAU,WACZ,GAAIC,GAAgBtG,EAAYM,SAKhC,IAJAyD,KACiC,iBAAtB/D,GAAYW,QACrBX,EAAYW,OAAQ,GAElBX,EAAYM,YAAcgG,GAAiBtG,EAAYM,aAAc,EACvEN,EAAYW,OAAQ,EACpBwD,GAAcW,MACZC,KAAM,QACNC,KAAM,wBAEH,KAAKb,GAAcoC,mBAA4C,OAAvBvG,EAAYC,OAAiB,CAC1E,GAAIuG,GAAUhE,EAAcO,gBACL,iBAAZyD,IAAwBA,GAAW,IAC5CxF,EAAqBzH,EAAY,WACQ,iBAA5ByG,GAAYS,cACrBT,EAAYS,aAAc,GAExBT,EAAYS,eAAgB,GAC9B0D,GAAcW,MACZC,KAAM,QACNC,KAAM,uBAGTwB,IAELxG,EAAYU,SAAU,EACtB+F,OAOAC,GAAW,WACbvC,GAAcwC,YACdxC,GAAcyC,OACdzC,GAAcW,KAAK,WACnB+B,KACA1C,GAAcmB,OAMZwB,GAAW,SAASC,EAAQZ,GAC9B,GAAIa,EACJ,IAAsB,gBAAXD,IAAuBA,GAA0B,mBAATZ,GACjDa,EAAUD,EACV5C,GAAcwC,gBACT,CAAA,GAAsB,gBAAXI,KAAuBA,EAIvC,MAHAC,MACAA,EAAQD,GAAUZ,EAIpB,IAAK,GAAIc,KAAcD,GACK,gBAAfC,IAA2BA,GAAc3L,EAAQkB,KAAKwK,EAASC,IAA8C,gBAAxBD,GAAQC,IAA4BD,EAAQC,KAC1InG,EAAUmG,GAAcC,GAAgBF,EAAQC,MAQlDE,GAAa,SAASJ,GACF,mBAAXA,IACTpJ,EAAqBmD,GACrBC,EAAqB,MACM,gBAAXgG,IAAuBzL,EAAQkB,KAAKsE,EAAWiG,UACxDjG,GAAUiG,IAOjBK,GAAW,SAASL,GACtB,MAAsB,mBAAXA,GACF3J,EAAU0D,GACU,gBAAXiG,IAAuBzL,EAAQkB,KAAKsE,EAAWiG,GACxDjG,EAAUiG,GADZ,QAQLM,GAAS,SAASC,GACpB,GAAMA,GAAgC,IAArBA,EAAQlL,SAAzB,CAGIpD,IACFuO,GAAavO,EAAiBwJ,EAAce,aACxCvK,IAAoBsO,GACtBC,GAAavO,EAAiBwJ,EAAcc,aAGhDtK,EAAkBsO,EAClBE,GAAUF,EAAS9E,EAAcc,WACjC,IAAImE,GAAWH,EAAQI,aAAa,UAAYlF,EAAciB,KAC9D,IAAwB,gBAAbgE,IAAyBA,EAAU,CAC5C,GAAIE,GAAaC,GAAe5H,EAAYC,OACxC0H,IACFA,EAAWE,aAAa,QAASJ,GAGrC,GAAIK,GAAgBtF,EAAcgB,mBAAoB,GAAyC,YAAjCuE,GAAUT,EAAS,SACjFU,IAAeF,GACfG,OAMEC,GAAQ,WACV,GAAIP,GAAaC,GAAe5H,EAAYC,OACxC0H,KACFA,EAAWQ,gBAAgB,SAC3BR,EAAWS,MAAMC,KAAO,MACxBV,EAAWS,MAAMtI,IAAM,UACvB6H,EAAWS,MAAME,MAAQ,MACzBX,EAAWS,MAAMG,OAAS,OAExBvP,IACFuO,GAAavO,EAAiBwJ,EAAcc,YAC5CiE,GAAavO,EAAiBwJ,EAAce,aAC5CvK,EAAkB,OAOlBwP,GAAiB,WACnB,MAAOxP,IAAmB,MAMxB6K,GAAkB,SAAS4E,GAC7B,MAAqB,gBAAPA,IAAmBA,GAAM,+BAA+BjJ,KAAKiJ,IAMzE3C,GAAe,SAASJ,GAC1B,GAAInB,EAOJ,IANqB,gBAAVmB,IAAsBA,GAC/BnB,EAAYmB,EACZA,MAC0B,gBAAVA,IAAsBA,GAA+B,gBAAfA,GAAMX,MAAqBW,EAAMX,OACvFR,EAAYmB,EAAMX,MAEfR,EAAL,CAGAA,EAAYA,EAAUI,eACjBe,EAAMxI,SAAW,4BAA4BsC,KAAK+E,IAA4B,UAAdA,GAAwC,oBAAfmB,EAAMV,QAClGU,EAAMxI,OAASjE,GAEjBwD,EAAQiJ,GACNX,KAAMR,EACNrH,OAAQwI,EAAMxI,QAAUlE,GAAmB,KAC3C0P,cAAehD,EAAMgD,eAAiB,KACtCC,cAAe3I,GAAeA,EAAYC,QAAU,KACpD2I,UAAWlD,EAAMkD,WAAa9N,KAAU,MAE1C,IAAI+N,GAAM3H,EAAewE,EAAMX,KAsC/B,OArCmB,UAAfW,EAAMX,MAAoBW,EAAMV,MAAQ6D,IAC1CA,EAAMA,EAAInD,EAAMV,OAEd6D,IACFnD,EAAMoD,QAAUD,GAEC,UAAfnD,EAAMX,MACRtI,EAAQiJ,GACNxI,OAAQ,KACRgD,QAASF,EAAYE,UAGN,UAAfwF,EAAMX,OACJ9C,EAAkCzC,KAAKkG,EAAMV,OAC/CvI,EAAQiJ,GACNxI,OAAQ,KACR6L,eAAgBnI,IAGhB2B,EAAyC/C,KAAKkG,EAAMV,OACtDvI,EAAQiJ,GACNxF,QAASF,EAAYE,WAIR,SAAfwF,EAAMX,OACRW,EAAMsD,eACJC,QAAS9E,GAAc8E,QACvBtC,UAAWxC,GAAcwC,YAGV,cAAfjB,EAAMX,OACRW,EAAQwD,GAAyBxD,EAAO3E,IAEtC2E,EAAMxI,SAAWwI,EAAMgD,gBACzBhD,EAAMgD,cAAgBS,GAAkBzD,EAAMxI,SAEzCkM,GAAc1D,KAMnByD,GAAoB,SAASE,GAC/B,GAAIC,GAAkBD,GAAYA,EAAS3B,cAAgB2B,EAAS3B,aAAa,wBACjF,OAAO4B,GAAkBnQ,EAAUoQ,eAAeD,GAAmB,MAMnEF,GAAgB,SAAS1D,GAC3B,GAAIA,GAAS,8CAA8ClG,KAAKkG,EAAMX,MAAO,CAC3E,GAAIyE,GAAa9D,EAAMxI,OACnBuM,EAA6B,eAAf/D,EAAMX,MAAyBW,EAAMgD,cAAgBhD,EAAMgD,cAAgB5P,EACzF4Q,EAA2B,cAAfhE,EAAMX,MAAwBW,EAAMgD,cAAgBhD,EAAMgD,cAAgB5P,EACtF6Q,EAAMC,GAAoBJ,GAC1BK,EAAa3Q,EAAQ2Q,YAAc3Q,EAAQ4Q,SAAW,EACtDC,EAAY7Q,EAAQ6Q,WAAa7Q,EAAQ8Q,SAAW,EACpDC,EAAa9Q,EAAU+Q,KAAKD,WAAa9Q,EAAUgR,gBAAgBF,WACnEG,EAAYjR,EAAU+Q,KAAKE,UAAYjR,EAAUgR,gBAAgBC,UACjEC,EAAQV,EAAItB,MAAiC,gBAAlB3C,GAAM4E,QAAuB5E,EAAM4E,QAAU,GACxEC,EAAQZ,EAAI7J,KAAgC,gBAAlB4F,GAAM8E,QAAuB9E,EAAM8E,QAAU,GACvEC,EAAUJ,EAAQJ,EAClBS,EAAUH,EAAQH,EAClBN,EAAUD,EAAaY,EACvBT,EAAUD,EAAYW,EACtBC,EAAmC,gBAApBjF,GAAMkF,UAAyBlF,EAAMkF,UAAY,EAChEC,EAAmC,gBAApBnF,GAAMoF,UAAyBpF,EAAMoF,UAAY,QAC7DpF,GAAM4E,cACN5E,GAAM8E,QACb/N,EAAQiJ,GACN8D,WAAYA,EACZC,YAAaA,EACbC,UAAWA,EACXI,QAASA,EACTE,QAASA,EACTK,MAAOA,EACPE,MAAOA,EACPE,QAASA,EACTC,QAASA,EACTK,EAAGN,EACHO,EAAGN,EACHE,UAAWD,EACXG,UAAWD,EACXI,QAAS,EACTC,QAAS,EACTC,OAAQ,EACRC,OAAQ,IAGZ,MAAO1F,IAQL2F,GAAsB,SAAS3F,GACjC,GAAInB,GAAYmB,GAA+B,gBAAfA,GAAMX,MAAqBW,EAAMX,MAAQ,EACzE,QAAQ,gCAAgCvF,KAAK+E,IAQ3C+G,GAAoB,SAASC,EAAMC,EAASxO,EAAMyO,GAChDA,EACFlS,EAAY,WACVgS,EAAKG,MAAMF,EAASxO,IACnB,GAEHuO,EAAKG,MAAMF,EAASxO,IASpBgJ,GAAqB,SAASN,GAChC,GAAuB,gBAAVA,IAAsBA,GAASA,EAAMX,KAAlD,CAGA,GAAI0G,GAAQJ,GAAoB3F,GAC5BiG,EAAuB9K,EAAU,SACjC+K,EAAuB/K,EAAU6E,EAAMX,UACvC8G,EAAWF,EAAqBG,OAAOF,EAC3C,IAAIC,GAAYA,EAAS1O,OAAQ,CAC/B,GAAIT,GAAGC,EAAK4O,EAAMC,EAAS7F,EAAWoG,EAAkB9F,IACxD,KAAKvJ,EAAI,EAAGC,EAAMkP,EAAS1O,OAAYR,EAAJD,EAASA,IAC1C6O,EAAOM,EAASnP,GAChB8O,EAAUO,EACU,gBAATR,IAA8C,kBAAlBrS,GAAQqS,KAC7CA,EAAOrS,EAAQqS,IAEG,gBAATA,IAAqBA,GAAoC,kBAArBA,GAAKS,cAClDR,EAAUD,EACVA,EAAOA,EAAKS,aAEM,kBAATT,KACT5F,EAAYlJ,KAAYiJ,GACxB4F,GAAkBC,EAAMC,GAAW7F,GAAa8F,IAItD,MAAOxF,QAOLgG,GAAkC,SAASvG,GAC7C,GAAIwG,GAAc,IAIlB,QAHItM,KAAkB,GAAS8F,GAAwB,UAAfA,EAAMX,MAAoBW,EAAMV,MAAoE,KAA5DjD,EAAoCrE,QAAQgI,EAAMV,SAChIkH,GAAc,GAETA,GAOLnG,GAAmB,SAASL,GAC9B,GAAI4B,GAAU5B,EAAMxI,QAAUlE,GAAmB,KAC7CmT,EAAgC,QAAlBzG,EAAM0G,OAExB,cADO1G,GAAM0G,QACL1G,EAAMX,MACb,IAAK,QACJ,GAAImH,GAA6B,oBAAfxG,EAAMV,MAA8BiH,GAAgCvG,EAC3D,kBAAhBwG,KACTlM,EAAYM,UAAY4L,GAEwB,KAA9ClK,EAAsBtE,QAAQgI,EAAMV,MACtCvI,EAAQuD,GACNI,SAAyB,mBAAfsF,EAAMV,KAChB3E,SAAyB,mBAAfqF,EAAMV,KAChBzE,YAA4B,sBAAfmF,EAAMV,KACnBxE,SAAyB,mBAAfkF,EAAMV,KAChBvE,YAA4B,sBAAfiF,EAAMV,KACnBtE,QAAwB,kBAAfgF,EAAMV,KACfrE,OAAO,IAEe,qBAAf+E,EAAMV,OACfjM,EAAgB2M,EAAMR,WACtBzI,EAAQuD,GACNI,UAAU,EACVC,UAAU,EACVE,aAAa,EACbC,UAAU,EACVC,aAAa,EACbC,SAAS,EACTC,OAAO,KAGX0L,IACA,MAED,KAAK,QACJtT,EAAgB2M,EAAMR,UACtB,IAAIoH,GAAiBtM,EAAYS,eAAgB,CACjDhE,GAAQuD,GACNI,UAAU,EACVC,UAAU,EACVC,WAAW,EACXC,aAAa,EACbC,UAAU,EACVC,aAAa,EACbC,QAAS4L,EACT3L,OAAQ2L,IAEVD,IACA,MAED,KAAK,aACJpT,EAAcqO,CACd,MAED,KAAK,OACJ,GAAIiF,GAAaC,EAAanD,EAAW3D,EAAMgD,eACzC5H,EAAU,eAAgBA,EAAU,eAAkBuI,IAAamD,EAAcnD,EAASoD,OAASpD,EAASqD,WAAarD,EAASsD,aAAeJ,EAAclD,EAASoD,OAASpD,EAASkD,aAAelD,EAASuD,YACtNlH,EAAMsD,cAAcrC,YACpBjB,EAAMsD,cAAcC,QAAQ,aAAcsD,GACtCC,IAAgBD,GAClB7G,EAAMsD,cAAcC,QAAQ,YAAauD,KAEjC1L,EAAU,eAAiB4E,EAAMxI,SAAWqP,EAAc7G,EAAMxI,OAAOwK,aAAa,0BAC9FhC,EAAMsD,cAAcrC,YACpBjB,EAAMsD,cAAcC,QAAQ,aAAcsD,GAE5C,MAED,KAAK,YACJM,GAA0BnH,GAC1BvB,GAAcwC,YACVW,GAAWA,IAAYwF,MAAwBxF,EAAQyF,OACzDzF,EAAQyF,OAEV,MAED,KAAK,aACJ5I,GAAc4I,MAAMzF,GAChB9E,EAAcS,gBAAiB,GAAQkJ,IACrC7E,GAAWA,IAAY5B,EAAMgD,gBAAkB9K,EAAa8H,EAAMgD,cAAepB,IACnF0F,GAAgBvQ,KAAYiJ,GAC1BX,KAAM,aACNkI,SAAS,EACTC,YAAY,KAGhBF,GAAgBvQ,KAAYiJ,GAC1BX,KAAM,eAGV,MAED,KAAK,YACJZ,GAAcyC,OACVpE,EAAcS,gBAAiB,GAAQkJ,IACrC7E,GAAWA,IAAY5B,EAAMgD,gBAAkB9K,EAAa8H,EAAMgD,cAAepB,IACnF0F,GAAgBvQ,KAAYiJ,GAC1BX,KAAM,aACNkI,SAAS,EACTC,YAAY,KAGhBF,GAAgBvQ,KAAYiJ,GAC1BX,KAAM,cAGV,MAED,KAAK,aACJyC,GAAUF,EAAS9E,EAAce,aAC7Bf,EAAcS,gBAAiB,GAAQkJ,GACzCa,GAAgBvQ,KAAYiJ,GAC1BX,KAAMW,EAAMX,KAAKpJ,MAAM,KAG3B,MAED,KAAK,WACJ4L,GAAaD,EAAS9E,EAAce,aAChCf,EAAcS,gBAAiB,GAAQkJ,GACzCa,GAAgBvQ,KAAYiJ,GAC1BX,KAAMW,EAAMX,KAAKpJ,MAAM,KAG3B,MAED,KAAK,SACJ1C,EAAc,KACVuJ,EAAcS,gBAAiB,GAAQkJ,GACzCa,GAAgBvQ,KAAYiJ,GAC1BX,KAAMW,EAAMX,KAAKpJ,MAAM,KAG3B,MAED,KAAK,aACA6G,EAAcS,gBAAiB,GAAQkJ,GACzCa,GAAgBvQ,KAAYiJ,GAC1BX,KAAMW,EAAMX,KAAKpJ,MAAM,MAK7B,MAAI,8CAA8C6D,KAAKkG,EAAMX,OACpD,EADT,QAQE8H,GAA4B,SAASM,GACvC,GAAIA,EAAeC,QAAUD,EAAeC,OAAOjQ,OAAS,EAAG,CAC7D,GAAIkQ,GAAajQ,EAAU+P,EAC3B1Q,GAAQ4Q,GACNtI,KAAM,QACNC,KAAM,0BAEDqI,GAAWC,QAClB/T,EAAY,WACV4K,GAAcW,KAAKuI,IAClB,KASHL,GAAkB,SAAStH,GAC7B,GAAMA,GAA+B,gBAAfA,GAAMX,MAAqBW,EAAjD,CAGA,GAAIrJ,GAAGa,EAASwI,EAAMxI,QAAU,KAAMqQ,EAAMrQ,GAAUA,EAAOY,eAAiB3E,EAAWqU,GACvFC,KAAMF,EAAIG,aAAexU,EACzByU,WAAW,EACXT,YAAY,EACZU,OAAuB,UAAflI,EAAMX,KAAmB,EAAI,EACrC8I,OAA+B,gBAAhBnI,GAAMoI,MAAqBpI,EAAMoI,MAAQ,EAA4B,gBAAjBpI,GAAMmI,OAAsBnI,EAAMmI,OAASN,EAAIQ,YAAc,EAAI,GACnI/Q,EAAOP,EAAQ+Q,EAAU9H,EACvBxI,IAGDqQ,EAAIQ,aAAe7Q,EAAO8Q,gBAC5BhR,GAASA,EAAK+H,KAAM/H,EAAK2Q,UAAW3Q,EAAKkQ,WAAYlQ,EAAKyQ,KAAMzQ,EAAK4Q,OAAQ5Q,EAAK8M,QAAS9M,EAAKgN,QAAShN,EAAKyN,QAASzN,EAAK0N,QAAS1N,EAAKiR,QAASjR,EAAKkR,OAAQlR,EAAKmR,SAAUnR,EAAKoR,QAASpR,EAAK6Q,OAAQ7Q,EAAK0L,eAC/MrM,EAAIkR,EAAIQ,YAAY,eAChB1R,EAAEgS,iBACJhS,EAAEgS,eAAe3C,MAAMrP,EAAGW,GAC1BX,EAAE+P,QAAU,KACZlP,EAAO8Q,cAAc3R,OAoBvBiS,GAA8B,WAChC,GAAI9H,GAAUhE,EAAcO,gBAC5B,IAAuB,gBAAZyD,IAAwBA,GAAW,EAAG,CAC/C,GAAI+H,GAAWC,KAAKC,IAAI,IAAKjI,EAAU,IACnCkI,EAAoBlM,EAAca,YAAc,kBACpDpC,GAA4BtH,EAAa,WACvC,GAAImC,GAAK3C,EAAUoQ,eAAemF,EAC9BC,IAAkB7S,KACpBuQ,KACArM,EAAYS,YAAc,KAC1B0D,GAAcW,MACZC,KAAM,QACNC,KAAM,oBAGTuJ,KAOHK,GAAoB,WACtB,GAAIC,GAAY1V,EAAU+C,cAAc,MASxC,OARA2S,GAAUpG,GAAKjG,EAAcW,YAC7B0L,EAAUC,UAAYtM,EAAcY,eACpCyL,EAAUzG,MAAM2G,SAAW,WAC3BF,EAAUzG,MAAMC,KAAO,MACvBwG,EAAUzG,MAAMtI,IAAM,UACtB+O,EAAUzG,MAAME,MAAQ,MACxBuG,EAAUzG,MAAMG,OAAS,MACzBsG,EAAUzG,MAAM1E,OAAS,GAAKsL,GAAexM,EAAckB,QACpDmL,GAMLjH,GAAiB,SAASqH,GAE5B,IADA,GAAItH,GAAasH,GAAeA,EAAYlR,WACrC4J,GAAsC,WAAxBA,EAAWuH,UAAyBvH,EAAW5J,YAClE4J,EAAaA,EAAW5J,UAE1B,OAAO4J,IAAc,MAQnBlB,GAAY,WACd,GAAI9J,GAAKsS,EAAcjP,EAAYC,OAAQ4O,EAAYjH,GAAeqH,EACtE,KAAKA,EAAa,CAChB,GAAIE,GAAoBC,GAAuBlW,EAAQyJ,SAASC,KAAMJ,GAClE6M,EAAwC,UAAtBF,EAAgC,OAAS,MAC3DG,EAAYC,GAAM9S,GACpBwI,UAAWd,GAAcjE,SACxBsC,IACCgN,EAAShN,EAAcC,QAAUgN,GAAWjN,EAAcC,QAASD,EACvEqM,GAAYD,IACZ,IAAIc,GAAkBvW,EAAU+C,cAAc,MAC9C2S,GAAUc,YAAYD,GACtBvW,EAAU+Q,KAAKyF,YAAYd,EAC3B,IAAIe,GAASzW,EAAU+C,cAAc,OACjC2T,EAA0C,YAA3B7P,EAAYG,UAC/ByP,GAAOjD,UAAY,eAAiBnK,EAAca,YAAc,WAAab,EAAca,YAAc,iCAAwCwM,EAAe,uDAAyD,8CAAgDL,EAAS,KAAO,KAAOK,EAAe,8BAAgCL,EAAS,MAAQ,IAAM,0CAA4CL,EAAoB,2CAAkDE,EAAkB,gHAAiIC,EAAY,eAAsB9M,EAAca,YAAc,0CACzqB4L,EAAcW,EAAOE,WACrBF,EAAS,KACThU,EAAQqT,GAAa9K,cAAgBA,GACrC0K,EAAUkB,aAAad,EAAaS,GACpCpB,KAYF,MAVKW,KACHA,EAAc9V,EAAUqJ,EAAca,aAClC4L,IAAgBtS,EAAMsS,EAAY9R,UACpC8R,EAAcA,EAAYtS,EAAM,KAE7BsS,GAAeJ,IAClBI,EAAcJ,EAAUiB,aAG5B9P,EAAYC,OAASgP,GAAe,KAC7BA,GAMLpI,GAAc,WAChB,GAAIoI,GAAcjP,EAAYC,MAC9B,IAAIgP,EAAa,CACf,GAAItH,GAAaC,GAAeqH,EAC5BtH,KAC6B,YAA3B3H,EAAYG,YAA4B,cAAgB8O,IAC1DA,EAAY7G,MAAM4H,QAAU,OAC5B,QAAUC,KACR,GAA+B,IAA3BhB,EAAY/P,WAAkB,CAChC,IAAK,GAAIrC,KAAQoS,GACkB,kBAAtBA,GAAYpS,KACrBoS,EAAYpS,GAAQ,KAGpBoS,GAAYlR,YACdkR,EAAYlR,WAAWmS,YAAYjB,GAEjCtH,EAAW5J,YACb4J,EAAW5J,WAAWmS,YAAYvI,OAGpCpO,GAAY0W,EAAiB,SAI7BhB,EAAYlR,YACdkR,EAAYlR,WAAWmS,YAAYjB,GAEjCtH,EAAW5J,YACb4J,EAAW5J,WAAWmS,YAAYvI,KAIxC0E,KACArM,EAAYW,MAAQ,KACpBX,EAAYC,OAAS,KACrBD,EAAYS,YAAc,KAC1B1H,EAAgBD,IAShBoN,GAAsB,SAASiK,GACjC,GAAIC,MAAkBhK,IACtB,IAA0B,gBAAb+J,IAAyBA,EAAtC,CAGA,IAAK,GAAIlJ,KAAckJ,GACrB,GAAIlJ,GAAc3L,EAAQkB,KAAK2T,EAAUlJ,IAA+C,gBAAzBkJ,GAASlJ,IAA4BkJ,EAASlJ,GAC3G,OAAQA,EAAWtC,eAClB,IAAK,aACL,IAAK,OACL,IAAK,WACL,IAAK,aACJyL,EAAYC,KAAOF,EAASlJ,GAC5Bb,EAAUiK,KAAOpJ,CACjB,MAED,KAAK,YACL,IAAK,OACL,IAAK,WACL,IAAK,aACJmJ,EAAYE,KAAOH,EAASlJ,GAC5Bb,EAAUkK,KAAOrJ,CACjB,MAED,KAAK,kBACL,IAAK,WACL,IAAK,MACL,IAAK,WACL,IAAK,UACL,IAAK,YACJmJ,EAAYG,IAAMJ,EAASlJ,GAC3Bb,EAAUmK,IAAMtJ,EAQtB,OACEd,KAAMiK,EACNhK,UAAWA,KASX8C,GAA2B,SAASsH,EAAapK,GACnD,GAA6B,gBAAhBoK,KAA4BA,GAAoC,gBAAdpK,KAA0BA,EACvF,MAAOoK,EAET,IAAIC,KACJ,KAAK,GAAI5T,KAAQ2T,GACf,GAAIlV,EAAQkB,KAAKgU,EAAa3T,GAC5B,GAAa,WAATA,EAAmB,CACrB4T,EAAW5T,GAAQ2T,EAAY3T,GAAQ2T,EAAY3T,GAAMlB,UACzD,KAAK,GAAIe,GAAI,EAAGC,EAAM8T,EAAW5T,GAAMM,OAAYR,EAAJD,EAASA,IACtD+T,EAAW5T,GAAMH,GAAGqK,OAASX,EAAUqK,EAAW5T,GAAMH,GAAGqK,YAExD,IAAa,YAATlK,GAA+B,SAATA,EAC/B4T,EAAW5T,GAAQ2T,EAAY3T,OAC1B,CACL4T,EAAW5T,KACX,IAAI6T,GAAUF,EAAY3T,EAC1B,KAAK,GAAIoK,KAAcyJ,GACjBzJ,GAAc3L,EAAQkB,KAAKkU,EAASzJ,IAAe3L,EAAQkB,KAAK4J,EAAWa,KAC7EwJ,EAAW5T,GAAMuJ,EAAUa,IAAeyJ,EAAQzJ,IAM5D,MAAOwJ,IAULhB,GAAa,SAASkB,EAAM/M,GAC9B,GAAIf,GAAuB,MAAXe,GAAmBA,GAAWA,EAAQf,aAAc,CACpE,OAAIA,IAC4B,KAAtB8N,EAAKjT,QAAQ,KAAc,IAAM,KAAO,WAAa5C,IAEtD,IAUPyU,GAAQ,SAAS3L,GACnB,GAAIlH,GAAGC,EAAKiU,EAAQC,EAASC,EAAM,GAAIC,IAQvC,IAPInN,EAAQlB,iBAC4B,gBAA3BkB,GAAQlB,eACjBmO,GAAYjN,EAAQlB,gBACuB,gBAA3BkB,GAAQlB,gBAA+B,UAAYkB,GAAQlB,iBAC3EmO,EAAUjN,EAAQlB,iBAGlBmO,GAAWA,EAAQ1T,OACrB,IAAKT,EAAI,EAAGC,EAAMkU,EAAQ1T,OAAYR,EAAJD,EAASA,IACzC,GAAIpB,EAAQkB,KAAKqU,EAASnU,IAAMmU,EAAQnU,IAA4B,gBAAfmU,GAAQnU,GAAiB,CAE5E,GADAkU,EAASI,GAAeH,EAAQnU,KAC3BkU,EACH,QAEF,IAAe,MAAXA,EAAgB,CAClBG,EAAuB5T,OAAS,EAChC4T,EAAuBlM,KAAK+L,EAC5B,OAEFG,EAAuBlM,KAAK6G,MAAMqF,GAA0BH,EAAQ,KAAOA,EAAQ1X,EAAQyJ,SAASsO,SAAW,KAAOL,IAgB5H,MAZIG,GAAuB5T,SACzB2T,GAAO,kBAAoB7W,EAAoB8W,EAAuBzO,KAAK,OAEzEsB,EAAQd,0BAA2B,IACrCgO,IAAQA,EAAM,IAAM,IAAM,+BAEO,gBAAxBlN,GAAQP,aAA4BO,EAAQP,cACrDyN,IAAQA,EAAM,IAAM,IAAM,eAAiB7W,EAAoB2J,EAAQP,cAExC,gBAAtBO,GAAQqB,WAA0BrB,EAAQqB,YACnD6L,IAAQA,EAAM,IAAM,IAAM,aAAe7W,EAAoB2J,EAAQqB,YAEhE6L,GASLE,GAAiB,SAASE,GAC5B,GAAmB,MAAfA,GAAuC,KAAhBA,EACzB,MAAO,KAGT,IADAA,EAAcA,EAAY7O,QAAQ,aAAc,IAC5B,KAAhB6O,EACF,MAAO,KAET,IAAIC,GAAgBD,EAAYxT,QAAQ,KACxCwT,GAAgC,KAAlBC,EAAuBD,EAAcA,EAAYvV,MAAMwV,EAAgB,EACrF,IAAIC,GAAYF,EAAYxT,QAAQ,IAEpC,OADAwT,GAA4B,KAAdE,EAAmBF,EAAgC,KAAlBC,GAAsC,IAAdC,EAAkB,KAAOF,EAAYvV,MAAM,EAAGyV,GACjHF,GAAuD,SAAxCA,EAAYvV,MAAM,IAAIgJ,cAChC,KAEFuM,GAAe,MAQpB9B,GAAyB,WAC3B,GAAIiC,GAAqB,SAASC,GAChC,GAAI5U,GAAGC,EAAKkJ,EAAK0L,IAIjB,IAHuB,gBAAZD,KACTA,GAAYA,IAEW,gBAAZA,KAAwBA,GAAqC,gBAAnBA,GAAQnU,OAC7D,MAAOoU,EAET,KAAK7U,EAAI,EAAGC,EAAM2U,EAAQnU,OAAYR,EAAJD,EAASA,IACzC,GAAIpB,EAAQkB,KAAK8U,EAAS5U,KAAOmJ,EAAMmL,GAAeM,EAAQ5U,KAAM,CAClE,GAAY,MAARmJ,EAAa,CACf0L,EAAapU,OAAS,EACtBoU,EAAa1M,KAAK,IAClB,OAEgC,KAA9B0M,EAAa7T,QAAQmI,IACvB0L,EAAa1M,KAAKgB,GAIxB,MAAO0L,GAET,OAAO,UAASC,EAAeC,GAC7B,GAAIC,GAAYV,GAAeS,EAAchP,QAC3B,QAAdiP,IACFA,EAAYF,EAEd,IAAI9O,GAAiB2O,EAAmBI,EAAc/O,gBAClD/F,EAAM+F,EAAevF,MACzB,IAAIR,EAAM,EAAG,CACX,GAAY,IAARA,GAAmC,MAAtB+F,EAAe,GAC9B,MAAO,QAET,IAA8C,KAA1CA,EAAehF,QAAQ8T,GACzB,MAAY,KAAR7U,GAAa6U,IAAkBE,EAC1B,aAEF,SAGX,MAAO,YASP5E,GAAqB,WACvB,IACE,MAAO3T,GAAUwY,cACjB,MAAOjT,GACP,MAAO,QASP8I,GAAY,SAASF,EAASmF,GAChC,GAAImF,GAAGC,EAAI/C,EAAWgD,IAItB,IAHqB,gBAAVrF,IAAsBA,IAC/BqF,EAAarF,EAAMtO,MAAM,QAEvBmJ,GAAgC,IAArBA,EAAQlL,UAAkB0V,EAAW3U,OAAS,EAAG,CAE9D,IADA2R,GAAa,KAAOxH,EAAQwH,WAAa,IAAM,KAAKzM,QAAQ,cAAe,KACtEuP,EAAI,EAAGC,EAAKC,EAAW3U,OAAY0U,EAAJD,EAAQA,IACW,KAAjD9C,EAAUpR,QAAQ,IAAMoU,EAAWF,GAAK,OAC1C9C,GAAagD,EAAWF,GAAK,IAGjC9C,GAAYA,EAAUzM,QAAQ,aAAc,IACxCyM,IAAcxH,EAAQwH,YACxBxH,EAAQwH,UAAYA,GAGxB,MAAOxH,IAQLC,GAAe,SAASD,EAASmF,GACnC,GAAImF,GAAGC,EAAI/C,EAAWgD,IAItB,IAHqB,gBAAVrF,IAAsBA,IAC/BqF,EAAarF,EAAMtO,MAAM,QAEvBmJ,GAAgC,IAArBA,EAAQlL,UAAkB0V,EAAW3U,OAAS,GACvDmK,EAAQwH,UAAW,CAErB,IADAA,GAAa,IAAMxH,EAAQwH,UAAY,KAAKzM,QAAQ,cAAe,KAC9DuP,EAAI,EAAGC,EAAKC,EAAW3U,OAAY0U,EAAJD,EAAQA,IAC1C9C,EAAYA,EAAUzM,QAAQ,IAAMyP,EAAWF,GAAK,IAAK,IAE3D9C,GAAYA,EAAUzM,QAAQ,aAAc,IACxCyM,IAAcxH,EAAQwH,YACxBxH,EAAQwH,UAAYA,GAI1B,MAAOxH,IAULS,GAAY,SAASjM,EAAIe,GAC3B,GAAI4P,GAAQ1S,EAAkB+B,EAAI,MAAMiW,iBAAiBlV,EACzD,OAAa,WAATA,GACG4P,GAAmB,SAAVA,GACQ,MAAhB3Q,EAAGoT,SAKJzC,EAJM,WAYX7C,GAAsB,SAAS9N,GACjC,GAAI6N,IACFtB,KAAM,EACNvI,IAAK,EACLwI,MAAO,EACPC,OAAQ,EAEV,IAAIzM,EAAGkW,sBAAuB,CAC5B,GAAIC,GAASnW,EAAGkW,wBACZE,EAAchZ,EAAQgZ,YACtBC,EAAcjZ,EAAQiZ,YACtBC,EAAkBjZ,EAAUgR,gBAAgBkI,YAAc,EAC1DC,EAAiBnZ,EAAUgR,gBAAgBoI,WAAa,EACxDC,EAAiB,EACjBC,EAAgB,CACpB,IAA8C,aAA1C1K,GAAU5O,EAAU+Q,KAAM,YAA4B,CACxD,GAAIwI,GAAWvZ,EAAU+Q,KAAK8H,wBAC1BW,EAAWxZ,EAAUgR,gBAAgB6H,uBACzCQ,GAAiBE,EAASrK,KAAOsK,EAAStK,MAAQ,EAClDoK,EAAgBC,EAAS5S,IAAM6S,EAAS7S,KAAO,EAEjD6J,EAAItB,KAAO4J,EAAO5J,KAAO6J,EAAcE,EAAkBI,EACzD7I,EAAI7J,IAAMmS,EAAOnS,IAAMqS,EAAcG,EAAiBG,EACtD9I,EAAIrB,MAAQ,SAAW2J,GAASA,EAAO3J,MAAQ2J,EAAOW,MAAQX,EAAO5J,KACrEsB,EAAIpB,OAAS,UAAY0J,GAASA,EAAO1J,OAAS0J,EAAOY,OAASZ,EAAOnS,IAE3E,MAAO6J,IAQLgF,GAAoB,SAAS7S,GAC/B,IAAKA,EACH,OAAO,CAET,IAAIgX,GAAS/Y,EAAkB+B,EAAI,KACnC,KAAKgX,EACH,OAAO,CAET,IAAIC,GAAerY,EAAYoY,EAAOvK,QAAU,EAC5CyK,EAActY,EAAYoY,EAAOxK,OAAS,EAC1C2K,EAAYvY,EAAYoY,EAAOhT,MAAQ,EACvCoT,EAAaxY,EAAYoY,EAAOzK,OAAS,EACzC8K,EAAWJ,GAAgBC,GAAeC,GAAaC,EACvDE,EAAOD,EAAW,KAAOvJ,GAAoB9N,GAC7CuX,EAA+B,SAAnBP,EAAO9C,SAA4C,aAAtB8C,EAAOQ,aAA8BH,KAAcC,IAASL,GAAgBK,EAAK7K,OAAS,KAAOyK,GAAeI,EAAK9K,MAAQ,KAAO2K,GAAaG,EAAKtT,KAAO,KAAOoT,GAAcE,EAAK/K,MAAQ,GAC5O,OAAOgL,IAQLhH,GAA2B,WAC7B5S,EAAcuH,GACdA,EAAqB,EACrBnH,EAAeoH,GACfA,EAA4B,GAQ1BgH,GAAc,WAChB,GAAIN,EACJ,IAAI3O,IAAoB2O,EAAaC,GAAe5H,EAAYC,SAAU,CACxE,GAAI0J,GAAMC,GAAoB5Q,EAC9ByD,GAAQkL,EAAWS,OACjBE,MAAOqB,EAAIrB,MAAQ,KACnBC,OAAQoB,EAAIpB,OAAS,KACrBzI,IAAK6J,EAAI7J,IAAM,KACfuI,KAAMsB,EAAItB,KAAO,KACjB3E,OAAQ,GAAKsL,GAAexM,EAAckB,YAU5CsE,GAAiB,SAASuL,GACxBvT,EAAYW,SAAU,IACpBX,EAAYC,QAAsD,kBAArCD,GAAYC,OAAOuT,cAClDxT,EAAYC,OAAOuT,cAAcD,GAEjCvT,EAAYW,OAAQ,IAUtBqO,GAAiB,SAASyE,GAC5B,GAAI,qBAAqBjU,KAAKiU,GAC5B,MAAOA,EAET,IAAI/P,EAMJ,OALmB,gBAAR+P,IAAqB7Y,EAAO6Y,GAEb,gBAARA,KAChB/P,EAASsL,GAAezU,EAAUkZ,EAAK,MAFvC/P,EAAS+P,EAIc,gBAAX/P,GAAsBA,EAAS,QAQ3CwD,GAAkB,SAASwM,GAC7B,GAAIC,GAAe,eAUnB,OATuB,gBAAZD,IAAwBlR,EAAcU,kBAAmB,IAC9D5D,IACE,4BAA4BE,KAAKkU,KACnCA,EAAUA,EAAQrR,QAAQsR,EAAc,SAEjC,KAAKnU,KAAKkU,KACnBA,EAAUA,EAAQrR,QAAQsR,EAAc,QAGrCD,GAaL3P,GAAiB,SAAS6P,GAC5B,GAAIC,GAAuBC,EAAOC,EAAYzN,EAAgBtG,EAAYM,UAAW4L,EAAc,IAEnG,IADA0H,EAA4BA,KAA8B,EACtDhU,KAAkB,EACpBsM,GAAc,MACT,CACL,IACE4H,EAAQjb,EAAOmb,cAAgB,KAC/B,MAAO3X,GACP0X,GACE/O,KAAM3I,EAAE2I,KACR8D,QAASzM,EAAEyM,SAGf,GAAIgL,GAA4B,IAAnBA,EAAM1X,UAAqC,WAAnB0X,EAAM5E,SACzC,IACEhD,EAAc4H,EAAMG,aAAa,WACjC,MAAO5X,GACP6P,EAAc,SAEX,CACL,IACE2H,EAAwBza,SAASwX,QAAU,KAC3C,MAAOvU,GACPwX,EAAwB,MAEI,OAA1BA,GAAkCE,GAAkC,kBAApBA,EAAW/O,MAA4B,kDAAkDxF,KAAKuU,EAAWjL,QAAQnE,kBACnKuH,GAAc,IAQpB,MAJAlM,GAAYM,UAAY4L,EACpB5F,IAAkB4F,GAAgB0H,GACpCM,GAAoB/Z,GAEf+R,GAWLgI,GAAsB,SAAS9Z,GAQjC,QAAS+Z,GAAkBC,GACzB,GAAI7V,GAAU6V,EAAK5V,MAAM,SAEzB,OADAD,GAAQpB,OAAS,EACVoB,EAAQ+D,KAAK,KAEtB,QAAS+R,GAAcC,GACrB,QAASA,IAAwBA,EAAsBA,EAAoB3P,iBAAmB,0EAA0EnF,KAAK8U,IAA2D,kBAAnCA,EAAoB3Y,MAAM,MAEjO,QAAS4Y,GAAcC,GACjBA,IACFC,GAAW,EACPD,EAAOtU,UACTwU,EAAeP,EAAkBK,EAAOtU,WAErCwU,GAAgBF,EAAOG,cAC1BD,EAAeP,EAAkBK,EAAOG,cAEtCH,EAAOI,WACTC,EAAUR,EAAcG,EAAOI,YAzBrC,GAAIJ,GAAQM,EAAIC,EAAUN,GAAW,EAAOO,GAAY,EAAOH,GAAU,EAAOH,EAAe,EA6B/F,IAAIrb,EAAW4b,SAAW5b,EAAW4b,QAAQ9X,OAC3CqX,EAASnb,EAAW4b,QAAQ,mBAC5BV,EAAcC,GACVnb,EAAW4b,QAAQ,yBACrBR,GAAW,EACXC,EAAe,gBAEZ,IAAIrb,EAAW6b,WAAa7b,EAAW6b,UAAU/X,OACtD4X,EAAW1b,EAAW6b,UAAU,iCAChCV,EAASO,GAAYA,EAASI,cAC9BZ,EAAcC,OACT,IAA6B,mBAAlBpa,GAA+B,CAC/C4a,GAAY,CACZ,KACEF,EAAK,GAAI1a,GAAc,mCACvBqa,GAAW,EACXC,EAAeP,EAAkBW,EAAGM,YAAY,aAChD,MAAOC,GACP,IACEP,EAAK,GAAI1a,GAAc,mCACvBqa,GAAW,EACXC,EAAe,SACf,MAAOY,GACP,IACER,EAAK,GAAI1a,GAAc,iCACvBqa,GAAW,EACXC,EAAeP,EAAkBW,EAAGM,YAAY,aAChD,MAAOG,GACPP,GAAY,KAKpBhV,EAAYI,SAAWqU,KAAa,EACpCzU,EAAYK,SAAWqU,GAAgBha,EAAYga,GAAgBha,EAAYkG,GAC/EZ,EAAYE,QAAUwU,GAAgB,QACtC1U,EAAYG,WAAa0U,EAAU,SAAWG,EAAY,UAAYP,EAAW,WAAa,UAKhGP,IAAoB/Z,GAIpB4J,IAAe,EAMf,IAAII,IAAgB,WAClB,MAAM8B,gBAAgB9B,SAGqB,kBAAhCA,IAAcqR,eACvBrR,GAAcqR,cAAc9J,MAAMzF,KAAM3J,EAAMW,aAHvC,GAAIkH,IAaf/I,GAAgB+I,GAAe,WAC7BsI,MAAO,eACPgJ,UAAU,EACVC,cAAc,EACdC,YAAY,IASdxR,GAAcC,OAAS,WACrB,MAAOT,GAAQ+H,MAAMzF,KAAM3J,EAAMW,aAQnCkH,GAAcyR,MAAQ,WACpB,MAAO9R,IAAO4H,MAAMzF,KAAM3J,EAAMW,aAQlCkH,GAAcoC,gBAAkB,WAC9B,MAAOlC,IAAiBqH,MAAMzF,KAAM3J,EAAMW,aAQ5CkH,GAAcS,GAAK,WACjB,MAAON,IAAIoH,MAAMzF,KAAM3J,EAAMW,aAU/BkH,GAAcmB,IAAM,WAClB,MAAOH,IAAKuG,MAAMzF,KAAM3J,EAAMW,aAQhCkH,GAAc0H,SAAW,WACvB,MAAOrG,IAAWkG,MAAMzF,KAAM3J,EAAMW,aAQtCkH,GAAcW,KAAO,WACnB,MAAOW,IAAMiG,MAAMzF,KAAM3J,EAAMW,aAQjCkH,GAAc0R,OAAS,WACrB,MAAOxP,IAAQqF,MAAMzF,KAAM3J,EAAMW,aAQnCkH,GAAc2R,QAAU,WACtB,MAAOpP,IAASgF,MAAMzF,KAAM3J,EAAMW,aAQpCkH,GAAc8E,QAAU,WACtB,MAAOnC,IAAS4E,MAAMzF,KAAM3J,EAAMW,aASpCkH,GAAcwC,UAAY,WACxB,MAAOQ,IAAWuE,MAAMzF,KAAM3J,EAAMW,aAStCkH,GAAc4R,QAAU,WACtB,MAAO3O,IAASsE,MAAMzF,KAAM3J,EAAMW,aAWpCkH,GAAc4I,MAAQ5I,GAAc6R,SAAW,WAC7C,MAAO3O,IAAOqE,MAAMzF,KAAM3J,EAAMW,aAUlCkH,GAAcyC,KAAOzC,GAAc8R,WAAa,WAC9C,MAAO/N,IAAMwD,MAAMzF,KAAM3J,EAAMW,aAQjCkH,GAAcwN,cAAgB,WAC5B,MAAOnJ,IAAekD,MAAMzF,KAAM3J,EAAMW,YAK1C,IAAIiZ,IAAmB,EAWnBC,MAIAC,GAAoB,EAOpBC,MAaAC,KAIJ7Z,GAAQ+F,GACNQ,cAAc,GAMhB,IAAIuT,IAAqB,SAASC,GAChC,GAAIC,GAASxQ,IACbwQ,GAAOhO,GAAK,GAAKyN,KACjBC,GAAYM,EAAOhO,KACjBiO,SAAUD,EACVD,YACA3K,aAEE2K,GACFC,EAAOE,KAAKH,GAEdrS,GAAcS,GAAG,IAAK,SAASc,GAC7B,MAAO+Q,GAAO3R,KAAKY,KAErBvB,GAAcS,GAAG,UAAW,WAC1B6R,EAAOX,YAET3R,GAAc0R,UAMZe,GAAY,SAASrS,EAAWC,GAClC,GAAI9H,GAAGC,EAAK8H,EAAQC,KAAYmS,EAAOV,GAAYlQ,KAAKwC,IAAKoD,EAAWgL,GAAQA,EAAKhL,QACrF,KAAKgL,EACH,KAAM,IAAIvc,OAAM,gFAElB,IAAyB,gBAAdiK,IAA0BA,EACnCE,EAASF,EAAUI,cAAcxG,MAAM,WAClC,IAAyB,gBAAdoG,IAA0BA,GAAiC,mBAAbC,GAC9D,IAAK9H,IAAK6H,GACJjJ,EAAQkB,KAAK+H,EAAW7H,IAAmB,gBAANA,IAAkBA,GAA6B,kBAAjB6H,GAAU7H,IAC/EuJ,KAAKrB,GAAGlI,EAAG6H,EAAU7H,GAI3B,IAAI+H,GAAUA,EAAOtH,OAAQ,CAC3B,IAAKT,EAAI,EAAGC,EAAM8H,EAAOtH,OAAYR,EAAJD,EAASA,IACxC6H,EAAYE,EAAO/H,GAAG2F,QAAQ,MAAO,IACrCqC,EAAMH,IAAa,EACdsH,EAAStH,KACZsH,EAAStH,OAEXsH,EAAStH,GAAWM,KAAKL,EAQ3B,IANIE,EAAM/D,OAASX,EAAYW,OAC7BsF,KAAKnB,MACHC,KAAM,QACN0R,OAAQxQ,OAGRvB,EAAMvD,MAAO,CACf,IAAKzE,EAAI,EAAGC,EAAMqF,EAAsB7E,OAAYR,EAAJD,EAASA,IACvD,GAAIsD,EAAYgC,EAAsBtF,GAAG2F,QAAQ,UAAW,KAAM,CAChE4D,KAAKnB,MACHC,KAAM,QACNC,KAAMhD,EAAsBtF,GAC5B+Z,OAAQxQ,MAEV,OAGAlN,IAAkBD,GAAaqL,GAAcjE,UAAYnH,GAC3DkN,KAAKnB,MACHC,KAAM,QACNC,KAAM,mBACNC,UAAWd,GAAcjE,QACzBgF,WAAYnM,KAKpB,MAAOkN,OAML6Q,GAAa,SAASvS,EAAWC,GACnC,GAAI9H,GAAGC,EAAKyI,EAAYX,EAAQY,EAAkBwR,EAAOV,GAAYlQ,KAAKwC,IAAKoD,EAAWgL,GAAQA,EAAKhL,QACvG,KAAKA,EACH,MAAO5F,KAET,IAAyB,IAArBhJ,UAAUE,OACZsH,EAASxJ,EAAM4Q,OACV,IAAyB,gBAAdtH,IAA0BA,EAC1CE,EAASF,EAAUpG,MAAM,WACpB,IAAyB,gBAAdoG,IAA0BA,GAAiC,mBAAbC,GAC9D,IAAK9H,IAAK6H,GACJjJ,EAAQkB,KAAK+H,EAAW7H,IAAmB,gBAANA,IAAkBA,GAA6B,kBAAjB6H,GAAU7H,IAC/EuJ,KAAKX,IAAI5I,EAAG6H,EAAU7H,GAI5B,IAAI+H,GAAUA,EAAOtH,OACnB,IAAKT,EAAI,EAAGC,EAAM8H,EAAOtH,OAAYR,EAAJD,EAASA,IAGxC,GAFA6H,EAAYE,EAAO/H,GAAGiI,cAActC,QAAQ,MAAO,IACnDgD,EAAmBwG,EAAStH,GACxBc,GAAoBA,EAAiBlI,OACvC,GAAIqH,EAEF,IADAY,EAAaC,EAAiB3H,QAAQ8G,GAChB,KAAfY,GACLC,EAAiBE,OAAOH,EAAY,GACpCA,EAAaC,EAAiB3H,QAAQ8G,EAAUY,OAGlDC,GAAiBlI,OAAS,CAKlC,OAAO8I,OAML8Q,GAAmB,SAASxS,GAC9B,GAAIxH,GAAO,KAAM8O,EAAWsK,GAAYlQ,KAAKwC,KAAO0N,GAAYlQ,KAAKwC,IAAIoD,QAQzE,OAPIA,KAEA9O,EADuB,gBAAdwH,IAA0BA,EAC5BsH,EAAStH,GAAasH,EAAStH,GAAW5I,MAAM,MAEhDyB,EAAUyO,IAGd9O,GAMLia,GAAc,SAAStR,GACzB,GAAIuR,GAAkBza,KAAKyJ,KAAMP,GAAQ,CAClB,gBAAVA,IAAsBA,GAA+B,gBAAfA,GAAMX,MAAqBW,EAAMX,OAChFW,EAAQjJ,KAAYiJ,GAEtB,IAAIC,GAAYlJ,KAAYqJ,GAAaJ,IACvC+Q,OAAQxQ,MAEViR,IAAyB1a,KAAKyJ,KAAMN,GAEtC,MAAOM,OAMLkR,GAAc,SAASX,GACzB,IAAKL,GAAYlQ,KAAKwC,IACpB,KAAM,IAAInO,OAAM,4EAElBkc,GAAWY,GAAUZ,EACrB,KAAK,GAAI9Z,GAAI,EAAGA,EAAI8Z,EAASrZ,OAAQT,IACnC,GAAIpB,EAAQkB,KAAKga,EAAU9Z,IAAM8Z,EAAS9Z,IAA+B,IAAzB8Z,EAAS9Z,GAAGN,SAAgB,CACrEoa,EAAS9Z,GAAG2a,aAMsD,KAA5DhB,GAAaG,EAAS9Z,GAAG2a,cAAc3Z,QAAQuI,KAAKwC,KAC7D4N,GAAaG,EAAS9Z,GAAG2a,cAAcxS,KAAKoB,KAAKwC,KANjD+N,EAAS9Z,GAAG2a,aAAe,gBAAkBjB,KAC7CC,GAAaG,EAAS9Z,GAAG2a,eAAkBpR,KAAKwC,IAC5CjG,EAAcQ,gBAAiB,GACjCsU,GAAkBd,EAAS9Z,IAK/B,IAAI6a,GAAkBpB,GAAYlQ,KAAKwC,KAAO0N,GAAYlQ,KAAKwC,IAAI+N,QACtB,MAAzCe,EAAgB7Z,QAAQ8Y,EAAS9Z,KACnC6a,EAAgB1S,KAAK2R,EAAS9Z,IAIpC,MAAOuJ,OAMLuR,GAAgB,SAAShB,GAC3B,GAAIK,GAAOV,GAAYlQ,KAAKwC,GAC5B,KAAKoO,EACH,MAAO5Q,KAET,IACIwR,GADAF,EAAkBV,EAAKL,QAGzBA,GADsB,mBAAbA,GACEe,EAAgB5b,MAAM,GAEtByb,GAAUZ,EAEvB,KAAK,GAAI9Z,GAAI8Z,EAASrZ,OAAQT,KAC5B,GAAIpB,EAAQkB,KAAKga,EAAU9Z,IAAM8Z,EAAS9Z,IAA+B,IAAzB8Z,EAAS9Z,GAAGN,SAAgB,CAE1E,IADAqb,EAAa,EAC8D,MAAnEA,EAAaF,EAAgB7Z,QAAQ8Y,EAAS9Z,GAAI+a,KACxDF,EAAgBhS,OAAOkS,EAAY,EAErC,IAAIC,GAAYrB,GAAaG,EAAS9Z,GAAG2a,aACzC,IAAIK,EAAW,CAEb,IADAD,EAAa,EACoD,MAAzDA,EAAaC,EAAUha,QAAQuI,KAAKwC,GAAIgP,KAC9CC,EAAUnS,OAAOkS,EAAY,EAEN,KAArBC,EAAUva,SACRqF,EAAcQ,gBAAiB,GACjC2U,GAAqBnB,EAAS9Z,UAEzB8Z,GAAS9Z,GAAG2a,eAK3B,MAAOpR,OAML2R,GAAkB,WACpB,GAAIf,GAAOV,GAAYlQ,KAAKwC,GAC5B,OAAOoO,IAAQA,EAAKL,SAAWK,EAAKL,SAAS7a,MAAM,OAMjDkc,GAAiB,WACd1B,GAAYlQ,KAAKwC,MAGtBxC,KAAK6R,SACL7R,KAAKX,YACE6Q,IAAYlQ,KAAKwC,MAMtBwO,GAAoB,SAASvR,GAC/B,IAAMA,IAASA,EAAMX,KACnB,OAAO,CAET,IAAIW,EAAM+Q,QAAU/Q,EAAM+Q,SAAWxQ,KACnC,OAAO,CAET,IAAI4Q,GAAOV,GAAYlQ,KAAKwC,IACxBsP,EAAalB,GAAQA,EAAKL,SAC1BwB,IAAkBD,GAAcA,EAAW5a,OAAS,EACpD8a,GAAcvS,EAAMxI,QAAU8a,GAAsD,KAArCD,EAAWra,QAAQgI,EAAMxI,QACxEgb,EAAgBxS,EAAMgD,eAAiBsP,GAA6D,KAA5CD,EAAWra,QAAQgI,EAAMgD,eACjFyP,EAAazS,EAAM+Q,QAAU/Q,EAAM+Q,SAAWxQ,IAClD,OAAK4Q,KAAUoB,GAAcC,GAAiBC,IAGvC,GAFE,GAUPjB,GAA2B,SAASxR,GACtC,GAAImR,GAAOV,GAAYlQ,KAAKwC,GAC5B,IAAuB,gBAAV/C,IAAsBA,GAASA,EAAMX,MAAQ8R,EAA1D,CAGA,GAAIpL,GAAQJ,GAAoB3F,GAC5BiG,EAAuBkL,GAAQA,EAAKhL,SAAS,SAC7CD,EAAuBiL,GAAQA,EAAKhL,SAASnG,EAAMX,UACnD8G,EAAWF,EAAqBG,OAAOF,EAC3C,IAAIC,GAAYA,EAAS1O,OAAQ,CAC/B,GAAIT,GAAGC,EAAK4O,EAAMC,EAAS7F,EAAWoG,EAAkB9F,IACxD,KAAKvJ,EAAI,EAAGC,EAAMkP,EAAS1O,OAAYR,EAAJD,EAASA,IAC1C6O,EAAOM,EAASnP,GAChB8O,EAAUO,EACU,gBAATR,IAA8C,kBAAlBrS,GAAQqS,KAC7CA,EAAOrS,EAAQqS,IAEG,gBAATA,IAAqBA,GAAoC,kBAArBA,GAAKS,cAClDR,EAAUD,EACVA,EAAOA,EAAKS,aAEM,kBAATT,KACT5F,EAAYlJ,KAAYiJ,GACxB4F,GAAkBC,EAAMC,GAAW7F,GAAa8F,OAWpD2L,GAAY,SAASZ,GAIvB,MAHwB,gBAAbA,KACTA,MAEgC,gBAApBA,GAASrZ,QAAwBqZ,GAAaA,GAQ1Dc,GAAoB,SAAShQ,GAC/B,GAAMA,GAAgC,IAArBA,EAAQlL,SAAzB,CAGA,GAAIgc,GAAuB,SAAS1S,IAC5BA,IAAUA,EAAQxM,EAAQwM,UAGV,OAAlBA,EAAM0G,UACR1G,EAAM2S,2BACN3S,EAAM4S,wBAED5S,GAAM0G,UAEXmM,EAAoB,SAAS7S,IACzBA,IAAUA,EAAQxM,EAAQwM,UAGhC0S,EAAqB1S,GACrBvB,GAAc4I,MAAMzF,IAEtBA,GAAQkR,iBAAiB,YAAaD,GAAmB,GACzDjR,EAAQkR,iBAAiB,WAAYJ,GAAsB,GAC3D9Q,EAAQkR,iBAAiB,aAAcJ,GAAsB,GAC7D9Q,EAAQkR,iBAAiB,aAAcJ,GAAsB,GAC7D9Q,EAAQkR,iBAAiB,YAAaJ,GAAsB,GAC5D9B,GAAehP,EAAQ+P,eACrBoB,UAAWF,EACXG,SAAUN,EACVO,WAAYP,EACZQ,WAAYR,EACZS,UAAWT,KASXT,GAAuB,SAASrQ,GAClC,GAAMA,GAAgC,IAArBA,EAAQlL,SAAzB,CAGA,GAAI0c,GAAgBxC,GAAehP,EAAQ+P,aAC3C,IAA+B,gBAAlByB,IAA8BA,EAA3C,CAIA,IAAK,GADDC,GAAKtF,EAAKuF,GAAgB,OAAQ,QAAS,QAAS,MAAO,QACtDtc,EAAI,EAAGC,EAAMqc,EAAY7b,OAAYR,EAAJD,EAASA,IACjDqc,EAAM,QAAUC,EAAYtc,GAC5B+W,EAAMqF,EAAcC,GACD,kBAARtF,IACTnM,EAAQ2R,oBAAoBF,EAAKtF,GAAK,SAGnC6C,IAAehP,EAAQ+P,gBAQhClT,IAAcqR,cAAgB,WAC5Be,GAAmB7K,MAAMzF,KAAM3J,EAAMW,aAOvCkH,GAAc5I,UAAUqJ,GAAK,WAC3B,MAAOgS,IAAUlL,MAAMzF,KAAM3J,EAAMW,aASrCkH,GAAc5I,UAAU+J,IAAM,WAC5B,MAAOwR,IAAWpL,MAAMzF,KAAM3J,EAAMW,aAQtCkH,GAAc5I,UAAUsQ,SAAW,WACjC,MAAOkL,IAAiBrL,MAAMzF,KAAM3J,EAAMW,aAO5CkH,GAAc5I,UAAUuJ,KAAO,WAC7B,MAAOkS,IAAYtL,MAAMzF,KAAM3J,EAAMW,aAOvCkH,GAAc5I,UAAUob,KAAO,WAC7B,MAAOQ,IAAYzL,MAAMzF,KAAM3J,EAAMW,aAQvCkH,GAAc5I,UAAUuc,OAAS,WAC/B,MAAON,IAAc9L,MAAMzF,KAAM3J,EAAMW,aAOzCkH,GAAc5I,UAAUib,SAAW,WACjC,MAAOoB,IAAgBlM,MAAMzF,KAAM3J,EAAMW,aAQ3CkH,GAAc5I,UAAUua,QAAU,WAChC,MAAO+B,IAAenM,MAAMzF,KAAM3J,EAAMW,aAO1CkH,GAAc5I,UAAU2d,QAAU,SAAS7I,GACzC,IAAK8F,GAAYlQ,KAAKwC,IACpB,KAAM,IAAInO,OAAM,yFAGlB,OADA6J,IAAc8E,QAAQ,aAAcoH,GAC7BpK,MAOT9B,GAAc5I,UAAU4d,QAAU,SAAS7I,GACzC,IAAK6F,GAAYlQ,KAAKwC,IACpB,KAAM,IAAInO,OAAM,yFAGlB,OADA6J,IAAc8E,QAAQ,YAAaqH,GAC5BrK,MAOT9B,GAAc5I,UAAU6d,YAAc,SAASC,GAC7C,IAAKlD,GAAYlQ,KAAKwC,IACpB,KAAM,IAAInO,OAAM,yFAGlB,OADA6J,IAAc8E,QAAQ,kBAAmBoQ,GAClCpT,MAOT9B,GAAc5I,UAAU0N,QAAU,WAChC,IAAKkN,GAAYlQ,KAAKwC,IACpB,KAAM,IAAInO,OAAM,yFAGlB,OADA6J,IAAc8E,QAAQyC,MAAMzF,KAAM3J,EAAMW,YACjCgJ,MAQT9B,GAAc5I,UAAUoL,UAAY,WAClC,IAAKwP,GAAYlQ,KAAKwC,IACpB,KAAM,IAAInO,OAAM,2FAGlB,OADA6J,IAAcwC,UAAU+E,MAAMzF,KAAM3J,EAAMW,YACnCgJ,MAQT9B,GAAc5I,UAAUwa,QAAU,WAChC,IAAKI,GAAYlQ,KAAKwC,IACpB,KAAM,IAAInO,OAAM,yFAElB,OAAO6J,IAAc4R,QAAQrK,MAAMzF,KAAM3J,EAAMW,aAE3B,kBAAXqc,SAAyBA,OAAOC,IACzCD,OAAO,WACL,MAAOnV,MAEkB,gBAAXqV,SAAuBA,QAAoC,gBAAnBA,QAAOC,SAAwBD,OAAOC,QAC9FD,OAAOC,QAAUtV,GAEjBtL,EAAOsL,cAAgBA,IAExB,WACD,MAAO8B,OAAQpN","sourcesContent":["/*!\n * ZeroClipboard\n * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.\n * Copyright (c) 2009-2015 Jon Rohan, James M. Greene\n * Licensed MIT\n * http://zeroclipboard.org/\n * v2.3.0-beta.1\n */\n(function(window, undefined) {\n \"use strict\";\n /**\n * Store references to critically important global functions that may be\n * overridden on certain web pages.\n */\n var _window = window, _document = _window.document, _navigator = _window.navigator, _setTimeout = _window.setTimeout, _clearTimeout = _window.clearTimeout, _setInterval = _window.setInterval, _clearInterval = _window.clearInterval, _getComputedStyle = _window.getComputedStyle, _encodeURIComponent = _window.encodeURIComponent, _ActiveXObject = _window.ActiveXObject, _Error = _window.Error, _parseInt = _window.Number.parseInt || _window.parseInt, _parseFloat = _window.Number.parseFloat || _window.parseFloat, _isNaN = _window.Number.isNaN || _window.isNaN, _now = _window.Date.now, _keys = _window.Object.keys, _defineProperty = _window.Object.defineProperty, _hasOwn = _window.Object.prototype.hasOwnProperty, _slice = _window.Array.prototype.slice, _unwrap = function() {\n var unwrapper = function(el) {\n return el;\n };\n if (typeof _window.wrap === \"function\" && typeof _window.unwrap === \"function\") {\n try {\n var div = _document.createElement(\"div\");\n var unwrappedDiv = _window.unwrap(div);\n if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) {\n unwrapper = _window.unwrap;\n }\n } catch (e) {}\n }\n return unwrapper;\n }();\n /**\n * Convert an `arguments` object into an Array.\n *\n * @returns The arguments as an Array\n * @private\n */\n var _args = function(argumentsObj) {\n return _slice.call(argumentsObj, 0);\n };\n /**\n * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`.\n *\n * @returns The target object, augmented\n * @private\n */\n var _extend = function() {\n var i, len, arg, prop, src, copy, args = _args(arguments), target = args[0] || {};\n for (i = 1, len = args.length; i < len; i++) {\n if ((arg = args[i]) != null) {\n for (prop in arg) {\n if (_hasOwn.call(arg, prop)) {\n src = target[prop];\n copy = arg[prop];\n if (target !== copy && copy !== undefined) {\n target[prop] = copy;\n }\n }\n }\n }\n }\n return target;\n };\n /**\n * Return a deep copy of the source object or array.\n *\n * @returns Object or Array\n * @private\n */\n var _deepCopy = function(source) {\n var copy, i, len, prop;\n if (typeof source !== \"object\" || source == null || typeof source.nodeType === \"number\") {\n copy = source;\n } else if (typeof source.length === \"number\") {\n copy = [];\n for (i = 0, len = source.length; i < len; i++) {\n if (_hasOwn.call(source, i)) {\n copy[i] = _deepCopy(source[i]);\n }\n }\n } else {\n copy = {};\n for (prop in source) {\n if (_hasOwn.call(source, prop)) {\n copy[prop] = _deepCopy(source[prop]);\n }\n }\n }\n return copy;\n };\n /**\n * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep.\n * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to\n * be kept.\n *\n * @returns A new filtered object.\n * @private\n */\n var _pick = function(obj, keys) {\n var newObj = {};\n for (var i = 0, len = keys.length; i < len; i++) {\n if (keys[i] in obj) {\n newObj[keys[i]] = obj[keys[i]];\n }\n }\n return newObj;\n };\n /**\n * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit.\n * The inverse of `_pick`.\n *\n * @returns A new filtered object.\n * @private\n */\n var _omit = function(obj, keys) {\n var newObj = {};\n for (var prop in obj) {\n if (keys.indexOf(prop) === -1) {\n newObj[prop] = obj[prop];\n }\n }\n return newObj;\n };\n /**\n * Remove all owned, enumerable properties from an object.\n *\n * @returns The original object without its owned, enumerable properties.\n * @private\n */\n var _deleteOwnProperties = function(obj) {\n if (obj) {\n for (var prop in obj) {\n if (_hasOwn.call(obj, prop)) {\n delete obj[prop];\n }\n }\n }\n return obj;\n };\n /**\n * Determine if an element is contained within another element.\n *\n * @returns Boolean\n * @private\n */\n var _containedBy = function(el, ancestorEl) {\n if (el && el.nodeType === 1 && el.ownerDocument && ancestorEl && (ancestorEl.nodeType === 1 && ancestorEl.ownerDocument && ancestorEl.ownerDocument === el.ownerDocument || ancestorEl.nodeType === 9 && !ancestorEl.ownerDocument && ancestorEl === el.ownerDocument)) {\n do {\n if (el === ancestorEl) {\n return true;\n }\n el = el.parentNode;\n } while (el);\n }\n return false;\n };\n /**\n * Get the URL path's parent directory.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getDirPathOfUrl = function(url) {\n var dir;\n if (typeof url === \"string\" && url) {\n dir = url.split(\"#\")[0].split(\"?\")[0];\n dir = url.slice(0, url.lastIndexOf(\"/\") + 1);\n }\n return dir;\n };\n /**\n * Get the current script's URL by throwing an `Error` and analyzing it.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getCurrentScriptUrlFromErrorStack = function(stack) {\n var url, matches;\n if (typeof stack === \"string\" && stack) {\n matches = stack.match(/^(?:|[^:@]*@|.+\\)@(?=http[s]?|file)|.+?\\s+(?: at |@)(?:[^:\\(]+ )*[\\(]?)((?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?/);\n if (matches && matches[1]) {\n url = matches[1];\n } else {\n matches = stack.match(/\\)@((?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?/);\n if (matches && matches[1]) {\n url = matches[1];\n }\n }\n }\n return url;\n };\n /**\n * Get the current script's URL by throwing an `Error` and analyzing it.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getCurrentScriptUrlFromError = function() {\n var url, err;\n try {\n throw new _Error();\n } catch (e) {\n err = e;\n }\n if (err) {\n url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack);\n }\n return url;\n };\n /**\n * Get the current script's URL.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getCurrentScriptUrl = function() {\n var jsPath, scripts, i;\n if (_document.currentScript && (jsPath = _document.currentScript.src)) {\n return jsPath;\n }\n scripts = _document.getElementsByTagName(\"script\");\n if (scripts.length === 1) {\n return scripts[0].src || undefined;\n }\n if (\"readyState\" in scripts[0]) {\n for (i = scripts.length; i--; ) {\n if (scripts[i].readyState === \"interactive\" && (jsPath = scripts[i].src)) {\n return jsPath;\n }\n }\n }\n if (_document.readyState === \"loading\" && (jsPath = scripts[scripts.length - 1].src)) {\n return jsPath;\n }\n if (jsPath = _getCurrentScriptUrlFromError()) {\n return jsPath;\n }\n return undefined;\n };\n /**\n * Get the unanimous parent directory of ALL script tags.\n * If any script tags are either (a) inline or (b) from differing parent\n * directories, this method must return `undefined`.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getUnanimousScriptParentDir = function() {\n var i, jsDir, jsPath, scripts = _document.getElementsByTagName(\"script\");\n for (i = scripts.length; i--; ) {\n if (!(jsPath = scripts[i].src)) {\n jsDir = null;\n break;\n }\n jsPath = _getDirPathOfUrl(jsPath);\n if (jsDir == null) {\n jsDir = jsPath;\n } else if (jsDir !== jsPath) {\n jsDir = null;\n break;\n }\n }\n return jsDir || undefined;\n };\n /**\n * Get the presumed location of the \"ZeroClipboard.swf\" file, based on the location\n * of the executing JavaScript file (e.g. \"ZeroClipboard.js\", etc.).\n *\n * @returns String\n * @private\n */\n var _getDefaultSwfPath = function() {\n var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || \"\";\n return jsDir + \"ZeroClipboard.swf\";\n };\n /**\n * Is the client's operating system some version of Windows?\n *\n * @returns Boolean\n * @private\n */\n var _isWindows = function() {\n var isWindowsRegex = /win(dows|[\\s]?(nt|me|ce|xp|vista|[\\d]+))/i;\n return !!_navigator && (isWindowsRegex.test(_navigator.appVersion || \"\") || isWindowsRegex.test(_navigator.platform || \"\") || (_navigator.userAgent || \"\").indexOf(\"Windows\") !== -1);\n };\n /**\n * Keep track of if the page is framed (in an `iframe`). This can never change.\n * @private\n */\n var _pageIsFramed = function() {\n return window.opener == null && (!!window.top && window != window.top || !!window.parent && window != window.parent);\n }();\n /**\n * Keep track of the state of the Flash object.\n * @private\n */\n var _flashState = {\n bridge: null,\n version: \"0.0.0\",\n pluginType: \"unknown\",\n disabled: null,\n outdated: null,\n sandboxed: null,\n unavailable: null,\n degraded: null,\n deactivated: null,\n overdue: null,\n ready: null\n };\n /**\n * The minimum Flash Player version required to use ZeroClipboard completely.\n * @readonly\n * @private\n */\n var _minimumFlashVersion = \"11.0.0\";\n /**\n * The ZeroClipboard library version number, as reported by Flash, at the time the SWF was compiled.\n */\n var _zcSwfVersion;\n /**\n * Keep track of all event listener registrations.\n * @private\n */\n var _handlers = {};\n /**\n * Keep track of the currently activated element.\n * @private\n */\n var _currentElement;\n /**\n * Keep track of the element that was activated when a `copy` process started.\n * @private\n */\n var _copyTarget;\n /**\n * Keep track of data for the pending clipboard transaction.\n * @private\n */\n var _clipData = {};\n /**\n * Keep track of data formats for the pending clipboard transaction.\n * @private\n */\n var _clipDataFormatMap = null;\n /**\n * Keep track of the Flash availability check timeout.\n * @private\n */\n var _flashCheckTimeout = 0;\n /**\n * Keep track of SWF network errors interval polling.\n * @private\n */\n var _swfFallbackCheckInterval = 0;\n /**\n * The `message` store for events\n * @private\n */\n var _eventMessages = {\n ready: \"Flash communication is established\",\n error: {\n \"flash-disabled\": \"Flash is disabled or not installed. May also be attempting to run Flash in a sandboxed iframe, which is impossible.\",\n \"flash-outdated\": \"Flash is too outdated to support ZeroClipboard\",\n \"flash-sandboxed\": \"Attempting to run Flash in a sandboxed iframe, which is impossible\",\n \"flash-unavailable\": \"Flash is unable to communicate bidirectionally with JavaScript\",\n \"flash-degraded\": \"Flash is unable to preserve data fidelity when communicating with JavaScript\",\n \"flash-deactivated\": \"Flash is too outdated for your browser and/or is configured as click-to-activate.\\nThis may also mean that the ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity.\\nMay also be attempting to run Flash in a sandboxed iframe, which is impossible.\",\n \"flash-overdue\": \"Flash communication was established but NOT within the acceptable time limit\",\n \"version-mismatch\": \"ZeroClipboard JS version number does not match ZeroClipboard SWF version number\",\n \"clipboard-error\": \"At least one error was thrown while ZeroClipboard was attempting to inject your data into the clipboard\",\n \"config-mismatch\": \"ZeroClipboard configuration does not match Flash's reality\",\n \"swf-not-found\": \"The ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity\"\n }\n };\n /**\n * The `name`s of `error` events that can only occur is Flash has at least\n * been able to load the SWF successfully.\n * @private\n */\n var _errorsThatOnlyOccurAfterFlashLoads = [ \"flash-unavailable\", \"flash-degraded\", \"flash-overdue\", \"version-mismatch\", \"config-mismatch\", \"clipboard-error\" ];\n /**\n * The `name`s of `error` events that should likely result in the `_flashState`\n * variable's property values being updated.\n * @private\n */\n var _flashStateErrorNames = [ \"flash-disabled\", \"flash-outdated\", \"flash-sandboxed\", \"flash-unavailable\", \"flash-degraded\", \"flash-deactivated\", \"flash-overdue\" ];\n /**\n * A RegExp to match the `name` property of `error` events related to Flash.\n * @private\n */\n var _flashStateErrorNameMatchingRegex = new RegExp(\"^flash-(\" + _flashStateErrorNames.map(function(errorName) {\n return errorName.replace(/^flash-/, \"\");\n }).join(\"|\") + \")$\");\n /**\n * A RegExp to match the `name` property of `error` events related to Flash,\n * which is enabled.\n * @private\n */\n var _flashStateEnabledErrorNameMatchingRegex = new RegExp(\"^flash-(\" + _flashStateErrorNames.slice(1).map(function(errorName) {\n return errorName.replace(/^flash-/, \"\");\n }).join(\"|\") + \")$\");\n /**\n * ZeroClipboard configuration defaults for the Core module.\n * @private\n */\n var _globalConfig = {\n swfPath: _getDefaultSwfPath(),\n trustedDomains: window.location.host ? [ window.location.host ] : [],\n cacheBust: true,\n forceEnhancedClipboard: false,\n flashLoadTimeout: 3e4,\n autoActivate: true,\n bubbleEvents: true,\n fixLineEndings: true,\n containerId: \"global-zeroclipboard-html-bridge\",\n containerClass: \"global-zeroclipboard-container\",\n swfObjectId: \"global-zeroclipboard-flash-bridge\",\n hoverClass: \"zeroclipboard-is-hover\",\n activeClass: \"zeroclipboard-is-active\",\n forceHandCursor: false,\n title: null,\n zIndex: 999999999\n };\n /**\n * The underlying implementation of `ZeroClipboard.config`.\n * @private\n */\n var _config = function(options) {\n if (typeof options === \"object\" && options !== null) {\n for (var prop in options) {\n if (_hasOwn.call(options, prop)) {\n if (/^(?:forceHandCursor|title|zIndex|bubbleEvents|fixLineEndings)$/.test(prop)) {\n _globalConfig[prop] = options[prop];\n } else if (_flashState.bridge == null) {\n if (prop === \"containerId\" || prop === \"swfObjectId\") {\n if (_isValidHtml4Id(options[prop])) {\n _globalConfig[prop] = options[prop];\n } else {\n throw new Error(\"The specified `\" + prop + \"` value is not valid as an HTML4 Element ID\");\n }\n } else {\n _globalConfig[prop] = options[prop];\n }\n }\n }\n }\n }\n if (typeof options === \"string\" && options) {\n if (_hasOwn.call(_globalConfig, options)) {\n return _globalConfig[options];\n }\n return;\n }\n return _deepCopy(_globalConfig);\n };\n /**\n * The underlying implementation of `ZeroClipboard.state`.\n * @private\n */\n var _state = function() {\n _detectSandbox();\n return {\n browser: _pick(_navigator, [ \"userAgent\", \"platform\", \"appName\", \"appVersion\" ]),\n flash: _omit(_flashState, [ \"bridge\" ]),\n zeroclipboard: {\n version: ZeroClipboard.version,\n config: ZeroClipboard.config()\n }\n };\n };\n /**\n * The underlying implementation of `ZeroClipboard.isFlashUnusable`.\n * @private\n */\n var _isFlashUnusable = function() {\n return !!(_flashState.disabled || _flashState.outdated || _flashState.sandboxed || _flashState.unavailable || _flashState.degraded || _flashState.deactivated);\n };\n /**\n * The underlying implementation of `ZeroClipboard.on`.\n * @private\n */\n var _on = function(eventType, listener) {\n var i, len, events, added = {};\n if (typeof eventType === \"string\" && eventType) {\n events = eventType.toLowerCase().split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n ZeroClipboard.on(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].replace(/^on/, \"\");\n added[eventType] = true;\n if (!_handlers[eventType]) {\n _handlers[eventType] = [];\n }\n _handlers[eventType].push(listener);\n }\n if (added.ready && _flashState.ready) {\n ZeroClipboard.emit({\n type: \"ready\"\n });\n }\n if (added.error) {\n for (i = 0, len = _flashStateErrorNames.length; i < len; i++) {\n if (_flashState[_flashStateErrorNames[i].replace(/^flash-/, \"\")] === true) {\n ZeroClipboard.emit({\n type: \"error\",\n name: _flashStateErrorNames[i]\n });\n break;\n }\n }\n if (_zcSwfVersion !== undefined && ZeroClipboard.version !== _zcSwfVersion) {\n ZeroClipboard.emit({\n type: \"error\",\n name: \"version-mismatch\",\n jsVersion: ZeroClipboard.version,\n swfVersion: _zcSwfVersion\n });\n }\n }\n }\n return ZeroClipboard;\n };\n /**\n * The underlying implementation of `ZeroClipboard.off`.\n * @private\n */\n var _off = function(eventType, listener) {\n var i, len, foundIndex, events, perEventHandlers;\n if (arguments.length === 0) {\n events = _keys(_handlers);\n } else if (typeof eventType === \"string\" && eventType) {\n events = eventType.split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n ZeroClipboard.off(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].toLowerCase().replace(/^on/, \"\");\n perEventHandlers = _handlers[eventType];\n if (perEventHandlers && perEventHandlers.length) {\n if (listener) {\n foundIndex = perEventHandlers.indexOf(listener);\n while (foundIndex !== -1) {\n perEventHandlers.splice(foundIndex, 1);\n foundIndex = perEventHandlers.indexOf(listener, foundIndex);\n }\n } else {\n perEventHandlers.length = 0;\n }\n }\n }\n }\n return ZeroClipboard;\n };\n /**\n * The underlying implementation of `ZeroClipboard.handlers`.\n * @private\n */\n var _listeners = function(eventType) {\n var copy;\n if (typeof eventType === \"string\" && eventType) {\n copy = _deepCopy(_handlers[eventType]) || null;\n } else {\n copy = _deepCopy(_handlers);\n }\n return copy;\n };\n /**\n * The underlying implementation of `ZeroClipboard.emit`.\n * @private\n */\n var _emit = function(event) {\n var eventCopy, returnVal, tmp;\n event = _createEvent(event);\n if (!event) {\n return;\n }\n if (_preprocessEvent(event)) {\n return;\n }\n if (event.type === \"ready\" && _flashState.overdue === true) {\n return ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-overdue\"\n });\n }\n eventCopy = _extend({}, event);\n _dispatchCallbacks.call(this, eventCopy);\n if (event.type === \"copy\") {\n tmp = _mapClipDataToFlash(_clipData);\n returnVal = tmp.data;\n _clipDataFormatMap = tmp.formatMap;\n }\n return returnVal;\n };\n /**\n * The underlying implementation of `ZeroClipboard.create`.\n * @private\n */\n var _create = function() {\n var previousState = _flashState.sandboxed;\n _detectSandbox();\n if (typeof _flashState.ready !== \"boolean\") {\n _flashState.ready = false;\n }\n if (_flashState.sandboxed !== previousState && _flashState.sandboxed === true) {\n _flashState.ready = false;\n ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-sandboxed\"\n });\n } else if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) {\n var maxWait = _globalConfig.flashLoadTimeout;\n if (typeof maxWait === \"number\" && maxWait >= 0) {\n _flashCheckTimeout = _setTimeout(function() {\n if (typeof _flashState.deactivated !== \"boolean\") {\n _flashState.deactivated = true;\n }\n if (_flashState.deactivated === true) {\n ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-deactivated\"\n });\n }\n }, maxWait);\n }\n _flashState.overdue = false;\n _embedSwf();\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.destroy`.\n * @private\n */\n var _destroy = function() {\n ZeroClipboard.clearData();\n ZeroClipboard.blur();\n ZeroClipboard.emit(\"destroy\");\n _unembedSwf();\n ZeroClipboard.off();\n };\n /**\n * The underlying implementation of `ZeroClipboard.setData`.\n * @private\n */\n var _setData = function(format, data) {\n var dataObj;\n if (typeof format === \"object\" && format && typeof data === \"undefined\") {\n dataObj = format;\n ZeroClipboard.clearData();\n } else if (typeof format === \"string\" && format) {\n dataObj = {};\n dataObj[format] = data;\n } else {\n return;\n }\n for (var dataFormat in dataObj) {\n if (typeof dataFormat === \"string\" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === \"string\" && dataObj[dataFormat]) {\n _clipData[dataFormat] = _fixLineEndings(dataObj[dataFormat]);\n }\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.clearData`.\n * @private\n */\n var _clearData = function(format) {\n if (typeof format === \"undefined\") {\n _deleteOwnProperties(_clipData);\n _clipDataFormatMap = null;\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n delete _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.getData`.\n * @private\n */\n var _getData = function(format) {\n if (typeof format === \"undefined\") {\n return _deepCopy(_clipData);\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n return _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.\n * @private\n */\n var _focus = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.activeClass);\n if (_currentElement !== element) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n }\n }\n _currentElement = element;\n _addClass(element, _globalConfig.hoverClass);\n var newTitle = element.getAttribute(\"title\") || _globalConfig.title;\n if (typeof newTitle === \"string\" && newTitle) {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.setAttribute(\"title\", newTitle);\n }\n }\n var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, \"cursor\") === \"pointer\";\n _setHandCursor(useHandCursor);\n _reposition();\n };\n /**\n * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.\n * @private\n */\n var _blur = function() {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.removeAttribute(\"title\");\n htmlBridge.style.left = \"0px\";\n htmlBridge.style.top = \"-9999px\";\n htmlBridge.style.width = \"1px\";\n htmlBridge.style.height = \"1px\";\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n _removeClass(_currentElement, _globalConfig.activeClass);\n _currentElement = null;\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.activeElement`.\n * @private\n */\n var _activeElement = function() {\n return _currentElement || null;\n };\n /**\n * Check if a value is a valid HTML4 `ID` or `Name` token.\n * @private\n */\n var _isValidHtml4Id = function(id) {\n return typeof id === \"string\" && id && /^[A-Za-z][A-Za-z0-9_:\\-\\.]*$/.test(id);\n };\n /**\n * Create or update an `event` object, based on the `eventType`.\n * @private\n */\n var _createEvent = function(event) {\n var eventType;\n if (typeof event === \"string\" && event) {\n eventType = event;\n event = {};\n } else if (typeof event === \"object\" && event && typeof event.type === \"string\" && event.type) {\n eventType = event.type;\n }\n if (!eventType) {\n return;\n }\n eventType = eventType.toLowerCase();\n if (!event.target && (/^(copy|aftercopy|_click)$/.test(eventType) || eventType === \"error\" && event.name === \"clipboard-error\")) {\n event.target = _copyTarget;\n }\n _extend(event, {\n type: eventType,\n target: event.target || _currentElement || null,\n relatedTarget: event.relatedTarget || null,\n currentTarget: _flashState && _flashState.bridge || null,\n timeStamp: event.timeStamp || _now() || null\n });\n var msg = _eventMessages[event.type];\n if (event.type === \"error\" && event.name && msg) {\n msg = msg[event.name];\n }\n if (msg) {\n event.message = msg;\n }\n if (event.type === \"ready\") {\n _extend(event, {\n target: null,\n version: _flashState.version\n });\n }\n if (event.type === \"error\") {\n if (_flashStateErrorNameMatchingRegex.test(event.name)) {\n _extend(event, {\n target: null,\n minimumVersion: _minimumFlashVersion\n });\n }\n if (_flashStateEnabledErrorNameMatchingRegex.test(event.name)) {\n _extend(event, {\n version: _flashState.version\n });\n }\n }\n if (event.type === \"copy\") {\n event.clipboardData = {\n setData: ZeroClipboard.setData,\n clearData: ZeroClipboard.clearData\n };\n }\n if (event.type === \"aftercopy\") {\n event = _mapClipResultsFromFlash(event, _clipDataFormatMap);\n }\n if (event.target && !event.relatedTarget) {\n event.relatedTarget = _getRelatedTarget(event.target);\n }\n return _addMouseData(event);\n };\n /**\n * Get a relatedTarget from the target's `data-clipboard-target` attribute\n * @private\n */\n var _getRelatedTarget = function(targetEl) {\n var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute(\"data-clipboard-target\");\n return relatedTargetId ? _document.getElementById(relatedTargetId) : null;\n };\n /**\n * Add element and position data to `MouseEvent` instances\n * @private\n */\n var _addMouseData = function(event) {\n if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n var srcElement = event.target;\n var fromElement = event.type === \"_mouseover\" && event.relatedTarget ? event.relatedTarget : undefined;\n var toElement = event.type === \"_mouseout\" && event.relatedTarget ? event.relatedTarget : undefined;\n var pos = _getElementPosition(srcElement);\n var screenLeft = _window.screenLeft || _window.screenX || 0;\n var screenTop = _window.screenTop || _window.screenY || 0;\n var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;\n var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;\n var pageX = pos.left + (typeof event._stageX === \"number\" ? event._stageX : 0);\n var pageY = pos.top + (typeof event._stageY === \"number\" ? event._stageY : 0);\n var clientX = pageX - scrollLeft;\n var clientY = pageY - scrollTop;\n var screenX = screenLeft + clientX;\n var screenY = screenTop + clientY;\n var moveX = typeof event.movementX === \"number\" ? event.movementX : 0;\n var moveY = typeof event.movementY === \"number\" ? event.movementY : 0;\n delete event._stageX;\n delete event._stageY;\n _extend(event, {\n srcElement: srcElement,\n fromElement: fromElement,\n toElement: toElement,\n screenX: screenX,\n screenY: screenY,\n pageX: pageX,\n pageY: pageY,\n clientX: clientX,\n clientY: clientY,\n x: clientX,\n y: clientY,\n movementX: moveX,\n movementY: moveY,\n offsetX: 0,\n offsetY: 0,\n layerX: 0,\n layerY: 0\n });\n }\n return event;\n };\n /**\n * Determine if an event's registered handlers should be execute synchronously or asynchronously.\n *\n * @returns {boolean}\n * @private\n */\n var _shouldPerformAsync = function(event) {\n var eventType = event && typeof event.type === \"string\" && event.type || \"\";\n return !/^(?:(?:before)?copy|destroy)$/.test(eventType);\n };\n /**\n * Control if a callback should be executed asynchronously or not.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallback = function(func, context, args, async) {\n if (async) {\n _setTimeout(function() {\n func.apply(context, args);\n }, 0);\n } else {\n func.apply(context, args);\n }\n };\n /**\n * Handle the actual dispatching of events to client instances.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallbacks = function(event) {\n if (!(typeof event === \"object\" && event && event.type)) {\n return;\n }\n var async = _shouldPerformAsync(event);\n var wildcardTypeHandlers = _handlers[\"*\"] || [];\n var specificTypeHandlers = _handlers[event.type] || [];\n var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);\n if (handlers && handlers.length) {\n var i, len, func, context, eventCopy, originalContext = this;\n for (i = 0, len = handlers.length; i < len; i++) {\n func = handlers[i];\n context = originalContext;\n if (typeof func === \"string\" && typeof _window[func] === \"function\") {\n func = _window[func];\n }\n if (typeof func === \"object\" && func && typeof func.handleEvent === \"function\") {\n context = func;\n func = func.handleEvent;\n }\n if (typeof func === \"function\") {\n eventCopy = _extend({}, event);\n _dispatchCallback(func, context, [ eventCopy ], async);\n }\n }\n }\n return this;\n };\n /**\n * Check an `error` event's `name` property to see if Flash has\n * already loaded, which rules out possible `iframe` sandboxing.\n * @private\n */\n var _getSandboxStatusFromErrorEvent = function(event) {\n var isSandboxed = null;\n if (_pageIsFramed === false || event && event.type === \"error\" && event.name && _errorsThatOnlyOccurAfterFlashLoads.indexOf(event.name) !== -1) {\n isSandboxed = false;\n }\n return isSandboxed;\n };\n /**\n * Preprocess any special behaviors, reactions, or state changes after receiving this event.\n * Executes only once per event emitted, NOT once per client.\n * @private\n */\n var _preprocessEvent = function(event) {\n var element = event.target || _currentElement || null;\n var sourceIsSwf = event._source === \"swf\";\n delete event._source;\n switch (event.type) {\n case \"error\":\n var isSandboxed = event.name === \"flash-sandboxed\" || _getSandboxStatusFromErrorEvent(event);\n if (typeof isSandboxed === \"boolean\") {\n _flashState.sandboxed = isSandboxed;\n }\n if (_flashStateErrorNames.indexOf(event.name) !== -1) {\n _extend(_flashState, {\n disabled: event.name === \"flash-disabled\",\n outdated: event.name === \"flash-outdated\",\n unavailable: event.name === \"flash-unavailable\",\n degraded: event.name === \"flash-degraded\",\n deactivated: event.name === \"flash-deactivated\",\n overdue: event.name === \"flash-overdue\",\n ready: false\n });\n } else if (event.name === \"version-mismatch\") {\n _zcSwfVersion = event.swfVersion;\n _extend(_flashState, {\n disabled: false,\n outdated: false,\n unavailable: false,\n degraded: false,\n deactivated: false,\n overdue: false,\n ready: false\n });\n }\n _clearTimeoutsAndPolling();\n break;\n\n case \"ready\":\n _zcSwfVersion = event.swfVersion;\n var wasDeactivated = _flashState.deactivated === true;\n _extend(_flashState, {\n disabled: false,\n outdated: false,\n sandboxed: false,\n unavailable: false,\n degraded: false,\n deactivated: false,\n overdue: wasDeactivated,\n ready: !wasDeactivated\n });\n _clearTimeoutsAndPolling();\n break;\n\n case \"beforecopy\":\n _copyTarget = element;\n break;\n\n case \"copy\":\n var textContent, htmlContent, targetEl = event.relatedTarget;\n if (!(_clipData[\"text/html\"] || _clipData[\"text/plain\"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n if (htmlContent !== textContent) {\n event.clipboardData.setData(\"text/html\", htmlContent);\n }\n } else if (!_clipData[\"text/plain\"] && event.target && (textContent = event.target.getAttribute(\"data-clipboard-text\"))) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n }\n break;\n\n case \"aftercopy\":\n _queueEmitClipboardErrors(event);\n ZeroClipboard.clearData();\n if (element && element !== _safeActiveElement() && element.focus) {\n element.focus();\n }\n break;\n\n case \"_mouseover\":\n ZeroClipboard.focus(element);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseenter\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseover\"\n }));\n }\n break;\n\n case \"_mouseout\":\n ZeroClipboard.blur();\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseleave\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseout\"\n }));\n }\n break;\n\n case \"_mousedown\":\n _addClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mouseup\":\n _removeClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_click\":\n _copyTarget = null;\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mousemove\":\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n }\n if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n return true;\n }\n };\n /**\n * Check an \"aftercopy\" event for clipboard errors and emit a corresponding \"error\" event.\n * @private\n */\n var _queueEmitClipboardErrors = function(aftercopyEvent) {\n if (aftercopyEvent.errors && aftercopyEvent.errors.length > 0) {\n var errorEvent = _deepCopy(aftercopyEvent);\n _extend(errorEvent, {\n type: \"error\",\n name: \"clipboard-error\"\n });\n delete errorEvent.success;\n _setTimeout(function() {\n ZeroClipboard.emit(errorEvent);\n }, 0);\n }\n };\n /**\n * Dispatch a synthetic MouseEvent.\n *\n * @returns `undefined`\n * @private\n */\n var _fireMouseEvent = function(event) {\n if (!(event && typeof event.type === \"string\" && event)) {\n return;\n }\n var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = {\n view: doc.defaultView || _window,\n canBubble: true,\n cancelable: true,\n detail: event.type === \"click\" ? 1 : 0,\n button: typeof event.which === \"number\" ? event.which - 1 : typeof event.button === \"number\" ? event.button : doc.createEvent ? 0 : 1\n }, args = _extend(defaults, event);\n if (!target) {\n return;\n }\n if (doc.createEvent && target.dispatchEvent) {\n args = [ args.type, args.canBubble, args.cancelable, args.view, args.detail, args.screenX, args.screenY, args.clientX, args.clientY, args.ctrlKey, args.altKey, args.shiftKey, args.metaKey, args.button, args.relatedTarget ];\n e = doc.createEvent(\"MouseEvents\");\n if (e.initMouseEvent) {\n e.initMouseEvent.apply(e, args);\n e._source = \"js\";\n target.dispatchEvent(e);\n }\n }\n };\n /**\n * Continuously poll the DOM until either:\n * (a) the fallback content becomes visible, or\n * (b) we receive an event from SWF (handled elsewhere)\n *\n * IMPORTANT:\n * This is NOT a necessary check but it can result in significantly faster\n * detection of bad `swfPath` configuration and/or network/server issues [in\n * supported browsers] than waiting for the entire `flashLoadTimeout` duration\n * to elapse before detecting that the SWF cannot be loaded. The detection\n * duration can be anywhere from 10-30 times faster [in supported browsers] by\n * using this approach.\n *\n * @returns `undefined`\n * @private\n */\n var _watchForSwfFallbackContent = function() {\n var maxWait = _globalConfig.flashLoadTimeout;\n if (typeof maxWait === \"number\" && maxWait >= 0) {\n var pollWait = Math.min(1e3, maxWait / 10);\n var fallbackContentId = _globalConfig.swfObjectId + \"_fallbackContent\";\n _swfFallbackCheckInterval = _setInterval(function() {\n var el = _document.getElementById(fallbackContentId);\n if (_isElementVisible(el)) {\n _clearTimeoutsAndPolling();\n _flashState.deactivated = null;\n ZeroClipboard.emit({\n type: \"error\",\n name: \"swf-not-found\"\n });\n }\n }, pollWait);\n }\n };\n /**\n * Create the HTML bridge element to embed the Flash object into.\n * @private\n */\n var _createHtmlBridge = function() {\n var container = _document.createElement(\"div\");\n container.id = _globalConfig.containerId;\n container.className = _globalConfig.containerClass;\n container.style.position = \"absolute\";\n container.style.left = \"0px\";\n container.style.top = \"-9999px\";\n container.style.width = \"1px\";\n container.style.height = \"1px\";\n container.style.zIndex = \"\" + _getSafeZIndex(_globalConfig.zIndex);\n return container;\n };\n /**\n * Get the HTML element container that wraps the Flash bridge object/element.\n * @private\n */\n var _getHtmlBridge = function(flashBridge) {\n var htmlBridge = flashBridge && flashBridge.parentNode;\n while (htmlBridge && htmlBridge.nodeName === \"OBJECT\" && htmlBridge.parentNode) {\n htmlBridge = htmlBridge.parentNode;\n }\n return htmlBridge || null;\n };\n /**\n * Create the SWF object.\n *\n * @returns The SWF object reference.\n * @private\n */\n var _embedSwf = function() {\n var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge);\n if (!flashBridge) {\n var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);\n var allowNetworking = allowScriptAccess === \"never\" ? \"none\" : \"all\";\n var flashvars = _vars(_extend({\n jsVersion: ZeroClipboard.version\n }, _globalConfig));\n var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);\n container = _createHtmlBridge();\n var divToBeReplaced = _document.createElement(\"div\");\n container.appendChild(divToBeReplaced);\n _document.body.appendChild(container);\n var tmpDiv = _document.createElement(\"div\");\n var usingActiveX = _flashState.pluginType === \"activex\";\n tmpDiv.innerHTML = '\" + (usingActiveX ? '' : \"\") + '' + '' + '' + '' + '' + '
     
    ' + \"
    \";\n flashBridge = tmpDiv.firstChild;\n tmpDiv = null;\n _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;\n container.replaceChild(flashBridge, divToBeReplaced);\n _watchForSwfFallbackContent();\n }\n if (!flashBridge) {\n flashBridge = _document[_globalConfig.swfObjectId];\n if (flashBridge && (len = flashBridge.length)) {\n flashBridge = flashBridge[len - 1];\n }\n if (!flashBridge && container) {\n flashBridge = container.firstChild;\n }\n }\n _flashState.bridge = flashBridge || null;\n return flashBridge;\n };\n /**\n * Destroy the SWF object.\n * @private\n */\n var _unembedSwf = function() {\n var flashBridge = _flashState.bridge;\n if (flashBridge) {\n var htmlBridge = _getHtmlBridge(flashBridge);\n if (htmlBridge) {\n if (_flashState.pluginType === \"activex\" && \"readyState\" in flashBridge) {\n flashBridge.style.display = \"none\";\n (function removeSwfFromIE() {\n if (flashBridge.readyState === 4) {\n for (var prop in flashBridge) {\n if (typeof flashBridge[prop] === \"function\") {\n flashBridge[prop] = null;\n }\n }\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n } else {\n _setTimeout(removeSwfFromIE, 10);\n }\n })();\n } else {\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n }\n }\n _clearTimeoutsAndPolling();\n _flashState.ready = null;\n _flashState.bridge = null;\n _flashState.deactivated = null;\n _zcSwfVersion = undefined;\n }\n };\n /**\n * Map the data format names of the \"clipData\" to Flash-friendly names.\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipDataToFlash = function(clipData) {\n var newClipData = {}, formatMap = {};\n if (!(typeof clipData === \"object\" && clipData)) {\n return;\n }\n for (var dataFormat in clipData) {\n if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === \"string\" && clipData[dataFormat]) {\n switch (dataFormat.toLowerCase()) {\n case \"text/plain\":\n case \"text\":\n case \"air:text\":\n case \"flash:text\":\n newClipData.text = clipData[dataFormat];\n formatMap.text = dataFormat;\n break;\n\n case \"text/html\":\n case \"html\":\n case \"air:html\":\n case \"flash:html\":\n newClipData.html = clipData[dataFormat];\n formatMap.html = dataFormat;\n break;\n\n case \"application/rtf\":\n case \"text/rtf\":\n case \"rtf\":\n case \"richtext\":\n case \"air:rtf\":\n case \"flash:rtf\":\n newClipData.rtf = clipData[dataFormat];\n formatMap.rtf = dataFormat;\n break;\n\n default:\n break;\n }\n }\n }\n return {\n data: newClipData,\n formatMap: formatMap\n };\n };\n /**\n * Map the data format names from Flash-friendly names back to their original \"clipData\" names (via a format mapping).\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipResultsFromFlash = function(clipResults, formatMap) {\n if (!(typeof clipResults === \"object\" && clipResults && typeof formatMap === \"object\" && formatMap)) {\n return clipResults;\n }\n var newResults = {};\n for (var prop in clipResults) {\n if (_hasOwn.call(clipResults, prop)) {\n if (prop === \"errors\") {\n newResults[prop] = clipResults[prop] ? clipResults[prop].slice() : [];\n for (var i = 0, len = newResults[prop].length; i < len; i++) {\n newResults[prop][i].format = formatMap[newResults[prop][i].format];\n }\n } else if (prop !== \"success\" && prop !== \"data\") {\n newResults[prop] = clipResults[prop];\n } else {\n newResults[prop] = {};\n var tmpHash = clipResults[prop];\n for (var dataFormat in tmpHash) {\n if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {\n newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];\n }\n }\n }\n }\n }\n return newResults;\n };\n /**\n * Will look at a path, and will create a \"?noCache={time}\" or \"&noCache={time}\"\n * query param string to return. Does NOT append that string to the original path.\n * This is useful because ExternalInterface often breaks when a Flash SWF is cached.\n *\n * @returns The `noCache` query param with necessary \"?\"/\"&\" prefix.\n * @private\n */\n var _cacheBust = function(path, options) {\n var cacheBust = options == null || options && options.cacheBust === true;\n if (cacheBust) {\n return (path.indexOf(\"?\") === -1 ? \"?\" : \"&\") + \"noCache=\" + _now();\n } else {\n return \"\";\n }\n };\n /**\n * Creates a query string for the FlashVars param.\n * Does NOT include the cache-busting query param.\n *\n * @returns FlashVars query string\n * @private\n */\n var _vars = function(options) {\n var i, len, domain, domains, str = \"\", trustedOriginsExpanded = [];\n if (options.trustedDomains) {\n if (typeof options.trustedDomains === \"string\") {\n domains = [ options.trustedDomains ];\n } else if (typeof options.trustedDomains === \"object\" && \"length\" in options.trustedDomains) {\n domains = options.trustedDomains;\n }\n }\n if (domains && domains.length) {\n for (i = 0, len = domains.length; i < len; i++) {\n if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === \"string\") {\n domain = _extractDomain(domains[i]);\n if (!domain) {\n continue;\n }\n if (domain === \"*\") {\n trustedOriginsExpanded.length = 0;\n trustedOriginsExpanded.push(domain);\n break;\n }\n trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, \"//\" + domain, _window.location.protocol + \"//\" + domain ]);\n }\n }\n }\n if (trustedOriginsExpanded.length) {\n str += \"trustedOrigins=\" + _encodeURIComponent(trustedOriginsExpanded.join(\",\"));\n }\n if (options.forceEnhancedClipboard === true) {\n str += (str ? \"&\" : \"\") + \"forceEnhancedClipboard=true\";\n }\n if (typeof options.swfObjectId === \"string\" && options.swfObjectId) {\n str += (str ? \"&\" : \"\") + \"swfObjectId=\" + _encodeURIComponent(options.swfObjectId);\n }\n if (typeof options.jsVersion === \"string\" && options.jsVersion) {\n str += (str ? \"&\" : \"\") + \"jsVersion=\" + _encodeURIComponent(options.jsVersion);\n }\n return str;\n };\n /**\n * Extract the domain (e.g. \"github.com\") from an origin (e.g. \"https://github.com\") or\n * URL (e.g. \"https://github.com/zeroclipboard/zeroclipboard/\").\n *\n * @returns the domain\n * @private\n */\n var _extractDomain = function(originOrUrl) {\n if (originOrUrl == null || originOrUrl === \"\") {\n return null;\n }\n originOrUrl = originOrUrl.replace(/^\\s+|\\s+$/g, \"\");\n if (originOrUrl === \"\") {\n return null;\n }\n var protocolIndex = originOrUrl.indexOf(\"//\");\n originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);\n var pathIndex = originOrUrl.indexOf(\"/\");\n originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);\n if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === \".swf\") {\n return null;\n }\n return originOrUrl || null;\n };\n /**\n * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.\n *\n * @returns The appropriate script access level.\n * @private\n */\n var _determineScriptAccess = function() {\n var _extractAllDomains = function(origins) {\n var i, len, tmp, resultsArray = [];\n if (typeof origins === \"string\") {\n origins = [ origins ];\n }\n if (!(typeof origins === \"object\" && origins && typeof origins.length === \"number\")) {\n return resultsArray;\n }\n for (i = 0, len = origins.length; i < len; i++) {\n if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {\n if (tmp === \"*\") {\n resultsArray.length = 0;\n resultsArray.push(\"*\");\n break;\n }\n if (resultsArray.indexOf(tmp) === -1) {\n resultsArray.push(tmp);\n }\n }\n }\n return resultsArray;\n };\n return function(currentDomain, configOptions) {\n var swfDomain = _extractDomain(configOptions.swfPath);\n if (swfDomain === null) {\n swfDomain = currentDomain;\n }\n var trustedDomains = _extractAllDomains(configOptions.trustedDomains);\n var len = trustedDomains.length;\n if (len > 0) {\n if (len === 1 && trustedDomains[0] === \"*\") {\n return \"always\";\n }\n if (trustedDomains.indexOf(currentDomain) !== -1) {\n if (len === 1 && currentDomain === swfDomain) {\n return \"sameDomain\";\n }\n return \"always\";\n }\n }\n return \"never\";\n };\n }();\n /**\n * Get the currently active/focused DOM element.\n *\n * @returns the currently active/focused element, or `null`\n * @private\n */\n var _safeActiveElement = function() {\n try {\n return _document.activeElement;\n } catch (err) {\n return null;\n }\n };\n /**\n * Add a class to an element, if it doesn't already have it.\n *\n * @returns The element, with its new class added.\n * @private\n */\n var _addClass = function(element, value) {\n var c, cl, className, classNames = [];\n if (typeof value === \"string\" && value) {\n classNames = value.split(/\\s+/);\n }\n if (element && element.nodeType === 1 && classNames.length > 0) {\n className = (\" \" + (element.className || \"\") + \" \").replace(/[\\t\\r\\n\\f]/g, \" \");\n for (c = 0, cl = classNames.length; c < cl; c++) {\n if (className.indexOf(\" \" + classNames[c] + \" \") === -1) {\n className += classNames[c] + \" \";\n }\n }\n className = className.replace(/^\\s+|\\s+$/g, \"\");\n if (className !== element.className) {\n element.className = className;\n }\n }\n return element;\n };\n /**\n * Remove a class from an element, if it has it.\n *\n * @returns The element, with its class removed.\n * @private\n */\n var _removeClass = function(element, value) {\n var c, cl, className, classNames = [];\n if (typeof value === \"string\" && value) {\n classNames = value.split(/\\s+/);\n }\n if (element && element.nodeType === 1 && classNames.length > 0) {\n if (element.className) {\n className = (\" \" + element.className + \" \").replace(/[\\t\\r\\n\\f]/g, \" \");\n for (c = 0, cl = classNames.length; c < cl; c++) {\n className = className.replace(\" \" + classNames[c] + \" \", \" \");\n }\n className = className.replace(/^\\s+|\\s+$/g, \"\");\n if (className !== element.className) {\n element.className = className;\n }\n }\n }\n return element;\n };\n /**\n * Attempt to interpret the element's CSS styling. If `prop` is `\"cursor\"`,\n * then we assume that it should be a hand (\"pointer\") cursor if the element\n * is an anchor element (\"a\" tag).\n *\n * @returns The computed style property.\n * @private\n */\n var _getStyle = function(el, prop) {\n var value = _getComputedStyle(el, null).getPropertyValue(prop);\n if (prop === \"cursor\") {\n if (!value || value === \"auto\") {\n if (el.nodeName === \"A\") {\n return \"pointer\";\n }\n }\n }\n return value;\n };\n /**\n * Get the absolutely positioned coordinates of a DOM element.\n *\n * @returns Object containing the element's position, width, and height.\n * @private\n */\n var _getElementPosition = function(el) {\n var pos = {\n left: 0,\n top: 0,\n width: 0,\n height: 0\n };\n if (el.getBoundingClientRect) {\n var elRect = el.getBoundingClientRect();\n var pageXOffset = _window.pageXOffset;\n var pageYOffset = _window.pageYOffset;\n var leftBorderWidth = _document.documentElement.clientLeft || 0;\n var topBorderWidth = _document.documentElement.clientTop || 0;\n var leftBodyOffset = 0;\n var topBodyOffset = 0;\n if (_getStyle(_document.body, \"position\") === \"relative\") {\n var bodyRect = _document.body.getBoundingClientRect();\n var htmlRect = _document.documentElement.getBoundingClientRect();\n leftBodyOffset = bodyRect.left - htmlRect.left || 0;\n topBodyOffset = bodyRect.top - htmlRect.top || 0;\n }\n pos.left = elRect.left + pageXOffset - leftBorderWidth - leftBodyOffset;\n pos.top = elRect.top + pageYOffset - topBorderWidth - topBodyOffset;\n pos.width = \"width\" in elRect ? elRect.width : elRect.right - elRect.left;\n pos.height = \"height\" in elRect ? elRect.height : elRect.bottom - elRect.top;\n }\n return pos;\n };\n /**\n * Determine is an element is visible somewhere within the document (page).\n *\n * @returns Boolean\n * @private\n */\n var _isElementVisible = function(el) {\n if (!el) {\n return false;\n }\n var styles = _getComputedStyle(el, null);\n if (!styles) {\n return false;\n }\n var hasCssHeight = _parseFloat(styles.height) > 0;\n var hasCssWidth = _parseFloat(styles.width) > 0;\n var hasCssTop = _parseFloat(styles.top) >= 0;\n var hasCssLeft = _parseFloat(styles.left) >= 0;\n var cssKnows = hasCssHeight && hasCssWidth && hasCssTop && hasCssLeft;\n var rect = cssKnows ? null : _getElementPosition(el);\n var isVisible = styles.display !== \"none\" && styles.visibility !== \"collapse\" && (cssKnows || !!rect && (hasCssHeight || rect.height > 0) && (hasCssWidth || rect.width > 0) && (hasCssTop || rect.top >= 0) && (hasCssLeft || rect.left >= 0));\n return isVisible;\n };\n /**\n * Clear all existing timeouts and interval polling delegates.\n *\n * @returns `undefined`\n * @private\n */\n var _clearTimeoutsAndPolling = function() {\n _clearTimeout(_flashCheckTimeout);\n _flashCheckTimeout = 0;\n _clearInterval(_swfFallbackCheckInterval);\n _swfFallbackCheckInterval = 0;\n };\n /**\n * Reposition the Flash object to cover the currently activated element.\n *\n * @returns `undefined`\n * @private\n */\n var _reposition = function() {\n var htmlBridge;\n if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {\n var pos = _getElementPosition(_currentElement);\n _extend(htmlBridge.style, {\n width: pos.width + \"px\",\n height: pos.height + \"px\",\n top: pos.top + \"px\",\n left: pos.left + \"px\",\n zIndex: \"\" + _getSafeZIndex(_globalConfig.zIndex)\n });\n }\n };\n /**\n * Sends a signal to the Flash object to display the hand cursor if `true`.\n *\n * @returns `undefined`\n * @private\n */\n var _setHandCursor = function(enabled) {\n if (_flashState.ready === true) {\n if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === \"function\") {\n _flashState.bridge.setHandCursor(enabled);\n } else {\n _flashState.ready = false;\n }\n }\n };\n /**\n * Get a safe value for `zIndex`\n *\n * @returns an integer, or \"auto\"\n * @private\n */\n var _getSafeZIndex = function(val) {\n if (/^(?:auto|inherit)$/.test(val)) {\n return val;\n }\n var zIndex;\n if (typeof val === \"number\" && !_isNaN(val)) {\n zIndex = val;\n } else if (typeof val === \"string\") {\n zIndex = _getSafeZIndex(_parseInt(val, 10));\n }\n return typeof zIndex === \"number\" ? zIndex : \"auto\";\n };\n /**\n * Ensure OS-compliant line endings, i.e. \"\\r\\n\" on Windows, \"\\n\" elsewhere\n *\n * @returns string\n * @private\n */\n var _fixLineEndings = function(content) {\n var replaceRegex = /(\\r\\n|\\r|\\n)/g;\n if (typeof content === \"string\" && _globalConfig.fixLineEndings === true) {\n if (_isWindows()) {\n if (/((^|[^\\r])\\n|\\r([^\\n]|$))/.test(content)) {\n content = content.replace(replaceRegex, \"\\r\\n\");\n }\n } else if (/\\r/.test(content)) {\n content = content.replace(replaceRegex, \"\\n\");\n }\n }\n return content;\n };\n /**\n * Attempt to detect if ZeroClipboard is executing inside of a sandboxed iframe.\n * If it is, Flash Player cannot be used, so ZeroClipboard is dead in the water.\n *\n * @see {@link http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Dec/0002.html}\n * @see {@link https://github.com/zeroclipboard/zeroclipboard/issues/511}\n * @see {@link http://zeroclipboard.org/test-iframes.html}\n *\n * @returns `true` (is sandboxed), `false` (is not sandboxed), or `null` (uncertain)\n * @private\n */\n var _detectSandbox = function(doNotReassessFlashSupport) {\n var effectiveScriptOrigin, frame, frameError, previousState = _flashState.sandboxed, isSandboxed = null;\n doNotReassessFlashSupport = doNotReassessFlashSupport === true;\n if (_pageIsFramed === false) {\n isSandboxed = false;\n } else {\n try {\n frame = window.frameElement || null;\n } catch (e) {\n frameError = {\n name: e.name,\n message: e.message\n };\n }\n if (frame && frame.nodeType === 1 && frame.nodeName === \"IFRAME\") {\n try {\n isSandboxed = frame.hasAttribute(\"sandbox\");\n } catch (e) {\n isSandboxed = null;\n }\n } else {\n try {\n effectiveScriptOrigin = document.domain || null;\n } catch (e) {\n effectiveScriptOrigin = null;\n }\n if (effectiveScriptOrigin === null || frameError && frameError.name === \"SecurityError\" && /(^|[\\s\\(\\[@])sandbox(es|ed|ing|[\\s\\.,!\\)\\]@]|$)/.test(frameError.message.toLowerCase())) {\n isSandboxed = true;\n }\n }\n }\n _flashState.sandboxed = isSandboxed;\n if (previousState !== isSandboxed && !doNotReassessFlashSupport) {\n _detectFlashSupport(_ActiveXObject);\n }\n return isSandboxed;\n };\n /**\n * Detect the Flash Player status, version, and plugin type.\n *\n * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}\n * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}\n *\n * @returns `undefined`\n * @private\n */\n var _detectFlashSupport = function(ActiveXObject) {\n var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = \"\";\n /**\n * Derived from Apple's suggested sniffer.\n * @param {String} desc e.g. \"Shockwave Flash 7.0 r61\"\n * @returns {String} \"7.0.61\"\n * @private\n */\n function parseFlashVersion(desc) {\n var matches = desc.match(/[\\d]+/g);\n matches.length = 3;\n return matches.join(\".\");\n }\n function isPepperFlash(flashPlayerFileName) {\n return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\\.dll|libpepflashplayer\\.so|pepperflashplayer\\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === \"chrome.plugin\");\n }\n function inspectPlugin(plugin) {\n if (plugin) {\n hasFlash = true;\n if (plugin.version) {\n flashVersion = parseFlashVersion(plugin.version);\n }\n if (!flashVersion && plugin.description) {\n flashVersion = parseFlashVersion(plugin.description);\n }\n if (plugin.filename) {\n isPPAPI = isPepperFlash(plugin.filename);\n }\n }\n }\n if (_navigator.plugins && _navigator.plugins.length) {\n plugin = _navigator.plugins[\"Shockwave Flash\"];\n inspectPlugin(plugin);\n if (_navigator.plugins[\"Shockwave Flash 2.0\"]) {\n hasFlash = true;\n flashVersion = \"2.0.0.11\";\n }\n } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {\n mimeType = _navigator.mimeTypes[\"application/x-shockwave-flash\"];\n plugin = mimeType && mimeType.enabledPlugin;\n inspectPlugin(plugin);\n } else if (typeof ActiveXObject !== \"undefined\") {\n isActiveX = true;\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.7\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e1) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.6\");\n hasFlash = true;\n flashVersion = \"6.0.21\";\n } catch (e2) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e3) {\n isActiveX = false;\n }\n }\n }\n }\n _flashState.disabled = hasFlash !== true;\n _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion);\n _flashState.version = flashVersion || \"0.0.0\";\n _flashState.pluginType = isPPAPI ? \"pepper\" : isActiveX ? \"activex\" : hasFlash ? \"netscape\" : \"unknown\";\n };\n /**\n * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.\n */\n _detectFlashSupport(_ActiveXObject);\n /**\n * Always assess the `sandboxed` state of the page at important Flash-related moments.\n */\n _detectSandbox(true);\n /**\n * A shell constructor for `ZeroClipboard` client instances.\n *\n * @constructor\n */\n var ZeroClipboard = function() {\n if (!(this instanceof ZeroClipboard)) {\n return new ZeroClipboard();\n }\n if (typeof ZeroClipboard._createClient === \"function\") {\n ZeroClipboard._createClient.apply(this, _args(arguments));\n }\n };\n /**\n * The ZeroClipboard library's version number.\n *\n * @static\n * @readonly\n * @property {string}\n */\n _defineProperty(ZeroClipboard, \"version\", {\n value: \"2.3.0-beta.1\",\n writable: false,\n configurable: true,\n enumerable: true\n });\n /**\n * Update or get a copy of the ZeroClipboard global configuration.\n * Returns a copy of the current/updated configuration.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.config = function() {\n return _config.apply(this, _args(arguments));\n };\n /**\n * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.state = function() {\n return _state.apply(this, _args(arguments));\n };\n /**\n * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.\n *\n * @returns Boolean\n * @static\n */\n ZeroClipboard.isFlashUnusable = function() {\n return _isFlashUnusable.apply(this, _args(arguments));\n };\n /**\n * Register an event listener.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.on = function() {\n return _on.apply(this, _args(arguments));\n };\n /**\n * Unregister an event listener.\n * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.\n * If no `eventType` is provided, it will unregister all listeners for every event type.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.off = function() {\n return _off.apply(this, _args(arguments));\n };\n /**\n * Retrieve event listeners for an `eventType`.\n * If no `eventType` is provided, it will retrieve all listeners for every event type.\n *\n * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`\n */\n ZeroClipboard.handlers = function() {\n return _listeners.apply(this, _args(arguments));\n };\n /**\n * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.\n *\n * @returns For the \"copy\" event, returns the Flash-friendly \"clipData\" object; otherwise `undefined`.\n * @static\n */\n ZeroClipboard.emit = function() {\n return _emit.apply(this, _args(arguments));\n };\n /**\n * Create and embed the Flash object.\n *\n * @returns The Flash object\n * @static\n */\n ZeroClipboard.create = function() {\n return _create.apply(this, _args(arguments));\n };\n /**\n * Self-destruct and clean up everything, including the embedded Flash object.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.destroy = function() {\n return _destroy.apply(this, _args(arguments));\n };\n /**\n * Set the pending data for clipboard injection.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.setData = function() {\n return _setData.apply(this, _args(arguments));\n };\n /**\n * Clear the pending data for clipboard injection.\n * If no `format` is provided, all pending data formats will be cleared.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.clearData = function() {\n return _clearData.apply(this, _args(arguments));\n };\n /**\n * Get a copy of the pending data for clipboard injection.\n * If no `format` is provided, a copy of ALL pending data formats will be returned.\n *\n * @returns `String` or `Object`\n * @static\n */\n ZeroClipboard.getData = function() {\n return _getData.apply(this, _args(arguments));\n };\n /**\n * Sets the current HTML object that the Flash object should overlay. This will put the global\n * Flash object on top of the current element; depending on the setup, this may also set the\n * pending clipboard text data as well as the Flash object's wrapping element's title attribute\n * based on the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.focus = ZeroClipboard.activate = function() {\n return _focus.apply(this, _args(arguments));\n };\n /**\n * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on\n * the setup, this may also unset the Flash object's wrapping element's title attribute based on\n * the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.blur = ZeroClipboard.deactivate = function() {\n return _blur.apply(this, _args(arguments));\n };\n /**\n * Returns the currently focused/\"activated\" HTML element that the Flash object is wrapping.\n *\n * @returns `HTMLElement` or `null`\n * @static\n */\n ZeroClipboard.activeElement = function() {\n return _activeElement.apply(this, _args(arguments));\n };\n /**\n * Keep track of the ZeroClipboard client instance counter.\n */\n var _clientIdCounter = 0;\n /**\n * Keep track of the state of the client instances.\n *\n * Entry structure:\n * _clientMeta[client.id] = {\n * instance: client,\n * elements: [],\n * handlers: {}\n * };\n */\n var _clientMeta = {};\n /**\n * Keep track of the ZeroClipboard clipped elements counter.\n */\n var _elementIdCounter = 0;\n /**\n * Keep track of the state of the clipped element relationships to clients.\n *\n * Entry structure:\n * _elementMeta[element.zcClippingId] = [client1.id, client2.id];\n */\n var _elementMeta = {};\n /**\n * Keep track of the state of the mouse event handlers for clipped elements.\n *\n * Entry structure:\n * _mouseHandlers[element.zcClippingId] = {\n * mouseover: function(event) {},\n * mouseout: function(event) {},\n * mouseenter: function(event) {},\n * mouseleave: function(event) {},\n * mousemove: function(event) {}\n * };\n */\n var _mouseHandlers = {};\n /**\n * Extending the ZeroClipboard configuration defaults for the Client module.\n */\n _extend(_globalConfig, {\n autoActivate: true\n });\n /**\n * The real constructor for `ZeroClipboard` client instances.\n * @private\n */\n var _clientConstructor = function(elements) {\n var client = this;\n client.id = \"\" + _clientIdCounter++;\n _clientMeta[client.id] = {\n instance: client,\n elements: [],\n handlers: {}\n };\n if (elements) {\n client.clip(elements);\n }\n ZeroClipboard.on(\"*\", function(event) {\n return client.emit(event);\n });\n ZeroClipboard.on(\"destroy\", function() {\n client.destroy();\n });\n ZeroClipboard.create();\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.on`.\n * @private\n */\n var _clientOn = function(eventType, listener) {\n var i, len, events, added = {}, meta = _clientMeta[this.id], handlers = meta && meta.handlers;\n if (!meta) {\n throw new Error(\"Attempted to add new listener(s) to a destroyed ZeroClipboard client instance\");\n }\n if (typeof eventType === \"string\" && eventType) {\n events = eventType.toLowerCase().split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n this.on(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].replace(/^on/, \"\");\n added[eventType] = true;\n if (!handlers[eventType]) {\n handlers[eventType] = [];\n }\n handlers[eventType].push(listener);\n }\n if (added.ready && _flashState.ready) {\n this.emit({\n type: \"ready\",\n client: this\n });\n }\n if (added.error) {\n for (i = 0, len = _flashStateErrorNames.length; i < len; i++) {\n if (_flashState[_flashStateErrorNames[i].replace(/^flash-/, \"\")]) {\n this.emit({\n type: \"error\",\n name: _flashStateErrorNames[i],\n client: this\n });\n break;\n }\n }\n if (_zcSwfVersion !== undefined && ZeroClipboard.version !== _zcSwfVersion) {\n this.emit({\n type: \"error\",\n name: \"version-mismatch\",\n jsVersion: ZeroClipboard.version,\n swfVersion: _zcSwfVersion\n });\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.off`.\n * @private\n */\n var _clientOff = function(eventType, listener) {\n var i, len, foundIndex, events, perEventHandlers, meta = _clientMeta[this.id], handlers = meta && meta.handlers;\n if (!handlers) {\n return this;\n }\n if (arguments.length === 0) {\n events = _keys(handlers);\n } else if (typeof eventType === \"string\" && eventType) {\n events = eventType.split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n this.off(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].toLowerCase().replace(/^on/, \"\");\n perEventHandlers = handlers[eventType];\n if (perEventHandlers && perEventHandlers.length) {\n if (listener) {\n foundIndex = perEventHandlers.indexOf(listener);\n while (foundIndex !== -1) {\n perEventHandlers.splice(foundIndex, 1);\n foundIndex = perEventHandlers.indexOf(listener, foundIndex);\n }\n } else {\n perEventHandlers.length = 0;\n }\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.handlers`.\n * @private\n */\n var _clientListeners = function(eventType) {\n var copy = null, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;\n if (handlers) {\n if (typeof eventType === \"string\" && eventType) {\n copy = handlers[eventType] ? handlers[eventType].slice(0) : [];\n } else {\n copy = _deepCopy(handlers);\n }\n }\n return copy;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.emit`.\n * @private\n */\n var _clientEmit = function(event) {\n if (_clientShouldEmit.call(this, event)) {\n if (typeof event === \"object\" && event && typeof event.type === \"string\" && event.type) {\n event = _extend({}, event);\n }\n var eventCopy = _extend({}, _createEvent(event), {\n client: this\n });\n _clientDispatchCallbacks.call(this, eventCopy);\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.clip`.\n * @private\n */\n var _clientClip = function(elements) {\n if (!_clientMeta[this.id]) {\n throw new Error(\"Attempted to clip element(s) to a destroyed ZeroClipboard client instance\");\n }\n elements = _prepClip(elements);\n for (var i = 0; i < elements.length; i++) {\n if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {\n if (!elements[i].zcClippingId) {\n elements[i].zcClippingId = \"zcClippingId_\" + _elementIdCounter++;\n _elementMeta[elements[i].zcClippingId] = [ this.id ];\n if (_globalConfig.autoActivate === true) {\n _addMouseHandlers(elements[i]);\n }\n } else if (_elementMeta[elements[i].zcClippingId].indexOf(this.id) === -1) {\n _elementMeta[elements[i].zcClippingId].push(this.id);\n }\n var clippedElements = _clientMeta[this.id] && _clientMeta[this.id].elements;\n if (clippedElements.indexOf(elements[i]) === -1) {\n clippedElements.push(elements[i]);\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.unclip`.\n * @private\n */\n var _clientUnclip = function(elements) {\n var meta = _clientMeta[this.id];\n if (!meta) {\n return this;\n }\n var clippedElements = meta.elements;\n var arrayIndex;\n if (typeof elements === \"undefined\") {\n elements = clippedElements.slice(0);\n } else {\n elements = _prepClip(elements);\n }\n for (var i = elements.length; i--; ) {\n if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {\n arrayIndex = 0;\n while ((arrayIndex = clippedElements.indexOf(elements[i], arrayIndex)) !== -1) {\n clippedElements.splice(arrayIndex, 1);\n }\n var clientIds = _elementMeta[elements[i].zcClippingId];\n if (clientIds) {\n arrayIndex = 0;\n while ((arrayIndex = clientIds.indexOf(this.id, arrayIndex)) !== -1) {\n clientIds.splice(arrayIndex, 1);\n }\n if (clientIds.length === 0) {\n if (_globalConfig.autoActivate === true) {\n _removeMouseHandlers(elements[i]);\n }\n delete elements[i].zcClippingId;\n }\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.elements`.\n * @private\n */\n var _clientElements = function() {\n var meta = _clientMeta[this.id];\n return meta && meta.elements ? meta.elements.slice(0) : [];\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.destroy`.\n * @private\n */\n var _clientDestroy = function() {\n if (!_clientMeta[this.id]) {\n return;\n }\n this.unclip();\n this.off();\n delete _clientMeta[this.id];\n };\n /**\n * Inspect an Event to see if the Client (`this`) should honor it for emission.\n * @private\n */\n var _clientShouldEmit = function(event) {\n if (!(event && event.type)) {\n return false;\n }\n if (event.client && event.client !== this) {\n return false;\n }\n var meta = _clientMeta[this.id];\n var clippedEls = meta && meta.elements;\n var hasClippedEls = !!clippedEls && clippedEls.length > 0;\n var goodTarget = !event.target || hasClippedEls && clippedEls.indexOf(event.target) !== -1;\n var goodRelTarget = event.relatedTarget && hasClippedEls && clippedEls.indexOf(event.relatedTarget) !== -1;\n var goodClient = event.client && event.client === this;\n if (!meta || !(goodTarget || goodRelTarget || goodClient)) {\n return false;\n }\n return true;\n };\n /**\n * Handle the actual dispatching of events to a client instance.\n *\n * @returns `undefined`\n * @private\n */\n var _clientDispatchCallbacks = function(event) {\n var meta = _clientMeta[this.id];\n if (!(typeof event === \"object\" && event && event.type && meta)) {\n return;\n }\n var async = _shouldPerformAsync(event);\n var wildcardTypeHandlers = meta && meta.handlers[\"*\"] || [];\n var specificTypeHandlers = meta && meta.handlers[event.type] || [];\n var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);\n if (handlers && handlers.length) {\n var i, len, func, context, eventCopy, originalContext = this;\n for (i = 0, len = handlers.length; i < len; i++) {\n func = handlers[i];\n context = originalContext;\n if (typeof func === \"string\" && typeof _window[func] === \"function\") {\n func = _window[func];\n }\n if (typeof func === \"object\" && func && typeof func.handleEvent === \"function\") {\n context = func;\n func = func.handleEvent;\n }\n if (typeof func === \"function\") {\n eventCopy = _extend({}, event);\n _dispatchCallback(func, context, [ eventCopy ], async);\n }\n }\n }\n };\n /**\n * Prepares the elements for clipping/unclipping.\n *\n * @returns An Array of elements.\n * @private\n */\n var _prepClip = function(elements) {\n if (typeof elements === \"string\") {\n elements = [];\n }\n return typeof elements.length !== \"number\" ? [ elements ] : elements;\n };\n /**\n * Add a `mouseover` handler function for a clipped element.\n *\n * @returns `undefined`\n * @private\n */\n var _addMouseHandlers = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n var _suppressMouseEvents = function(event) {\n if (!(event || (event = _window.event))) {\n return;\n }\n if (event._source !== \"js\") {\n event.stopImmediatePropagation();\n event.preventDefault();\n }\n delete event._source;\n };\n var _elementMouseOver = function(event) {\n if (!(event || (event = _window.event))) {\n return;\n }\n _suppressMouseEvents(event);\n ZeroClipboard.focus(element);\n };\n element.addEventListener(\"mouseover\", _elementMouseOver, false);\n element.addEventListener(\"mouseout\", _suppressMouseEvents, false);\n element.addEventListener(\"mouseenter\", _suppressMouseEvents, false);\n element.addEventListener(\"mouseleave\", _suppressMouseEvents, false);\n element.addEventListener(\"mousemove\", _suppressMouseEvents, false);\n _mouseHandlers[element.zcClippingId] = {\n mouseover: _elementMouseOver,\n mouseout: _suppressMouseEvents,\n mouseenter: _suppressMouseEvents,\n mouseleave: _suppressMouseEvents,\n mousemove: _suppressMouseEvents\n };\n };\n /**\n * Remove a `mouseover` handler function for a clipped element.\n *\n * @returns `undefined`\n * @private\n */\n var _removeMouseHandlers = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n var mouseHandlers = _mouseHandlers[element.zcClippingId];\n if (!(typeof mouseHandlers === \"object\" && mouseHandlers)) {\n return;\n }\n var key, val, mouseEvents = [ \"move\", \"leave\", \"enter\", \"out\", \"over\" ];\n for (var i = 0, len = mouseEvents.length; i < len; i++) {\n key = \"mouse\" + mouseEvents[i];\n val = mouseHandlers[key];\n if (typeof val === \"function\") {\n element.removeEventListener(key, val, false);\n }\n }\n delete _mouseHandlers[element.zcClippingId];\n };\n /**\n * Creates a new ZeroClipboard client instance.\n * Optionally, auto-`clip` an element or collection of elements.\n *\n * @constructor\n */\n ZeroClipboard._createClient = function() {\n _clientConstructor.apply(this, _args(arguments));\n };\n /**\n * Register an event listener to the client.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.on = function() {\n return _clientOn.apply(this, _args(arguments));\n };\n /**\n * Unregister an event handler from the client.\n * If no `listener` function/object is provided, it will unregister all handlers for the provided `eventType`.\n * If no `eventType` is provided, it will unregister all handlers for every event type.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.off = function() {\n return _clientOff.apply(this, _args(arguments));\n };\n /**\n * Retrieve event listeners for an `eventType` from the client.\n * If no `eventType` is provided, it will retrieve all listeners for every event type.\n *\n * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`\n */\n ZeroClipboard.prototype.handlers = function() {\n return _clientListeners.apply(this, _args(arguments));\n };\n /**\n * Event emission receiver from the Flash object for this client's registered JavaScript event listeners.\n *\n * @returns For the \"copy\" event, returns the Flash-friendly \"clipData\" object; otherwise `undefined`.\n */\n ZeroClipboard.prototype.emit = function() {\n return _clientEmit.apply(this, _args(arguments));\n };\n /**\n * Register clipboard actions for new element(s) to the client.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.clip = function() {\n return _clientClip.apply(this, _args(arguments));\n };\n /**\n * Unregister the clipboard actions of previously registered element(s) on the page.\n * If no elements are provided, ALL registered elements will be unregistered.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.unclip = function() {\n return _clientUnclip.apply(this, _args(arguments));\n };\n /**\n * Get all of the elements to which this client is clipped.\n *\n * @returns array of clipped elements\n */\n ZeroClipboard.prototype.elements = function() {\n return _clientElements.apply(this, _args(arguments));\n };\n /**\n * Self-destruct and clean up everything for a single client.\n * This will NOT destroy the embedded Flash object.\n *\n * @returns `undefined`\n */\n ZeroClipboard.prototype.destroy = function() {\n return _clientDestroy.apply(this, _args(arguments));\n };\n /**\n * Stores the pending plain text to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setText = function(text) {\n if (!_clientMeta[this.id]) {\n throw new Error(\"Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance\");\n }\n ZeroClipboard.setData(\"text/plain\", text);\n return this;\n };\n /**\n * Stores the pending HTML text to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setHtml = function(html) {\n if (!_clientMeta[this.id]) {\n throw new Error(\"Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance\");\n }\n ZeroClipboard.setData(\"text/html\", html);\n return this;\n };\n /**\n * Stores the pending rich text (RTF) to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setRichText = function(richText) {\n if (!_clientMeta[this.id]) {\n throw new Error(\"Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance\");\n }\n ZeroClipboard.setData(\"application/rtf\", richText);\n return this;\n };\n /**\n * Stores the pending data to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setData = function() {\n if (!_clientMeta[this.id]) {\n throw new Error(\"Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance\");\n }\n ZeroClipboard.setData.apply(this, _args(arguments));\n return this;\n };\n /**\n * Clears the pending data to inject into the clipboard.\n * If no `format` is provided, all pending data formats will be cleared.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.clearData = function() {\n if (!_clientMeta[this.id]) {\n throw new Error(\"Attempted to clear pending clipboard data from a destroyed ZeroClipboard client instance\");\n }\n ZeroClipboard.clearData.apply(this, _args(arguments));\n return this;\n };\n /**\n * Gets a copy of the pending data to inject into the clipboard.\n * If no `format` is provided, a copy of ALL pending data formats will be returned.\n *\n * @returns `String` or `Object`\n */\n ZeroClipboard.prototype.getData = function() {\n if (!_clientMeta[this.id]) {\n throw new Error(\"Attempted to get pending clipboard data from a destroyed ZeroClipboard client instance\");\n }\n return ZeroClipboard.getData.apply(this, _args(arguments));\n };\n if (typeof define === \"function\" && define.amd) {\n define(function() {\n return ZeroClipboard;\n });\n } else if (typeof module === \"object\" && module && typeof module.exports === \"object\" && module.exports) {\n module.exports = ZeroClipboard;\n } else {\n window.ZeroClipboard = ZeroClipboard;\n }\n})(function() {\n return this || window;\n}());"]} \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.swf b/SecurityShepherdCore/src/jsp/js/zeroclipboard/ZeroClipboard.swf new file mode 100644 index 0000000000000000000000000000000000000000..9c2171a241cf4dce4ed9dadce26ae15034c63428 GIT binary patch literal 6584 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 Date: Sat, 26 Sep 2015 03:33:18 +0100 Subject: [PATCH 027/112] Improving / Adding Getter JUnit Tests --- .../src/utils/ModulePlan.java | 4 +- .../test/dbProcs/GetterTest.java | 270 ++++++++++++++---- 2 files changed, 214 insertions(+), 60 deletions(-) diff --git a/SecurityShepherdCore/src/utils/ModulePlan.java b/SecurityShepherdCore/src/utils/ModulePlan.java index 6fd2964e3..ab6d238ff 100644 --- a/SecurityShepherdCore/src/utils/ModulePlan.java +++ b/SecurityShepherdCore/src/utils/ModulePlan.java @@ -22,8 +22,8 @@ */ public class ModulePlan { - public static boolean openFloor = true; - public static boolean incrementalFloor = false; + public static boolean openFloor = false; + public static boolean incrementalFloor = true; public static boolean tornyFloor = false; public static boolean isIncrementalFloor() diff --git a/SecurityShepherdCore/test/dbProcs/GetterTest.java b/SecurityShepherdCore/test/dbProcs/GetterTest.java index 3398476d4..f8ddc98f0 100644 --- a/SecurityShepherdCore/test/dbProcs/GetterTest.java +++ b/SecurityShepherdCore/test/dbProcs/GetterTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.*; +import java.sql.ResultSet; import java.util.ArrayList; import java.util.Locale; @@ -75,68 +76,87 @@ public void testCheckPlayerResultWhenModuleNotOpened() { @Test public void testCheckPlayerResultWhenModuleWhenOpened() { String csrfChallengeThree = new String("5ca9115f3279b9b9f3308eb6a59a4fcd374846d6"); - - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeThree, adminUserId).isEmpty()) + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - String test = Getter.checkPlayerResult(applicationRoot, csrfChallengeThree, adminUserId); - if(test == null) + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, csrfChallengeThree, adminUserId).isEmpty()) { - fail("Function says Admin has not opened module"); // Admin Should have opened and not completed CSRF Three. Ensure DB is clean + String test = Getter.checkPlayerResult(applicationRoot, csrfChallengeThree, adminUserId); + if(test == null) + { + fail("Function says Admin has not opened module"); // Admin Should have opened and not completed CSRF Three. Ensure DB is clean + } + else + return; //Pass } else - return; //Pass + fail("Could not Mark CSRF 3 as Opened by Default admin"); } else - fail("Could not Mark CSRF 3 as Opened by Default admin"); + { + fail("Could not Mark Modules As Opened"); + } } @Test public void testCheckPlayerResultWhenModuleNotComplete() { String contentProviderLeakage = new String("5b461ebe2e5e2797740cb3e9c7e3f93449a93e3a"); - - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, contentProviderLeakage, adminUserId).isEmpty()) + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, contentProviderLeakage, adminUserId); - if(checkPlayerResultTest != null) - return; //Pass - else - { - fail("Function says Admin has not opened challenge or has completed challenge before"); - } + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, contentProviderLeakage, adminUserId).isEmpty()) + { + String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, contentProviderLeakage, adminUserId); + if(checkPlayerResultTest != null) + return; //Pass + else + { + fail("Function says Admin has not opened challenge or has completed challenge before"); + } + } + else + fail("Could not Content Provider Leakage Lesson as Opened by Default admin"); } else - fail("Could not Mark Data Storage Lesson as Opened by Default admin"); + { + fail("Could not mark modules as opened"); + } } @Test public void testCheckPlayerResultWhenModuleComplete() { String dataStorageLessonId = new String("53a53a66cb3bf3e4c665c442425ca90e29536edd"); - - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, dataStorageLessonId, adminUserId).isEmpty()) + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - //Then, Mark the Challenge Complete for Default Admin (Insecure Data Storage Lesson) - String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, dataStorageLessonId, adminUserId, "Feedback is Disabled", 1, 1, 1); - if (markLevelCompleteTest != null) + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, dataStorageLessonId, adminUserId).isEmpty()) { - String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, dataStorageLessonId, adminUserId); - log.debug("checkPlayerResultTest" + checkPlayerResultTest); - if(checkPlayerResultTest == null) - return; //Pass - else + //Then, Mark the Challenge Complete for Default Admin (Insecure Data Storage Lesson) + String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, dataStorageLessonId, adminUserId, "Feedback is Disabled", 1, 1, 1); + if (markLevelCompleteTest != null) { - fail("Function says Admin has not completed module"); //Even though this test just marked it as Completed + String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, dataStorageLessonId, adminUserId); + log.debug("checkPlayerResultTest" + checkPlayerResultTest); + if(checkPlayerResultTest == null) + return; //Pass + else + { + fail("Function says Admin has not completed module"); //Even though this test just marked it as Completed + } } + else + fail("Could not mark data storage lesson as complete Default admin"); } else - fail("Could not mark data storage lesson as complete Default admin"); + fail("Could not Mark Data Storage Lesson as Opened by Default admin"); } else - fail("Could not Mark Data Storage Lesson as Opened by Default admin"); + fail("Could not Open All Modules"); } @@ -144,30 +164,37 @@ public void testCheckPlayerResultWhenModuleComplete() public void testIsCsrfLevelCompleteIncrementedCounter() { String csrfChallengeOne = new String("20e755179a5840be5503d42bb3711716235005ea"); //CSRF Challenge 1 (Should have CSRF Counter of 0 for Default Admin User - - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeOne, adminUserId).isEmpty()) + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - //Increment Challenge CSRF Counter - if(Setter.updateCsrfCounter(applicationRoot, csrfChallengeOne, adminUserId)) + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, csrfChallengeOne, adminUserId).isEmpty()) { - if(Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeOne, adminUserId)) + //Increment Challenge CSRF Counter + if(Setter.updateCsrfCounter(applicationRoot, csrfChallengeOne, adminUserId)) { - return; //Pass, because CSRF level is completed after the admin CSRF counter was incremented + if(Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeOne, adminUserId)) + { + return; //Pass, because CSRF level is completed after the admin CSRF counter was incremented + } + else + { + fail("CSRF 1 not completed after successful increment"); + } } else { - fail("CSRF 1 not completed after successful increment"); + fail("Could not Increment default Admin Counter for CSRF 1"); } } else { - fail("Could not Increment default Admin Counter for CSRF 1"); + fail("Could not Mark CSRF 1 as opened by default Admin"); } } else { - fail("Could not Mark CSRF 1 as opened by default Admin"); + fail("Could not Mark Modules as Opened"); } } @@ -175,21 +202,29 @@ public void testIsCsrfLevelCompleteIncrementedCounter() public void testIsCsrfLevelCompleteWithoutIncrementedCounter() { String csrfChallengeTwo = new String("94cd2de560d89ef59fc450ecc647ff4d4a55c15d"); //CSRF Challenge 2 (Should have CSRF Counter of 0 for Default Admin User - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeTwo, adminUserId).isEmpty()) + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - if(!Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeTwo, adminUserId)) + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, csrfChallengeTwo, adminUserId).isEmpty()) { - return; //Pass, because CSRF level is not completed because the CSRF Counter for the default admin is 0 + if(!Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeTwo, adminUserId)) + { + return; //Pass, because CSRF level is not completed because the CSRF Counter for the default admin is 0 + } + else + { + fail("CSRF 2 marked completed without increment"); // CSRF 2 Challenge should have a counter of 0 and should not return true. + } } else { - fail("CSRF 2 marked completed without increment"); // CSRF 2 Challenge should have a counter of 0 and should not return true. + fail("Could not Mark CSRF 2 as opened by default Admin"); } } else { - fail("Could not Mark CSRF 2 as opened by default Admin"); + fail("Could not mark All Modules as Opened"); } } @@ -351,16 +386,102 @@ public void testGetClassCount() { } } - /* + @Test public void testGetClassInfoString() { - fail("Not yet implemented"); + if(Setter.classCreate(applicationRoot, "NewClassForGetInfo", "2015")) + { + try + { + ResultSet rs = Getter.getClassInfo(applicationRoot); + if(rs.next()) + { + if(!rs.getString(1).isEmpty()) + { + log.debug("PASS: Class Information was returned"); + } + else + { + fail("Data in Class Info Result Set was Blank"); + } + } + else + { + fail("No Rows In Class Info Result Set"); + } + rs.close(); + } + catch(Exception e) + { + log.fatal("ClassInfo Failure: " + e.toString()); + fail("Could not open ClassInfo Result Set"); + } + } + else + { + fail("Could not Create Class"); + } } - + @Test - public void testGetClassInfoStringString() { - fail("Not yet implemented"); + public void testGetClassInfoStringString() + { + String classId = new String(); + if(Setter.classCreate(applicationRoot, "NewClassForGetInfo2", "2015")) + { + try + { + ResultSet rs = Getter.getClassInfo(applicationRoot); + while(rs.next()) + { + if(rs.getString(2).equalsIgnoreCase("NewClassForGetInfo2")) + { + classId = rs.getString(1); + break; + } + } + rs.close(); + if(classId.isEmpty()) + { + fail("Could not Find Class ID in Get Info Result"); + } + else + { + String[] classInfo = Getter.getClassInfo(applicationRoot, classId); + if(classInfo[0].equalsIgnoreCase("NewClassForGetInfo2") && classInfo[1].equalsIgnoreCase("2015")) + { + log.debug("PASS: Expected Data Returned from getClassInfo"); + } + else + { + if(!classInfo[0].equalsIgnoreCase("NewClassForGetInfo2")) + { + fail("Incorrect Class Name returned from getClassInfo"); + } + else if(!classInfo[1].equalsIgnoreCase("2015")) + { + fail("Incorrect Class Year returned from getClassInfo"); + } + else + { + fail("Unexpected Failure"); + } + } + } + } + catch(Exception e) + { + log.fatal("ClassInfo Failure: " + e.toString()); + fail("Could not open ClassInfo Result Set"); + } + } + else + { + fail("Could not Create Class"); + } } + + /* @Test public void testGetCsrfForumWithIframe() { @@ -396,12 +517,45 @@ public void testGetJsonScore() { public void testGetLessons() { fail("Not yet implemented"); } - + */ @Test - public void testGetModuleAddress() { - fail("Not yet implemented"); + public void testGetModuleAddress() + { + String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, insecureCryptoLesson, adminUserId).isEmpty()) + { + log.debug("PASS: Could mark level open when level was marked as open"); + } + else + fail("Could not Insecure Crypto Lesson as Opened by Default admin"); + } + else + fail("Could not Open All Modules"); } - + + @Test + public void testGetModuleAddressWhenClosed() + { + String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); + //Open all Modules First so that the Module Can Be Opened + if(Setter.closeAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(Getter.getModuleAddress(applicationRoot, insecureCryptoLesson, adminUserId).isEmpty()) + { + log.debug("PASS: Could not mark level open when level was marked as open"); + } + else + fail("Could Mark Insecure Crypto Lesson as Opened by Default admin when marked as closed"); + } + else + fail("Could not Open All Modules"); + } + /* @Test public void testGetModuleCategory() { fail("Not yet implemented"); From 6fb215fda7aa16cdcd1fc536590d57404ebf8665 Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Sat, 26 Sep 2015 03:35:08 +0100 Subject: [PATCH 028/112] Fixing issue with HTML for around key being encoded --- .../lesson/UnvalidatedForwardsLesson.java | 331 +++++++++--------- 1 file changed, 164 insertions(+), 167 deletions(-) diff --git a/SecurityShepherdCore/src/servlets/module/lesson/UnvalidatedForwardsLesson.java b/SecurityShepherdCore/src/servlets/module/lesson/UnvalidatedForwardsLesson.java index f17049f5e..b2967635f 100644 --- a/SecurityShepherdCore/src/servlets/module/lesson/UnvalidatedForwardsLesson.java +++ b/SecurityShepherdCore/src/servlets/module/lesson/UnvalidatedForwardsLesson.java @@ -1,167 +1,164 @@ -package servlets.module.lesson; - -import java.io.IOException; -import java.io.PrintWriter; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Locale; -import java.util.ResourceBundle; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.apache.log4j.Logger; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Encoder; - -import utils.FindXSS; -import utils.Hash; -import utils.ShepherdLogManager; -import utils.Validate; -import dbProcs.Getter; -/** - * Unvalidated Redirects and Forwards Lesson - *

    - * This file is part of the Security Shepherd Project. - * - * The Security Shepherd project is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version.
    - * - * The Security Shepherd project is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details.
    - * - * You should have received a copy of the GNU General Public License - * along with the Security Shepherd project. If not, see . - * @author Mark Denihan - * - */ -public class UnvalidatedForwardsLesson extends HttpServlet -{ - private static final long serialVersionUID = 1L; - private static org.apache.log4j.Logger log = Logger.getLogger(UnvalidatedForwardsLesson.class); - private static String levelName = "Unvalidated Redirects and Forwards Lesson"; - private static String levelHash = "f15f2766c971e16e68aa26043e6016a0a7f6879283c873d9476a8e7e94ea736f"; - - /** - * User submission is parsed for a valid URL. This is then used to construct a URL object. This URL object is then checked to ensure a valid attack - * @param tempId User's session stored temporary id - * @param messageForAdmin Users lesson submission - */ - public void doPost (HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException - { - //Setting IpAddress To Log and taking header for original IP if forwarded from proxy - ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); - log.debug(levelName +" Servlet Accessed"); - PrintWriter out = response.getWriter(); - out.print(getServletInfo()); - Encoder encoder = ESAPI.encoder(); - - //Translation Stuff - Locale locale = new Locale(Validate.validateLanguage(request.getSession())); - ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); - ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.lessons.unvalidatedRedirect", locale); - - try - { - HttpSession ses = request.getSession(true); - if(Validate.validateSession(ses)) - { - ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); - log.debug("Current User: " + ses.getAttribute("userName").toString()); - Cookie tokenCookie = Validate.getToken(request.getCookies()); - Object tokenParmeter = request.getParameter("csrfToken"); - if(Validate.validateTokens(tokenCookie, tokenParmeter)) - { - String tempId = (String) ses.getAttribute("tempId"); - log.debug("tempId = " + tempId); - String userName = (String) ses.getAttribute("userName"); - String messageForAdmin = request.getParameter("messageForAdmin").toLowerCase(); - log.debug("User Submitted - " + messageForAdmin); - String htmlOutput = new String(); - boolean validUrl = true; - boolean validSolution = false; - boolean validAttack = false; - try - { - URL csrfUrl = new URL(messageForAdmin); - log.debug("Url Host: " + csrfUrl.getHost()); - log.debug("Url Port: " + csrfUrl.getPort()); - log.debug("Url Path: " + csrfUrl.getPath()); - log.debug("Url Query: " + csrfUrl.getQuery()); - validSolution = csrfUrl.getPath().toLowerCase().equalsIgnoreCase("/user/redirect"); - if(!validSolution) - log.debug("Invalid Solution: Bad Path or Above"); - validSolution = csrfUrl.getQuery().toLowerCase().startsWith(("to=").toLowerCase()) && validSolution; - if(!validSolution) - log.debug("Invalid Solution: Bad Query or Above"); - if(validSolution) - { - log.debug("Redirect URL Correct: Now checking the Redirected URL for valid CSRF Attack"); - int csrfStart = 0; - int csrfEnd = 0; - csrfStart = csrfUrl.getQuery().indexOf("to=") + 3; - csrfEnd = csrfUrl.getQuery().indexOf("&"); - if(csrfEnd == -1) - { - csrfEnd = csrfUrl.getQuery().length(); - } - String csrfAttack = csrfUrl.getQuery().substring(csrfStart, csrfEnd); - log.debug("csrfAttack Found to be: " + csrfAttack); - validAttack = FindXSS.findCsrfAttackUrl(csrfAttack, "/root/grantComplete/unvalidatedredirectlesson", "userId", tempId); - } - } - catch(MalformedURLException e) - { - log.error("Invalid URL: " + e.toString()); - validUrl = false; - validSolution = false; - validAttack = false; - messageForAdmin = ""; - htmlOutput="Invalid URL"; - } - - if(validSolution && validAttack) - { - htmlOutput = "

    " + bundle.getString("result.wellDone") + "

    " + - "

    " + bundle.getString("result.youDidIt") + "
    " + - bundle.getString("result.resultKey") + " " + - encoder.encodeForHTML( - Hash.generateUserSolution( - Getter.getModuleResultFromHash(getServletContext().getRealPath(""), levelHash - ), (String)ses.getAttribute("userName") - ) - ) + - ""; - } - if(validUrl) - { - log.debug("Adding message to Html: " + messageForAdmin); - htmlOutput += "

    " + bundle.getString("response.messageSent") + "

    " + - "

    " + - "
    " + bundle.getString("response.sentTo") + ": administrator@SecurityShepherd.com
    " + bundle.getString("response.message") + ": " + encoder.encodeForHTML("" + bundle.getString("response.linkFrom") + " " + userName) + - "

    "; - } - log.debug("Outputting HTML"); - out.write(htmlOutput); - } - } - } - catch(Exception e) - { - out.write(errors.getString("error.funky")); - log.fatal(levelName + " - " + e.toString()); - } - log.debug("End of " + levelName + " Servlet"); - } -} +package servlets.module.lesson; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Locale; +import java.util.ResourceBundle; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.log4j.Logger; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Encoder; + +import utils.FindXSS; +import utils.Hash; +import utils.ShepherdLogManager; +import utils.Validate; +import dbProcs.Getter; +/** + * Unvalidated Redirects and Forwards Lesson + *

    + * This file is part of the Security Shepherd Project. + * + * The Security Shepherd project is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version.
    + * + * The Security Shepherd project is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details.
    + * + * You should have received a copy of the GNU General Public License + * along with the Security Shepherd project. If not, see . + * @author Mark Denihan + * + */ +public class UnvalidatedForwardsLesson extends HttpServlet +{ + private static final long serialVersionUID = 1L; + private static org.apache.log4j.Logger log = Logger.getLogger(UnvalidatedForwardsLesson.class); + private static String levelName = "Unvalidated Redirects and Forwards Lesson"; + private static String levelHash = "f15f2766c971e16e68aa26043e6016a0a7f6879283c873d9476a8e7e94ea736f"; + + /** + * User submission is parsed for a valid URL. This is then used to construct a URL object. This URL object is then checked to ensure a valid attack + * @param tempId User's session stored temporary id + * @param messageForAdmin Users lesson submission + */ + public void doPost (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + //Setting IpAddress To Log and taking header for original IP if forwarded from proxy + ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For")); + log.debug(levelName +" Servlet Accessed"); + PrintWriter out = response.getWriter(); + out.print(getServletInfo()); + Encoder encoder = ESAPI.encoder(); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.lessons.unvalidatedRedirect", locale); + + try + { + HttpSession ses = request.getSession(true); + if(Validate.validateSession(ses)) + { + ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); + log.debug("Current User: " + ses.getAttribute("userName").toString()); + Cookie tokenCookie = Validate.getToken(request.getCookies()); + Object tokenParmeter = request.getParameter("csrfToken"); + if(Validate.validateTokens(tokenCookie, tokenParmeter)) + { + String tempId = (String) ses.getAttribute("tempId"); + log.debug("tempId = " + tempId); + String userName = (String) ses.getAttribute("userName"); + String messageForAdmin = request.getParameter("messageForAdmin").toLowerCase(); + log.debug("User Submitted - " + messageForAdmin); + String htmlOutput = new String(); + boolean validUrl = true; + boolean validSolution = false; + boolean validAttack = false; + try + { + URL csrfUrl = new URL(messageForAdmin); + log.debug("Url Host: " + csrfUrl.getHost()); + log.debug("Url Port: " + csrfUrl.getPort()); + log.debug("Url Path: " + csrfUrl.getPath()); + log.debug("Url Query: " + csrfUrl.getQuery()); + validSolution = csrfUrl.getPath().toLowerCase().equalsIgnoreCase("/user/redirect"); + if(!validSolution) + log.debug("Invalid Solution: Bad Path or Above"); + validSolution = csrfUrl.getQuery().toLowerCase().startsWith(("to=").toLowerCase()) && validSolution; + if(!validSolution) + log.debug("Invalid Solution: Bad Query or Above"); + if(validSolution) + { + log.debug("Redirect URL Correct: Now checking the Redirected URL for valid CSRF Attack"); + int csrfStart = 0; + int csrfEnd = 0; + csrfStart = csrfUrl.getQuery().indexOf("to=") + 3; + csrfEnd = csrfUrl.getQuery().indexOf("&"); + if(csrfEnd == -1) + { + csrfEnd = csrfUrl.getQuery().length(); + } + String csrfAttack = csrfUrl.getQuery().substring(csrfStart, csrfEnd); + log.debug("csrfAttack Found to be: " + csrfAttack); + validAttack = FindXSS.findCsrfAttackUrl(csrfAttack, "/root/grantComplete/unvalidatedredirectlesson", "userId", tempId); + } + } + catch(MalformedURLException e) + { + log.error("Invalid URL: " + e.toString()); + validUrl = false; + validSolution = false; + validAttack = false; + messageForAdmin = ""; + htmlOutput="Invalid URL"; + } + + if(validSolution && validAttack) + { + htmlOutput = "

    " + bundle.getString("result.wellDone") + "

    " + + "

    " + bundle.getString("result.youDidIt") + "
    " + + bundle.getString("result.resultKey") + " " + + Hash.generateUserSolution( + Getter.getModuleResultFromHash(getServletContext().getRealPath(""), levelHash), + (String)ses.getAttribute("userName")) + +""; + } + if(validUrl) + { + log.debug("Adding message to Html: " + messageForAdmin); + htmlOutput += "

    " + bundle.getString("response.messageSent") + "

    " + + "

    " + + "
    " + bundle.getString("response.sentTo") + ": administrator@SecurityShepherd.com
    " + bundle.getString("response.message") + ": " + encoder.encodeForHTML("" + bundle.getString("response.linkFrom") + " " + userName) + + "

    "; + } + log.debug("Outputting HTML"); + out.write(htmlOutput); + } + } + } + catch(Exception e) + { + out.write(errors.getString("error.funky")); + log.fatal(levelName + " - " + e.toString()); + } + log.debug("End of " + levelName + " Servlet"); + } +} From 2b9d155c88103b3f6fa820074b839d76013e79e0 Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Sun, 27 Sep 2015 00:32:15 +0100 Subject: [PATCH 029/112] Update README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 5b3329e5c..521ab10b5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,28 @@ The [OWASP Security Shepherd Project](http://bit.ly/owaspSecurityShepherd) is a # Where can I download Security Shepherd? You can download Security Shepherd VM's or Manual Installation Packs from [SourceForge](http://bit.ly/1JbtQtP) +There is also a docker image available from [Dockerhub](https://hub.docker.com/r/ismisepaul/securityshepherd/) you can pull it down with +`docker pull ismisepaul/securityshepherd` + +Note: You'll need to get a shell on your docker container and run mysql and tomcat manually; +```BASH +docker run -i -p 80:80 -p 443:443 -t ismisepaul/securityshepherd /bin/bash +``` +```BASH +/usr/bin/mysqld_safe & +service tomcat7 start +``` +If you don't have authbind install and configured e.g. on Ubuntu do the following; +```BASH +sudo apt-get install authbind && \ +touch /etc/authbind/byport/80 && \ +touch /etc/authbind/byport/443 && \ +chmod 550 /etc/authbind/byport/80 && \ +chmod 550 /etc/authbind/byport/443 && \ +chown tomcat7 /etc/authbind/byport/80 && \ +chown tomcat7 /etc/authbind/byport/443 +``` + # How do I setup Security Shepherd? We've got fully automated and step by step walkthroughs on our [wiki page](https://github.com/markdenihan/owaspSecurityShepherd/wiki) to help you get Security Shepherd up and running. From e7388be96593ec8c3f159507819dfac170c86a7a Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Sun, 27 Sep 2015 00:33:46 +0100 Subject: [PATCH 030/112] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 521ab10b5..e87a7397e 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,11 @@ The [OWASP Security Shepherd Project](http://bit.ly/owaspSecurityShepherd) is a web and mobile application security training platform. Security Shepherd has been designed to foster and improve security awareness among a varied skill-set demographic. The aim of this project is to take AppSec novices or experienced engineers and sharpen their penetration testing skill set to security expert status. # Where can I download Security Shepherd? + +### Virtual Machine You can download Security Shepherd VM's or Manual Installation Packs from [SourceForge](http://bit.ly/1JbtQtP) +### Docker There is also a docker image available from [Dockerhub](https://hub.docker.com/r/ismisepaul/securityshepherd/) you can pull it down with `docker pull ismisepaul/securityshepherd` From cb85451448db434f2cc27083ebf7ab37b17e527a Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Sun, 27 Sep 2015 00:35:03 +0100 Subject: [PATCH 031/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e87a7397e..480bf6a7d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ The [OWASP Security Shepherd Project](http://bit.ly/owaspSecurityShepherd) is a # Where can I download Security Shepherd? -### Virtual Machine +### Virtual Machine or Manual Setup You can download Security Shepherd VM's or Manual Installation Packs from [SourceForge](http://bit.ly/1JbtQtP) ### Docker From 241a985c9e5586e9b5e86cb34a3ecfb5676e9d7d Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Mon, 28 Sep 2015 09:10:44 +0100 Subject: [PATCH 032/112] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 480bf6a7d..b44e8ff9b 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,15 @@ docker run -i -p 80:80 -p 443:443 -t ismisepaul/securityshepherd /bin/bash /usr/bin/mysqld_safe & service tomcat7 start ``` -If you don't have authbind install and configured e.g. on Ubuntu do the following; +If you don't have ```authbind``` installed and configured on your host machine e.g. on Ubuntu you'll need to do the following; ```BASH -sudo apt-get install authbind && \ -touch /etc/authbind/byport/80 && \ -touch /etc/authbind/byport/443 && \ -chmod 550 /etc/authbind/byport/80 && \ -chmod 550 /etc/authbind/byport/443 && \ -chown tomcat7 /etc/authbind/byport/80 && \ -chown tomcat7 /etc/authbind/byport/443 +sudo apt-get install authbind +touch /etc/authbind/byport/80 +touch /etc/authbind/byport/443 +chmod 550 /etc/authbind/byport/80 +chmod 550 /etc/authbind/byport/443 +chown tomcat7 /etc/authbind/byport/80 +chown tomcat7 /etc/authbind/byport/443 ``` # How do I setup Security Shepherd? From 5766b4a6fa804c1e215119dc1c30320e4d936193 Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Tue, 29 Sep 2015 21:13:06 +0100 Subject: [PATCH 033/112] Updating Challenges for translations --- ...f743630b46aece3784d61adc56498f7603ccd7cb8ae92629.properties | 0 ...167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.properties | 0 ...9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.properties | 0 ...b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.properties | 0 ...f1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.properties | 0 ...f54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.properties | 0 ...25b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.properties | 0 ...26237f1308afe54491d1bacf9fffa0b21a072b03c5bafe66.properties | 0 ...3d67b816538613e64f7d0f51c432a164efc8418513711b0a.properties | 0 ...4cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.properties | 0 .../insecureCryptoStorage/insecureCryptoStorage.properties | 0 ...f26236112f8e31f32939bd496ffe8c9f7b564bce32bd5e3a8c2f751.jsp | 2 +- ...888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp | 3 ++- ...b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp | 3 ++- ...c6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp | 2 +- ...5b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.jsp | 2 +- ...0180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp | 3 ++- ...e0ff635dcaeec8533085e5eae5d25e8646dcd4b05009353c9cf9c80.jsp | 2 +- ...727b74b9a2522a5ca3bf993087de5a0ac72adff216002abf79146fa.jsp | 2 +- ...fb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp | 3 ++- ...8278a57dcfaae3b8da0c78d5a70c2d38ea9d8b3e14db3aea01afcbb.jsp | 2 +- ...149e507c75b5a54e558470469d7024929cf78d570cd16c03bee3569.jsp | 2 +- ...6bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp | 3 ++- ...68f64107db3bbc7ee74e3f1336d350c4e1e51d4eda5b52dddf86c99.jsp | 2 +- ...5ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp | 3 ++- ...744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp | 3 ++- ...72adf3bf347cbc37c34489287969d5ba504ac2439915184d6e5dc49.jsp | 2 +- ...303bbef8b5cabab60b1060ac41f0d96f53b6ea54705bb1ea4316334.jsp | 2 +- ...00087d56c844ed9474c671f8999680556c127a19ee79fa5d7a132e1.jsp | 2 +- ...a82543d480a63e55ebb8fef3209c5d648b54d1276813cd072815df3.jsp | 2 +- ...8f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.jsp | 2 +- ...0f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp | 3 ++- ...6cd78fecc3563ba2873d944aacb7b72f28693a23f9949ac310648b5.jsp | 2 +- ...397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp | 3 ++- ...df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp | 3 ++- ...18e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.jsp | 2 +- ...cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.jsp | 2 +- ...891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp | 2 +- ...c79bf10dd54ee62de148ab44b7bd028009a908ce3f1b4d019886d0e.jsp | 2 +- ...742cf2c0880d4098146c4dde25ebd8ceab51807bad88ff47c316ece.jsp | 2 +- ...90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp | 2 +- ...c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp | 2 +- ...734a10897d672c1ed3dd9417e0530a4e0186c27699f54637c7fb5d4.jsp | 2 +- ...3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp | 2 +- ...ef42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp | 2 +- ...afdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp | 2 +- ...264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp | 2 +- ...cf21886b685184ee222ea8e0a60589c3940afd6ebf433469e997caf.jsp | 2 +- ...422af2e6b3c5d90e4c11e7b4575a7bc12ee6d0a384ac2469449e8fa.jsp | 2 +- ...4172cbc245300d3bc22937090ebd3769466a501a5e7ac605b9f34b7.jsp | 2 +- ...f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp | 2 +- ...278f44b05dc06486dd06134dac7d843367763a3226c9081f537fb2f.jsp | 2 +- ...033fa380e378299b6a998c759646bd8aea02511482b8ce5d707f93a.jsp | 2 +- ...bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.jsp | 2 +- ...c40b8e0169e433366350f55c77b82878329570efa894838980de5b4.jsp | 2 +- ...04d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp | 2 +- ...b8bf7abb9c85a87cf95c23f7fadcf08a092e05620c9968bd60fcba6.jsp | 2 +- ...c6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp | 2 +- ...7832cdc6e91358dca3f53039d4489c94df2ee280d6203b389dd5671.jsp | 2 +- ...44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp | 2 +- ...5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp | 2 +- ...26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.jsp | 2 +- ...45fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp | 2 +- ...cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c.jsp | 2 +- ...4e25b854906d88f622170c1c06817e72b526b3d1e9a6085f429cf52.jsp | 2 +- ...f049bcf65cdcac72269eeac25dbb2a6887bdb38873e57d0ef447bc3.jsp | 2 +- ...888e807ff0f0eff751d6034bafe48954575c3a6563cb47a85b1e888.jsp | 2 +- ...df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp | 2 +- ...e75ec92495e0caf9a544edb2ee8f624249f3e920663edb733f15cd7.jsp | 2 +- ...8a13604705d608fb3171ebf49bc18753b0ec34b8dff5e4f9147eb5e.jsp | 2 +- ...e112dd09a6c430a167415820adc5633256a7b44a7d1e262db105e3c.jsp | 2 +- 71 files changed, 70 insertions(+), 60 deletions(-) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/d63c2fb5da9b81ca26237f1308afe54491d1bacf9fffa0b21a072b03c5bafe66.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.properties (100%) rename SecurityShepherdCore/src/i18n/challenges/{sqli => injection}/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.properties (100%) create mode 100644 SecurityShepherdCore/src/i18n/challenges/insecureCryptoStorage/insecureCryptoStorage.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.properties b/SecurityShepherdCore/src/i18n/challenges/injection/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.properties b/SecurityShepherdCore/src/i18n/challenges/injection/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.properties b/SecurityShepherdCore/src/i18n/challenges/injection/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.properties b/SecurityShepherdCore/src/i18n/challenges/injection/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.properties b/SecurityShepherdCore/src/i18n/challenges/injection/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.properties b/SecurityShepherdCore/src/i18n/challenges/injection/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.properties b/SecurityShepherdCore/src/i18n/challenges/injection/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/d63c2fb5da9b81ca26237f1308afe54491d1bacf9fffa0b21a072b03c5bafe66.properties b/SecurityShepherdCore/src/i18n/challenges/injection/d63c2fb5da9b81ca26237f1308afe54491d1bacf9fffa0b21a072b03c5bafe66.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/d63c2fb5da9b81ca26237f1308afe54491d1bacf9fffa0b21a072b03c5bafe66.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/d63c2fb5da9b81ca26237f1308afe54491d1bacf9fffa0b21a072b03c5bafe66.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.properties b/SecurityShepherdCore/src/i18n/challenges/injection/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/sqli/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.properties b/SecurityShepherdCore/src/i18n/challenges/injection/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.properties similarity index 100% rename from SecurityShepherdCore/src/i18n/challenges/sqli/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.properties rename to SecurityShepherdCore/src/i18n/challenges/injection/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/insecureCryptoStorage/insecureCryptoStorage.properties b/SecurityShepherdCore/src/i18n/challenges/insecureCryptoStorage/insecureCryptoStorage.properties new file mode 100644 index 000000000..e69de29bb diff --git a/SecurityShepherdCore/src/jsp/challenges/06f81ca93f26236112f8e31f32939bd496ffe8c9f7b564bce32bd5e3a8c2f751.jsp b/SecurityShepherdCore/src/jsp/challenges/06f81ca93f26236112f8e31f32939bd496ffe8c9f7b564bce32bd5e3a8c2f751.jsp index bd726569e..29c35e915 100644 --- a/SecurityShepherdCore/src/jsp/challenges/06f81ca93f26236112f8e31f32939bd496ffe8c9f7b564bce32bd5e3a8c2f751.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/06f81ca93f26236112f8e31f32939bd496ffe8c9f7b564bce32bd5e3a8c2f751.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% // Cross Site Scripting Challenge 4 diff --git a/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp b/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp index a471be66c..a1ccf2db3 100644 --- a/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp b/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp index d6bfe7f17..713d11649 100644 --- a/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp b/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp index 37a36e910..3efb1bfdf 100644 --- a/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp @@ -1,5 +1,5 @@ <%@page import="servlets.module.challenge.DirectObjectBankLogin"%> -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*, servlets.module.challenge.DirectObjectBankLogin" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*, servlets.module.challenge.DirectObjectBankLogin" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.jsp b/SecurityShepherdCore/src/jsp/challenges/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.jsp index 1fde20e63..80a2f11e8 100644 --- a/SecurityShepherdCore/src/jsp/challenges/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/1feccf2205b4c5ddf743630b46aece3784d61adc56498f7603ccd7cb8ae92629.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp b/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp index e511e54d9..7e6b665a7 100644 --- a/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/269d55bc0e0ff635dcaeec8533085e5eae5d25e8646dcd4b05009353c9cf9c80.jsp b/SecurityShepherdCore/src/jsp/challenges/269d55bc0e0ff635dcaeec8533085e5eae5d25e8646dcd4b05009353c9cf9c80.jsp index 667d9d86d..937937fa2 100644 --- a/SecurityShepherdCore/src/jsp/challenges/269d55bc0e0ff635dcaeec8533085e5eae5d25e8646dcd4b05009353c9cf9c80.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/269d55bc0e0ff635dcaeec8533085e5eae5d25e8646dcd4b05009353c9cf9c80.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle" %> <% // Broken Authentication and Session Management Challenge 7 diff --git a/SecurityShepherdCore/src/jsp/challenges/278fa30ee727b74b9a2522a5ca3bf993087de5a0ac72adff216002abf79146fa.jsp b/SecurityShepherdCore/src/jsp/challenges/278fa30ee727b74b9a2522a5ca3bf993087de5a0ac72adff216002abf79146fa.jsp index 1390e77ae..87854a64e 100644 --- a/SecurityShepherdCore/src/jsp/challenges/278fa30ee727b74b9a2522a5ca3bf993087de5a0ac72adff216002abf79146fa.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/278fa30ee727b74b9a2522a5ca3bf993087de5a0ac72adff216002abf79146fa.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% diff --git a/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp b/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp index 7d605b1fc..bcac9fc38 100644 --- a/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** * This level uses XOR's user input with a key. the vulnerability in the cipher is if the attacker submits spaces, the key will be revealed after the XOR. diff --git a/SecurityShepherdCore/src/jsp/challenges/2e0981dcb8278a57dcfaae3b8da0c78d5a70c2d38ea9d8b3e14db3aea01afcbb.jsp b/SecurityShepherdCore/src/jsp/challenges/2e0981dcb8278a57dcfaae3b8da0c78d5a70c2d38ea9d8b3e14db3aea01afcbb.jsp index 2bb0208d5..cfd844524 100644 --- a/SecurityShepherdCore/src/jsp/challenges/2e0981dcb8278a57dcfaae3b8da0c78d5a70c2d38ea9d8b3e14db3aea01afcbb.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/2e0981dcb8278a57dcfaae3b8da0c78d5a70c2d38ea9d8b3e14db3aea01afcbb.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/2fff41105149e507c75b5a54e558470469d7024929cf78d570cd16c03bee3569.jsp b/SecurityShepherdCore/src/jsp/challenges/2fff41105149e507c75b5a54e558470469d7024929cf78d570cd16c03bee3569.jsp index 58e38960b..0e47295e5 100644 --- a/SecurityShepherdCore/src/jsp/challenges/2fff41105149e507c75b5a54e558470469d7024929cf78d570cd16c03bee3569.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/2fff41105149e507c75b5a54e558470469d7024929cf78d570cd16c03bee3569.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp b/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp index a7025bd38..dd0d76597 100644 --- a/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/4a1bc73dd68f64107db3bbc7ee74e3f1336d350c4e1e51d4eda5b52dddf86c99.jsp b/SecurityShepherdCore/src/jsp/challenges/4a1bc73dd68f64107db3bbc7ee74e3f1336d350c4e1e51d4eda5b52dddf86c99.jsp index 07891b05d..5de80e138 100644 --- a/SecurityShepherdCore/src/jsp/challenges/4a1bc73dd68f64107db3bbc7ee74e3f1336d350c4e1e51d4eda5b52dddf86c99.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/4a1bc73dd68f64107db3bbc7ee74e3f1336d350c4e1e51d4eda5b52dddf86c99.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp b/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp index 58c66c1b5..80d94f7e5 100644 --- a/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage=""%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage=""%> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp b/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp index f9e65963c..b848e31dd 100644 --- a/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage=""%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage=""%> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/70b96195472adf3bf347cbc37c34489287969d5ba504ac2439915184d6e5dc49.jsp b/SecurityShepherdCore/src/jsp/challenges/70b96195472adf3bf347cbc37c34489287969d5ba504ac2439915184d6e5dc49.jsp index c175dad42..cb7ef1da5 100644 --- a/SecurityShepherdCore/src/jsp/challenges/70b96195472adf3bf347cbc37c34489287969d5ba504ac2439915184d6e5dc49.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/70b96195472adf3bf347cbc37c34489287969d5ba504ac2439915184d6e5dc49.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% // Cross Site Request Forgery Challenge 5 diff --git a/SecurityShepherdCore/src/jsp/challenges/714d8601c303bbef8b5cabab60b1060ac41f0d96f53b6ea54705bb1ea4316334.jsp b/SecurityShepherdCore/src/jsp/challenges/714d8601c303bbef8b5cabab60b1060ac41f0d96f53b6ea54705bb1ea4316334.jsp index 5e7964f4a..0419baca5 100644 --- a/SecurityShepherdCore/src/jsp/challenges/714d8601c303bbef8b5cabab60b1060ac41f0d96f53b6ea54705bb1ea4316334.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/714d8601c303bbef8b5cabab60b1060ac41f0d96f53b6ea54705bb1ea4316334.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle" %> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/7aed58f3a00087d56c844ed9474c671f8999680556c127a19ee79fa5d7a132e1.jsp b/SecurityShepherdCore/src/jsp/challenges/7aed58f3a00087d56c844ed9474c671f8999680556c127a19ee79fa5d7a132e1.jsp index 4fda6de4c..e1deb66da 100644 --- a/SecurityShepherdCore/src/jsp/challenges/7aed58f3a00087d56c844ed9474c671f8999680556c127a19ee79fa5d7a132e1.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/7aed58f3a00087d56c844ed9474c671f8999680556c127a19ee79fa5d7a132e1.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle" %> <% // Broken Authentication and Session Management Challenge Five diff --git a/SecurityShepherdCore/src/jsp/challenges/7d79ea2b2a82543d480a63e55ebb8fef3209c5d648b54d1276813cd072815df3.jsp b/SecurityShepherdCore/src/jsp/challenges/7d79ea2b2a82543d480a63e55ebb8fef3209c5d648b54d1276813cd072815df3.jsp index be03ab9b1..b5102a2f6 100644 --- a/SecurityShepherdCore/src/jsp/challenges/7d79ea2b2a82543d480a63e55ebb8fef3209c5d648b54d1276813cd072815df3.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/7d79ea2b2a82543d480a63e55ebb8fef3209c5d648b54d1276813cd072815df3.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% // Cross Site Request Forgery Challenge 7 diff --git a/SecurityShepherdCore/src/jsp/challenges/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.jsp b/SecurityShepherdCore/src/jsp/challenges/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.jsp index dd36377b4..c54d3c4dc 100644 --- a/SecurityShepherdCore/src/jsp/challenges/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/7edcbc1418f11347167dabb69fcb54137960405da2f7a90a0684f86c4d45a2e7.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp b/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp index d8b38048d..b697ba91d 100644 --- a/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage=""%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage=""%> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/84118752e6cd78fecc3563ba2873d944aacb7b72f28693a23f9949ac310648b5.jsp b/SecurityShepherdCore/src/jsp/challenges/84118752e6cd78fecc3563ba2873d944aacb7b72f28693a23f9949ac310648b5.jsp index 91ad9a07c..ed642ecd1 100644 --- a/SecurityShepherdCore/src/jsp/challenges/84118752e6cd78fecc3563ba2873d944aacb7b72f28693a23f9949ac310648b5.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/84118752e6cd78fecc3563ba2873d944aacb7b72f28693a23f9949ac310648b5.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% // Cross Site Request Forgery Challenge 4 diff --git a/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp b/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp index 649269245..0a50e8d3b 100644 --- a/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp b/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp index 1f4c7c9e7..d1bba126c 100644 --- a/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp @@ -1,4 +1,5 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.jsp b/SecurityShepherdCore/src/jsp/challenges/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.jsp index af29fcc9a..f96967f6d 100644 --- a/SecurityShepherdCore/src/jsp/challenges/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/8c2dd7e9818e5c6a9f8562feefa002dc0e455f0e92c8a46ab0cf519b1547eced.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.jsp b/SecurityShepherdCore/src/jsp/challenges/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.jsp index 035b54a4a..0e990e840 100644 --- a/SecurityShepherdCore/src/jsp/challenges/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/8c3c35c30cdbbb73b7be3a4f8587aa9d88044dc43e248984a252c6e861f673d4.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp b/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp index e19c6d610..d45f27098 100644 --- a/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/ad2628bcc79bf10dd54ee62de148ab44b7bd028009a908ce3f1b4d019886d0e.jsp b/SecurityShepherdCore/src/jsp/challenges/ad2628bcc79bf10dd54ee62de148ab44b7bd028009a908ce3f1b4d019886d0e.jsp index 8f5d6fe59..1754ff5c2 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ad2628bcc79bf10dd54ee62de148ab44b7bd028009a908ce3f1b4d019886d0e.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ad2628bcc79bf10dd54ee62de148ab44b7bd028009a908ce3f1b4d019886d0e.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% // Cross Site Scripting Challenge 3 diff --git a/SecurityShepherdCore/src/jsp/challenges/b5e1020e3742cf2c0880d4098146c4dde25ebd8ceab51807bad88ff47c316ece.jsp b/SecurityShepherdCore/src/jsp/challenges/b5e1020e3742cf2c0880d4098146c4dde25ebd8ceab51807bad88ff47c316ece.jsp index 740768d16..f96bd0c38 100644 --- a/SecurityShepherdCore/src/jsp/challenges/b5e1020e3742cf2c0880d4098146c4dde25ebd8ceab51807bad88ff47c316ece.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/b5e1020e3742cf2c0880d4098146c4dde25ebd8ceab51807bad88ff47c316ece.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle" %> <% // Broken Authentication and Session Management Challenge Six diff --git a/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp b/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp index f1acae99e..6752d5199 100644 --- a/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% // SQL Injection Challenge Three diff --git a/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp b/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp index 98add21b5..fbe145b8a 100644 --- a/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/c4285bbc6734a10897d672c1ed3dd9417e0530a4e0186c27699f54637c7fb5d4.jsp b/SecurityShepherdCore/src/jsp/challenges/c4285bbc6734a10897d672c1ed3dd9417e0530a4e0186c27699f54637c7fb5d4.jsp index 1daf92734..30ae34c82 100644 --- a/SecurityShepherdCore/src/jsp/challenges/c4285bbc6734a10897d672c1ed3dd9417e0530a4e0186c27699f54637c7fb5d4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/c4285bbc6734a10897d672c1ed3dd9417e0530a4e0186c27699f54637c7fb5d4.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="servlets.module.challenge.SecurityMisconfigStealTokens, java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="servlets.module.challenge.SecurityMisconfigStealTokens, java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp b/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp index 427524bfc..87674f34b 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp b/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp index 0be1ee9f5..092ad0012 100644 --- a/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp b/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp index deb42d43a..f1eb7612d 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp b/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp index ede9c0d78..b79d9a51f 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/d330dea1acf21886b685184ee222ea8e0a60589c3940afd6ebf433469e997caf.jsp b/SecurityShepherdCore/src/jsp/challenges/d330dea1acf21886b685184ee222ea8e0a60589c3940afd6ebf433469e997caf.jsp index 86e5a571d..3f94aa073 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d330dea1acf21886b685184ee222ea8e0a60589c3940afd6ebf433469e997caf.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d330dea1acf21886b685184ee222ea8e0a60589c3940afd6ebf433469e997caf.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/d72ca2694422af2e6b3c5d90e4c11e7b4575a7bc12ee6d0a384ac2469449e8fa.jsp b/SecurityShepherdCore/src/jsp/challenges/d72ca2694422af2e6b3c5d90e4c11e7b4575a7bc12ee6d0a384ac2469449e8fa.jsp index 39c81a044..84845d93b 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d72ca2694422af2e6b3c5d90e4c11e7b4575a7bc12ee6d0a384ac2469449e8fa.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d72ca2694422af2e6b3c5d90e4c11e7b4575a7bc12ee6d0a384ac2469449e8fa.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/d779e34a54172cbc245300d3bc22937090ebd3769466a501a5e7ac605b9f34b7.jsp b/SecurityShepherdCore/src/jsp/challenges/d779e34a54172cbc245300d3bc22937090ebd3769466a501a5e7ac605b9f34b7.jsp index 30fbda0e8..5ed7add8b 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d779e34a54172cbc245300d3bc22937090ebd3769466a501a5e7ac605b9f34b7.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d779e34a54172cbc245300d3bc22937090ebd3769466a501a5e7ac605b9f34b7.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle" %> <% // Broken Authentication and Session Management Challenge Two diff --git a/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp b/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp index aa04a8362..631e7b49a 100644 --- a/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage=""%> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage=""%> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/dfa404130278f44b05dc06486dd06134dac7d843367763a3226c9081f537fb2f.jsp b/SecurityShepherdCore/src/jsp/challenges/dfa404130278f44b05dc06486dd06134dac7d843367763a3226c9081f537fb2f.jsp index 6c024c8b4..160fd19f6 100644 --- a/SecurityShepherdCore/src/jsp/challenges/dfa404130278f44b05dc06486dd06134dac7d843367763a3226c9081f537fb2f.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/dfa404130278f44b05dc06486dd06134dac7d843367763a3226c9081f537fb2f.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/dfd6bfba1033fa380e378299b6a998c759646bd8aea02511482b8ce5d707f93a.jsp b/SecurityShepherdCore/src/jsp/challenges/dfd6bfba1033fa380e378299b6a998c759646bd8aea02511482b8ce5d707f93a.jsp index bbfd8b401..530e404cf 100644 --- a/SecurityShepherdCore/src/jsp/challenges/dfd6bfba1033fa380e378299b6a998c759646bd8aea02511482b8ce5d707f93a.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/dfd6bfba1033fa380e378299b6a998c759646bd8aea02511482b8ce5d707f93a.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.jsp b/SecurityShepherdCore/src/jsp/challenges/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.jsp index abee30e0a..519203b9d 100644 --- a/SecurityShepherdCore/src/jsp/challenges/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/e40333fc2c40b8e0169e433366350f55c77b82878329570efa894838980de5b4.jsp b/SecurityShepherdCore/src/jsp/challenges/e40333fc2c40b8e0169e433366350f55c77b82878329570efa894838980de5b4.jsp index 27c2d51ca..b0c9bee24 100644 --- a/SecurityShepherdCore/src/jsp/challenges/e40333fc2c40b8e0169e433366350f55c77b82878329570efa894838980de5b4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/e40333fc2c40b8e0169e433366350f55c77b82878329570efa894838980de5b4.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp b/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp index 39119016d..00eb48836 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/ec43ae137b8bf7abb9c85a87cf95c23f7fadcf08a092e05620c9968bd60fcba6.jsp b/SecurityShepherdCore/src/jsp/challenges/ec43ae137b8bf7abb9c85a87cf95c23f7fadcf08a092e05620c9968bd60fcba6.jsp index e57adf472..ad6410874 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ec43ae137b8bf7abb9c85a87cf95c23f7fadcf08a092e05620c9968bd60fcba6.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ec43ae137b8bf7abb9c85a87cf95c23f7fadcf08a092e05620c9968bd60fcba6.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle" %> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp b/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp index 77b047a53..7b6055010 100644 --- a/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/f37d45f597832cdc6e91358dca3f53039d4489c94df2ee280d6203b389dd5671.jsp b/SecurityShepherdCore/src/jsp/challenges/f37d45f597832cdc6e91358dca3f53039d4489c94df2ee280d6203b389dd5671.jsp index 45db3a84c..26c3a374a 100644 --- a/SecurityShepherdCore/src/jsp/challenges/f37d45f597832cdc6e91358dca3f53039d4489c94df2ee280d6203b389dd5671.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/f37d45f597832cdc6e91358dca3f53039d4489c94df2ee280d6203b389dd5671.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp b/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp index 6a07e0241..86ef16ac4 100644 --- a/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp b/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp index 8a0c4d011..e87fa02f0 100644 --- a/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** *

    diff --git a/SecurityShepherdCore/src/jsp/challenges/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.jsp b/SecurityShepherdCore/src/jsp/challenges/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.jsp index 7a187e793..643d74975 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ffd39cb26727f34cbf9fce3e82b9d703404e99cdef54d2aa745f497abe070b.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp b/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp index fc2b2f376..bf8e5845f 100644 --- a/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <% /** * Insecure Cryptographic Storage Challenge 2 Accessed diff --git a/SecurityShepherdCore/src/jsp/challenges/o9a450a64cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c.jsp b/SecurityShepherdCore/src/jsp/challenges/o9a450a64cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c.jsp index f9805b772..03893b4d1 100644 --- a/SecurityShepherdCore/src/jsp/challenges/o9a450a64cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/o9a450a64cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/s74a796e84e25b854906d88f622170c1c06817e72b526b3d1e9a6085f429cf52.jsp b/SecurityShepherdCore/src/jsp/challenges/s74a796e84e25b854906d88f622170c1c06817e72b526b3d1e9a6085f429cf52.jsp index e43af419a..160c1d046 100644 --- a/SecurityShepherdCore/src/jsp/challenges/s74a796e84e25b854906d88f622170c1c06817e72b526b3d1e9a6085f429cf52.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/s74a796e84e25b854906d88f622170c1c06817e72b526b3d1e9a6085f429cf52.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/t193c6634f049bcf65cdcac72269eeac25dbb2a6887bdb38873e57d0ef447bc3.jsp b/SecurityShepherdCore/src/jsp/challenges/t193c6634f049bcf65cdcac72269eeac25dbb2a6887bdb38873e57d0ef447bc3.jsp index 9b23ca863..85048e928 100644 --- a/SecurityShepherdCore/src/jsp/challenges/t193c6634f049bcf65cdcac72269eeac25dbb2a6887bdb38873e57d0ef447bc3.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/t193c6634f049bcf65cdcac72269eeac25dbb2a6887bdb38873e57d0ef447bc3.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle" %> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/t227357536888e807ff0f0eff751d6034bafe48954575c3a6563cb47a85b1e888.jsp b/SecurityShepherdCore/src/jsp/challenges/t227357536888e807ff0f0eff751d6034bafe48954575c3a6563cb47a85b1e888.jsp index 7a013f1c4..490e84629 100644 --- a/SecurityShepherdCore/src/jsp/challenges/t227357536888e807ff0f0eff751d6034bafe48954575c3a6563cb47a85b1e888.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/t227357536888e807ff0f0eff751d6034bafe48954575c3a6563cb47a85b1e888.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% diff --git a/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp b/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp index e16355f6d..8d2a5d490 100644 --- a/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/vc9b78627df2c032ceaf7375df1d847e47ed7abac2a4ce4cb6086646e0f313a4.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/x9c408d23e75ec92495e0caf9a544edb2ee8f624249f3e920663edb733f15cd7.jsp b/SecurityShepherdCore/src/jsp/challenges/x9c408d23e75ec92495e0caf9a544edb2ee8f624249f3e920663edb733f15cd7.jsp index a83dbee94..ff14ca00f 100644 --- a/SecurityShepherdCore/src/jsp/challenges/x9c408d23e75ec92495e0caf9a544edb2ee8f624249f3e920663edb733f15cd7.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/x9c408d23e75ec92495e0caf9a544edb2ee8f624249f3e920663edb733f15cd7.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/z311736498a13604705d608fb3171ebf49bc18753b0ec34b8dff5e4f9147eb5e.jsp b/SecurityShepherdCore/src/jsp/challenges/z311736498a13604705d608fb3171ebf49bc18753b0ec34b8dff5e4f9147eb5e.jsp index bc3ec1b93..9b920f308 100644 --- a/SecurityShepherdCore/src/jsp/challenges/z311736498a13604705d608fb3171ebf49bc18753b0ec34b8dff5e4f9147eb5e.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/z311736498a13604705d608fb3171ebf49bc18753b0ec34b8dff5e4f9147eb5e.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** diff --git a/SecurityShepherdCore/src/jsp/challenges/z6b2f5ebbe112dd09a6c430a167415820adc5633256a7b44a7d1e262db105e3c.jsp b/SecurityShepherdCore/src/jsp/challenges/z6b2f5ebbe112dd09a6c430a167415820adc5633256a7b44a7d1e262db105e3c.jsp index 06aa0018f..83e50179c 100644 --- a/SecurityShepherdCore/src/jsp/challenges/z6b2f5ebbe112dd09a6c430a167415820adc5633256a7b44a7d1e262db105e3c.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/z6b2f5ebbe112dd09a6c430a167415820adc5633256a7b44a7d1e262db105e3c.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** From c639ae86b7b44b3afb51db1c6659cde7d2f1e806 Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Tue, 29 Sep 2015 21:49:53 +0100 Subject: [PATCH 034/112] More translation stuff --- .../mobile/reverseEng/reverseEngText.properties | 2 ++ ...9fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp | 17 ++++++++++++----- SecurityShepherdCore/src/utils/Analytics.java | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 SecurityShepherdCore/src/i18n/challenges/mobile/reverseEng/reverseEngText.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEng/reverseEngText.properties b/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEng/reverseEngText.properties new file mode 100644 index 000000000..4aa53f230 --- /dev/null +++ b/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEng/reverseEngText.properties @@ -0,0 +1,2 @@ +challenge1.challengeName = Mobile Reverse Engineer 1 +challenge1.para1 = This key to this challenge is the App author's name. Find it to complete the challenge. \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp b/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp index a1ccf2db3..98f54166f 100644 --- a/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*, org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder" errorPage="" %> <%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** @@ -25,7 +25,13 @@ String levelName = "Mobile Reverse Engineer 1"; //Alphanumeric Only String levelHash = "072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp"; //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; + +//Translation Stuff +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.reverseEng.reverseEngText", locale); +//Used more than once translations +String i18nLevelName = bundle.getString("challenge1.challengeName"); +String paragraph1 = bundle.getString("challenge1.para1"); ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -45,11 +51,12 @@ if (request.getSession() != null) if (Validate.validateSession(ses) && tokenCookie != null) { ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " has been accessed by " + ses.getAttribute("userName").toString(), ses.getAttribute("userName")); + %> - Security Shepherd - <%= levelName %> + Security Shepherd - <%= i18nLevelName %> @@ -57,10 +64,10 @@ if (request.getSession() != null)
    -

    <%= levelName %>

    +

    <%= i18nLevelName %>


    - This key to this challenge is the App author's name. Find it to complete the challenge. + <%= paragraph1 %>

    <%= Analytics.getMobileLevelBlurb("ReverseEngineer2.apk") %> diff --git a/SecurityShepherdCore/src/utils/Analytics.java b/SecurityShepherdCore/src/utils/Analytics.java index 16cf6e722..ae0269ff7 100644 --- a/SecurityShepherdCore/src/utils/Analytics.java +++ b/SecurityShepherdCore/src/utils/Analytics.java @@ -37,5 +37,6 @@ public class Analytics public static String getMobileLevelBlurb (String appName) { return mobileVmLinkBlurb1 + appName + mobileVmLinkBlurb2; + //TODO Extract strings for translation } } From d014def70885763e083bdc986e02ea4eb99350f7 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Tue, 29 Sep 2015 22:45:23 +0100 Subject: [PATCH 035/112] Fixing Category Typo in CoreSchema.sql --- SecurityShepherdCore/database/coreSchema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityShepherdCore/database/coreSchema.sql b/SecurityShepherdCore/database/coreSchema.sql index 8f6304cd6..77382deb8 100644 --- a/SecurityShepherdCore/database/coreSchema.sql +++ b/SecurityShepherdCore/database/coreSchema.sql @@ -1382,7 +1382,7 @@ INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleT INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('408610f220b4f71f7261207a17055acbffb8a747', 'SQL Injection', 'sql.injection', 'lesson', 'Injection', 'injection', '3c17f6bf34080979e0cebda5672e989c07ceec9fa4ee7b7c17c9e3ce26bc63e0', 'e881086d4d8eb2604d8093d93ae60986af8119c4f643894775433dbfb6faa594', 'open', '55', '30', '5', 1); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('891a0208a95f1791287be721a4b851d4c584880a', 'Insecure Cryptographic Storage Challenge 1', 'insecure.cryptographic.storage.challenge.1', 'challenge', 'Insecure Cryptographic Storage', 'insecure.cryptographic.storage', 'mylovelyhorserunningthroughthefieldwhereareyougoingwithyourbiga', 'x9c408d23e75ec92495e0caf9a544edb2ee8f624249f3e920663edb733f15cd7', 'open', '65', '35', '5', 1); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('2dc909fd89c2b03059b1512e7b54ce5d1aaa4bb4', 'Insecure Direct Object Reference Challenge 1', 'insecure.direct.object.reference.challenge.1', 'challenge', 'Insecure Direct Object References', 'insecure.direct.object.references', 'dd6301b38b5ad9c54b85d07c087aebec89df8b8c769d4da084a55663e6186742', 'o9a450a64cc2a196f55878e2bd9a27a72daea0f17017253f87e7ebd98c71c98c', 'open', '66', '35', '5', 1); -INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('6be5de81223cc1b38b6e427cc44f8b6a28d2bc96', 'Poor Data Validation 1', 'poor.data.validation.1', 'challenge', 'Poor Data Validation', 'poor.data.validation', 'd30475881612685092e5ec469317dcc5ccc1f548a97bfdb041236b5bba7627bf', 'ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e', 'open', '67', '35', '5', 0); +INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('6be5de81223cc1b38b6e427cc44f8b6a28d2bc96', 'Poor Data Validation 1', 'poor.data.validation.1', 'challenge', 'Poor Data Validation', 'poor.data.validation', 'd30475881612685092e5ec469317dcc5ccc1f548a97bfdb041236b5bba7627bf', 'ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e', 'open', '67', '35', '5', 0); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('544aa22d3dd16a8232b093848a6523b0712b23da', 'SQL Injection 1', 'sql.injection.1', 'challenge', 'Injection', 'injection', 'fd8e9a29dab791197115b58061b215594211e72c1680f1eacc50b0394133a09f', 'e1e109444bf5d7ae3d67b816538613e64f7d0f51c432a164efc8418513711b0a', 'open', '68', '35', '5', 1); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('2ab09c0c18470ae5f87d219d019a1f603e66f944', 'Reverse Engineering', 'reverse.engineering', 'lesson', 'Mobile Reverse Engineering', 'mobile.reverse.engineering', 'DrumaDrumaDrumBoomBoom', '19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9', 'open', '75', '40', '5', 1); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('a4bf43f2ba5ced041b03d0b3f9fa3541c520d65d', 'Session Management Challenge 1', 'session.management.challenge.1', 'challenge', 'Session Management', 'session.management', 'db7b1da5d7a43c7100a6f01bb0c', 'dfd6bfba1033fa380e378299b6a998c759646bd8aea02511482b8ce5d707f93a', 'open', '75', '40', '5', 0); From 5b24ec666e0a1027a20ec6128e7555bf7231cc4b Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Wed, 30 Sep 2015 18:32:09 +0100 Subject: [PATCH 036/112] Changing Font to Helvetica Neue for Comfortable Reading --- SecurityShepherdCore/src/jsp/css/lessonCss/theCss.css | 4 ++-- SecurityShepherdCore/src/jsp/css/theCss.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SecurityShepherdCore/src/jsp/css/lessonCss/theCss.css b/SecurityShepherdCore/src/jsp/css/lessonCss/theCss.css index 7e335a429..2c65c2d90 100644 --- a/SecurityShepherdCore/src/jsp/css/lessonCss/theCss.css +++ b/SecurityShepherdCore/src/jsp/css/lessonCss/theCss.css @@ -5,8 +5,8 @@ body { color: #090212; } -body, th, td, input, textarea, select, option { - font-family: "Trebuchet MS", "Times New Roman", Arial, Times, serif; +body, th, td, input, textarea, select, option, p, div { + font-family: "Helvetica Neue", Helvetica Arial, "Lucida Grande", sans-serif; } h1, h2, h3 { diff --git a/SecurityShepherdCore/src/jsp/css/theCss.css b/SecurityShepherdCore/src/jsp/css/theCss.css index 55c2126eb..7481ceba8 100644 --- a/SecurityShepherdCore/src/jsp/css/theCss.css +++ b/SecurityShepherdCore/src/jsp/css/theCss.css @@ -6,8 +6,8 @@ body { color: #090212; } -body, th, td, input, textarea, select, option { - font-family: "Trebuchet MS", "Times New Roman", Arial, Times, serif; +body, th, td, input, textarea, select, option, p, div { + font-family: "Helvetica Neue", Helvetica Arial, "Lucida Grande", sans-serif; } h1, h2, h3 { From 24c8cc18979117c7920adb9b1be64be3a4a7c4f4 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Wed, 30 Sep 2015 18:38:15 +0100 Subject: [PATCH 037/112] Fix for Broken CTF Default Menu Buttons --- SecurityShepherdCore/src/jsp/index.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityShepherdCore/src/jsp/index.jsp b/SecurityShepherdCore/src/jsp/index.jsp index 75529bd56..6db14e2e4 100644 --- a/SecurityShepherdCore/src/jsp/index.jsp +++ b/SecurityShepherdCore/src/jsp/index.jsp @@ -745,7 +745,7 @@ if (request.getSession() != null) }); } <% if(ModulePlan.isIncrementalFloor()) { %> - applyMenuButtonActions('<%= encoder.encodeForHTML(csrfToken) %>', ""); + applyMenuButtonActionsCtfMode('<%= encoder.encodeForHTML(csrfToken) %>', ""); <% } //End of if(CTF Mode Enabled) %> <% if (!ModulePlan.isIncrementalFloor()) {%> applyMenuButtonActionsOpenOrTourney('<%= encoder.encodeForHTML(csrfToken) %>', ""); From 66cc0f66dfa2709adbb39e6e9ed04e0a4914bf1d Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Wed, 30 Sep 2015 19:09:19 +0100 Subject: [PATCH 038/112] Adding Another JUnit Test to GetterTest --- SecurityShepherdCore/src/dbProcs/Setter.java | 1 + .../test/dbProcs/GetterTest.java | 83 ++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/SecurityShepherdCore/src/dbProcs/Setter.java b/SecurityShepherdCore/src/dbProcs/Setter.java index f2491c89c..ec780e64b 100644 --- a/SecurityShepherdCore/src/dbProcs/Setter.java +++ b/SecurityShepherdCore/src/dbProcs/Setter.java @@ -1220,6 +1220,7 @@ public static boolean userCreate (String ApplicationRoot, String classId, String catch(SQLException e) { log.fatal("userCreate Failure: " + e.toString()); + throw new SQLException(e); } Database.closeConnection(conn); log.debug("*** END userCreate ***"); diff --git a/SecurityShepherdCore/test/dbProcs/GetterTest.java b/SecurityShepherdCore/test/dbProcs/GetterTest.java index f8ddc98f0..66fc287cb 100644 --- a/SecurityShepherdCore/test/dbProcs/GetterTest.java +++ b/SecurityShepherdCore/test/dbProcs/GetterTest.java @@ -5,10 +5,13 @@ import java.sql.ResultSet; import java.util.ArrayList; import java.util.Locale; +import java.util.ResourceBundle; import org.apache.log4j.Logger; import org.junit.Test; +import utils.Validate; + public class GetterTest { private static org.apache.log4j.Logger log = Logger.getLogger(GetterTest.class); @@ -480,14 +483,88 @@ else if(!classInfo[1].equalsIgnoreCase("2015")) fail("Could not Create Class"); } } - - /* @Test public void testGetCsrfForumWithIframe() { - fail("Not yet implemented"); + String classId = new String(); + String moduleId = new String("0a37cb9296ff3763f7f3a45ff313bce47afa9384"); //CSRF Challenge 5 + Locale locale = new Locale("en_GB"); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.csrf.csrfGenerics", locale); + if(Setter.classCreate(applicationRoot, "NewClassForCsrfIframeFourm", "2015")) + { + //Get Class Id + try + { + ResultSet rs = Getter.getClassInfo(applicationRoot); + while(rs.next()) + { + if(rs.getString(2).equalsIgnoreCase("NewClassForCsrfIframeFourm")) + { + classId = rs.getString(1); + break; + } + } + } + catch(Exception e) + { + log.fatal("Could not Retreieve Class Id from Created Class : " + e.toString()); + } + if(classId.isEmpty()) + { + fail("Could not get ClassId"); + } + else + { + try + { + String userName = new String("userforiframeclass"); + if(Setter.userCreate(applicationRoot, classId, userName, "password1726", "player", "iframeclass@gmail.com", false)) + { + //Open all Modules First so that the Module Can Be Opened by the user + if(Setter.openAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, moduleId, Getter.getUserIdFromName(applicationRoot, userName)).isEmpty()) + { + String csrfFourm = Getter.getCsrfForumWithIframe(applicationRoot, classId, moduleId, bundle); + if(csrfFourm.indexOf(userName) > -1) + { + log.debug("PASS: User was found in the fourm"); + return; + } + else + { + log.error("Could not find user name '" + userName + "' in this: " + csrfFourm); + fail("User was not contained in the CSRF iFrame Forum"); + } + } + else + { + fail("Could not open CSRF 5 as Created User"); + } + } + else + { + fail("Could not Mark All Modules as Open"); + } + } + } + catch(Exception e) + { + log.fatal("Could not Create User: " + e.toString()); + fail("Could not create user in class"); + } + } + } + else + { + fail("Could not Create Class"); + } + log.debug("End of CSRF Iframe Forum Test"); } + /* + @Test public void testGetCsrfForumWithImg() { fail("Not yet implemented"); From 537203fa64a0d2c6aab65c4b2cac18f53ef9e77a Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Wed, 30 Sep 2015 19:26:50 +0100 Subject: [PATCH 039/112] Adding More JUnit Tests for Getter.java --- .../test/dbProcs/GetterTest.java | 86 +++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/SecurityShepherdCore/test/dbProcs/GetterTest.java b/SecurityShepherdCore/test/dbProcs/GetterTest.java index 66fc287cb..7ce11c382 100644 --- a/SecurityShepherdCore/test/dbProcs/GetterTest.java +++ b/SecurityShepherdCore/test/dbProcs/GetterTest.java @@ -485,7 +485,8 @@ else if(!classInfo[1].equalsIgnoreCase("2015")) } @Test - public void testGetCsrfForumWithIframe() { + public void testGetCsrfForumWithIframe() + { String classId = new String(); String moduleId = new String("0a37cb9296ff3763f7f3a45ff313bce47afa9384"); //CSRF Challenge 5 Locale locale = new Locale("en_GB"); @@ -562,14 +563,87 @@ public void testGetCsrfForumWithIframe() { } log.debug("End of CSRF Iframe Forum Test"); } - - /* @Test - public void testGetCsrfForumWithImg() { - fail("Not yet implemented"); + public void testGetCsrfForumWithImg() + { + String classId = new String(); + String moduleId = new String("0a37cb9296ff3763f7f3a45ff313bce47afa9384"); //CSRF Challenge 5 + Locale locale = new Locale("en_GB"); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.csrf.csrfGenerics", locale); + if(Setter.classCreate(applicationRoot, "NewClassForCsrfImgFourm", "2015")) + { + //Get Class Id + try + { + ResultSet rs = Getter.getClassInfo(applicationRoot); + while(rs.next()) + { + if(rs.getString(2).equalsIgnoreCase("NewClassForCsrfImgFourm")) + { + classId = rs.getString(1); + break; + } + } + } + catch(Exception e) + { + log.fatal("Could not Retreieve Class Id from Created Class : " + e.toString()); + } + if(classId.isEmpty()) + { + fail("Could not get ClassId"); + } + else + { + try + { + String userName = new String("userforimgclass"); + if(Setter.userCreate(applicationRoot, classId, userName, "password1726", "player", "imgclass@gmail.com", false)) + { + //Open all Modules First so that the Module Can Be Opened by the user + if(Setter.openAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, moduleId, Getter.getUserIdFromName(applicationRoot, userName)).isEmpty()) + { + String csrfFourm = Getter.getCsrfForumWithImg(applicationRoot, classId, moduleId, bundle); + if(csrfFourm.indexOf(userName) > -1) + { + log.debug("PASS: User was found in the fourm"); + return; + } + else + { + log.error("Could not find user name '" + userName + "' in this: " + csrfFourm); + fail("User was not contained in the CSRF Img Forum"); + } + } + else + { + fail("Could not open CSRF 5 as Created User"); + } + } + else + { + fail("Could not Mark All Modules as Open"); + } + } + } + catch(Exception e) + { + log.fatal("Could not Create User: " + e.toString()); + fail("Could not create user in class"); + } + } + } + else + { + fail("Could not Create Class"); + } } - + + /* @Test public void testGetFeedback() { fail("Not yet implemented"); From 001ac96219e4873aca24cc83178618e314f52132 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Thu, 1 Oct 2015 20:25:53 +0100 Subject: [PATCH 040/112] Enhansing Existing and Adding New JUnit Tests --- SecurityShepherdCore/src/dbProcs/Getter.java | 2 + .../test/dbProcs/GetterTest.java | 1089 ++++++++++++++--- 2 files changed, 908 insertions(+), 183 deletions(-) diff --git a/SecurityShepherdCore/src/dbProcs/Getter.java b/SecurityShepherdCore/src/dbProcs/Getter.java index b1d4b58ee..3e7d803b3 100644 --- a/SecurityShepherdCore/src/dbProcs/Getter.java +++ b/SecurityShepherdCore/src/dbProcs/Getter.java @@ -1105,6 +1105,8 @@ public static String getModuleAddress (String ApplicationRoot, String moduleId, catch(Exception e) { log.error("Module Hash Retrieval: " + e.toString()); + log.error("moduleID = " + moduleId); + log.error("userID = " + userId); } Database.closeConnection(conn); log.debug("*** END getModuleAddress() ***"); diff --git a/SecurityShepherdCore/test/dbProcs/GetterTest.java b/SecurityShepherdCore/test/dbProcs/GetterTest.java index 7ce11c382..263a15f17 100644 --- a/SecurityShepherdCore/test/dbProcs/GetterTest.java +++ b/SecurityShepherdCore/test/dbProcs/GetterTest.java @@ -10,262 +10,617 @@ import org.apache.log4j.Logger; import org.junit.Test; -import utils.Validate; - public class GetterTest { private static org.apache.log4j.Logger log = Logger.getLogger(GetterTest.class); private static String propertiesFileDirectory = new String("/site"); - private static String unOpenedModuleId = new String("0dbea4cb5811fff0527184f99bd5034ca9286f11"); //Insecure Direct Object References Module Id - private static Locale lang = new Locale("en"); + private static String lang = new String("en_GB"); + private static Locale locale = new Locale(lang); private static String applicationRoot = new String(); - private static String adminUserName = new String("admin"); - private static String adminUserId = new String(); public GetterTest() { applicationRoot = System.getProperty("user.dir") + propertiesFileDirectory; - adminUserId = Getter.getUserIdFromName(applicationRoot, adminUserName); + } + + /** + * This method will sign in as a User, or create the user and sign in as them. If this fails it will throw an Exception + * @param applicationRoot Context of running application + * @param userName The user name of the user you want to create or sign in as + * @param password The password of the user you want to create or sign in as + * @return Boolean value depicting if the user exists and can be authenticated + * @throws Exception If User Create function fails, an exception will be passed up + */ + private static boolean verifyTestUser(String applicationRoot, String userName, String password) throws Exception + { + boolean result = false; + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, null, userName, userName, "player", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug(userName + " could authenticate. returning true"); + result = true; + } + else + { + log.error("Couldnt verify that " + userName + " could authenticate at all. Throwing Exception"); + throw new Exception("Could not Verify User " + userName + " could authenticate at all."); + } + } + catch(Exception e) + { + throw new Exception("Could not Create User " + userName + ": " + e.toString()); + } + return result; + } + + /** + * This method will sign in as a User, or create the user and sign in as them. If this fails it will throw an Exception. + * They will be added to the submitted class + * @param applicationRoot Context of running application + * @param userName The user name of the user you want to create or sign in as + * @param password The password of the user you want to create or sign in as + * @return Boolean value depicting if the user exists and can be authenticated + * @throws Exception If User Create function fails, an exception will be passed up + */ + private static boolean verifyTestUser(String applicationRoot, String userName, String password, String theClass) throws Exception + { + boolean result = false; + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, theClass, userName, userName, "player", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug(userName + " could authenticate. checking class"); + if(!user[4].equalsIgnoreCase(theClass)) + { + log.debug("Need to update user's class"); + Setter.updatePlayerClass(applicationRoot, theClass, user[0]); + } + else + log.debug("User in class submitted already"); + result = true; + } + else + { + log.error("Couldnt verify that " + userName + " could authenticate at all. Throwing Exception"); + throw new Exception("Could not Verify User " + userName + " could authenticate at all."); + } + } + catch(Exception e) + { + throw new Exception("Could not Create User " + userName + ": " + e.toString()); + } + return result; + } + + /** + * This method will sign in as an admin, or create the admin and sign in as them. If this fails it will throw an Exception. + * This function will pass if correct user credentials are passed as well + * @param applicationRoot Context of running application + * @param userName The user name of the admin you want to create or sign in as + * @param password The password of the admin you want to create or sign in as + * @return Boolean value depicting if the user exists and can be authenticated + * @throws Exception If admin Create function fails, an exception will be passed up + */ + private static boolean verifyTestAdmin(String applicationRoot, String userName, String password) throws Exception + { + boolean result = false; + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, null, userName, userName, "admin", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug(userName + " could authenticate. returning true"); + result = true; + } + else + { + log.error("Couldnt verify that " + userName + " could authenticate at all. Throwing Exception"); + throw new Exception("Could not Verify User " + userName + " could authenticate at all."); + } + } + catch(Exception e) + { + throw new Exception("Could not Create User " + userName + ": " + e.toString()); + } + return result; } @Test public void testAuthUserCorrectCredentials() { - if(Getter.authUser(applicationRoot, adminUserName, "password") == null) - fail("Could not Authenticate Default admin"); - else - return; //Pass + String userName = new String("authWithGoodCreds"); + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, null, userName, userName, "player", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug("PASS: Successfully signed in as " + userName); + return; + } + else + { + fail("Could not Authenticate as " + userName); + } + } + catch(Exception e) + { + log.fatal("Could not Create user: " + e.toString()); + fail("Could not create user " + userName); + } } @Test public void testAuthUserIncorrectCredentials() { - if(Getter.authUser(applicationRoot, adminUserName, "wrongPassword") == null) - return; //Pass - else - fail("Could authenticate as Default admin with incorrect Password"); + String userName = new String("authWithBadCreds"); + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, null, userName, userName, "player", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug("User " + userName + " exists. Checking if Auth Works with bad pass"); + if(Getter.authUser(applicationRoot, userName, userName+"wrongPassword") == null) + { + log.debug("PASS: Could not authenticate with bad pass for user " + userName); + return; + } + else + { + fail("Could Authenticate With Bad Pass for User " + userName); + } + } + else + { + fail("Couldnt verify " + userName + " could authenticate at all"); + } + } + catch(Exception e) + { + log.fatal("Could not Create user: " + e.toString()); + fail("Could not create user " + userName); + } } @Test public void testAuthUserSqlInjection() { - if(Getter.authUser(applicationRoot, adminUserName, "wrongPassword'or'1'='1") == null) - return; //Pass - else - fail("Could authenticate as Default admin with SQL Injection"); + String userName = new String("authWithSqliCreds"); + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, null, userName, userName, "player", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug("User " + userName + " exists. Checking if Auth Works with bad pass"); + if(Getter.authUser(applicationRoot, userName, "'or'='1'='1") == null) + { + log.debug("PASS: Could not authenticate with SQL Injection for user " + userName); + return; + } + else + { + fail("Could Authenticate With SQL Injection for User " + userName); + } + } + else + { + fail("Couldnt verify " + userName + " could authenticate at all"); + } + } + catch(Exception e) + { + log.fatal("Could not Create user: " + e.toString()); + fail("Could not create user " + userName); + } } @Test public void testAuthUserSqlInjectionUserName() { - if(Getter.authUser(applicationRoot, adminUserName + "'or'1'='1", "wrongPassword") == null) - return; //Pass - else - fail("Could authenticate as Default admin with SQL Injection (UserName)"); + String userName = new String("authWithSqli+BadPassCreds"); + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, null, userName, userName, "player", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug("User " + userName + " exists. Checking if Auth Works with bad pass"); + if(Getter.authUser(applicationRoot, "'or'='1'='1' -- ", "wrongPassword") == null) + { + log.debug("PASS: Could not authenticate with SQL Injection for user " + userName); + return; + } + else + { + fail("Could Authenticate With SQL Injection for User Name"); + } + } + else + { + fail("Couldnt verify " + userName + " could authenticate at all"); + } + } + catch(Exception e) + { + log.fatal("Could not Create user: " + e.toString()); + fail("Could not create user " + userName); + } } @Test - public void testCheckPlayerResultWhenModuleNotOpened() { - String test = Getter.checkPlayerResult(applicationRoot, unOpenedModuleId, adminUserId); - if(test != null) + public void testCheckPlayerResultWhenModuleNotOpened() + { + String userName = new String("noModulesOpened"); + String unOpenedModuleId = new String("0dbea4cb5811fff0527184f99bd5034ca9286f11"); //Insecure Direct Object References Module Id + try + { + if(verifyTestUser(applicationRoot, userName, userName)) + { + String test = Getter.checkPlayerResult(applicationRoot, unOpenedModuleId, userName); + if(test != null) + { + log.fatal("result should be null but it was: " + test); + fail("Function says User has opened module they should not have opened by default"); // User Should not have completed this module by default after running a fresh DB. ensure you have a fresh DB if this fails + } + else + { + log.debug("PASS: Function says user has not opened module"); + return; //Pass + } + } + else + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) { - log.fatal("result should be null but it was: " + test); - fail("Function says Admin has opened module they should not have opened by default"); // Admin Should not have completed this module by default after running a fresh DB. ensure you have a fresh DB if this fails + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } - else - return; //Pass } @Test - public void testCheckPlayerResultWhenModuleWhenOpened() { + public void testCheckPlayerResultWhenModuleWhenOpened() + { + String userName = new String("userHasModulesOpened"); String csrfChallengeThree = new String("5ca9115f3279b9b9f3308eb6a59a4fcd374846d6"); - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + try { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeThree, adminUserId).isEmpty()) + if(verifyTestUser(applicationRoot, userName, userName)) { - String test = Getter.checkPlayerResult(applicationRoot, csrfChallengeThree, adminUserId); - if(test == null) + if(Setter.openAllModules(applicationRoot)) { - fail("Function says Admin has not opened module"); // Admin Should have opened and not completed CSRF Three. Ensure DB is clean + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, csrfChallengeThree, Getter.getUserIdFromName(applicationRoot, userName)).isEmpty()) + { + String test = Getter.checkPlayerResult(applicationRoot, csrfChallengeThree, Getter.getUserIdFromName(applicationRoot, userName)); + if(test == null) + { + fail("Function says " + userName + " has not opened module"); // User Should have opened and not completed CSRF Three + } + else + return; //Pass + } + else + fail("Could not Mark CSRF 3 as Opened by " + userName); } else - return; //Pass + { + fail("Could not Mark Modules As Opened"); + } } else - fail("Could not Mark CSRF 3 as Opened by Default admin"); + { + fail("Could not verify user (No Exception Failure)"); + } } - else + catch(Exception e) { - fail("Could not Mark Modules As Opened"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @Test public void testCheckPlayerResultWhenModuleNotComplete() { + String userName = new String("userHasModulesOpened"); String contentProviderLeakage = new String("5b461ebe2e5e2797740cb3e9c7e3f93449a93e3a"); - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + try { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, contentProviderLeakage, adminUserId).isEmpty()) + if(verifyTestUser(applicationRoot, userName, userName)) { - String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, contentProviderLeakage, adminUserId); - if(checkPlayerResultTest != null) - return; //Pass + if(Setter.openAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, contentProviderLeakage, Getter.getUserIdFromName(applicationRoot, userName)).isEmpty()) + { + String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, contentProviderLeakage, Getter.getUserIdFromName(applicationRoot, userName)); + if(checkPlayerResultTest != null) + return; //Pass + else + { + fail("Function says user has not opened challenge or has completed challenge before"); + } + } else { - fail("Function says Admin has not opened challenge or has completed challenge before"); + fail("Could not Content Provider Leakage Lesson as Opened by user"); } + } + else + { + fail("Could not Mark Modules As Opened"); + } } else - fail("Could not Content Provider Leakage Lesson as Opened by Default admin"); + { + fail("Could not verify user (No Exception Failure)"); + } } - else + catch(Exception e) { - fail("Could not mark modules as opened"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @Test public void testCheckPlayerResultWhenModuleComplete() { + String userName = new String("userResultWhenComplete"); String dataStorageLessonId = new String("53a53a66cb3bf3e4c665c442425ca90e29536edd"); - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + try { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, dataStorageLessonId, adminUserId).isEmpty()) + if(verifyTestUser(applicationRoot, userName, userName)) { - //Then, Mark the Challenge Complete for Default Admin (Insecure Data Storage Lesson) - String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, dataStorageLessonId, adminUserId, "Feedback is Disabled", 1, 1, 1); - if (markLevelCompleteTest != null) + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, dataStorageLessonId, adminUserId); - log.debug("checkPlayerResultTest" + checkPlayerResultTest); - if(checkPlayerResultTest == null) - return; //Pass - else + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, dataStorageLessonId, userId).isEmpty()) { - fail("Function says Admin has not completed module"); //Even though this test just marked it as Completed + //Then, Mark the Challenge Complete for user (Insecure Data Storage Lesson) + String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, dataStorageLessonId, userId, "Feedback is Disabled", 1, 1, 1); + if (markLevelCompleteTest != null) + { + String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, dataStorageLessonId, userId); + log.debug("checkPlayerResultTest" + checkPlayerResultTest); + if(checkPlayerResultTest == null) + return; //Pass + else + { + fail("Function says user has not completed module"); //Even though this test just marked it as Completed + } + } + else + fail("Could not mark data storage lesson as complete for user"); } + else + fail("Could not Mark Data Storage Lesson as Opened by Default admin"); } else - fail("Could not mark data storage lesson as complete Default admin"); + fail("Could not Open All Modules"); } else - fail("Could not Mark Data Storage Lesson as Opened by Default admin"); + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) + { + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } - else - fail("Could not Open All Modules"); } @Test public void testIsCsrfLevelCompleteIncrementedCounter() { - String csrfChallengeOne = new String("20e755179a5840be5503d42bb3711716235005ea"); //CSRF Challenge 1 (Should have CSRF Counter of 0 for Default Admin User - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + String userName = new String("csrfCounterIncremented"); + String csrfChallengeOne = new String("20e755179a5840be5503d42bb3711716235005ea"); //CSRF Challenge 1 (Should have CSRF Counter of 0 for new user) + try { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeOne, adminUserId).isEmpty()) + if(verifyTestUser(applicationRoot, userName, userName)) { - //Increment Challenge CSRF Counter - if(Setter.updateCsrfCounter(applicationRoot, csrfChallengeOne, adminUserId)) + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - if(Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeOne, adminUserId)) + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, csrfChallengeOne, userId).isEmpty()) { - return; //Pass, because CSRF level is completed after the admin CSRF counter was incremented + //Increment Challenge CSRF Counter + if(Setter.updateCsrfCounter(applicationRoot, csrfChallengeOne, userId)) + { + if(Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeOne, userId)) + { + return; //Pass, because CSRF level is completed after the user CSRF counter was incremented + } + else + { + fail("CSRF 1 not completed after successful increment"); + } + } + else + { + fail("Could not Increment user Counter for CSRF 1"); + } } else { - fail("CSRF 1 not completed after successful increment"); + fail("Could not Mark CSRF 1 as opened by user"); } } else { - fail("Could not Increment default Admin Counter for CSRF 1"); + fail("Could not Mark Modules as Opened"); } } else { - fail("Could not Mark CSRF 1 as opened by default Admin"); + fail("Could not verify user (No Exception Failure)"); } } - else + catch(Exception e) { - fail("Could not Mark Modules as Opened"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @Test public void testIsCsrfLevelCompleteWithoutIncrementedCounter() { - String csrfChallengeTwo = new String("94cd2de560d89ef59fc450ecc647ff4d4a55c15d"); //CSRF Challenge 2 (Should have CSRF Counter of 0 for Default Admin User - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + String userName = new String("csrfCounterWithoutInc"); + String csrfChallengeTwo = new String("94cd2de560d89ef59fc450ecc647ff4d4a55c15d"); //CSRF Challenge 2 (Should have CSRF Counter of 0 for new user + try { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeTwo, adminUserId).isEmpty()) + if(verifyTestUser(applicationRoot, userName, userName)) { - if(!Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeTwo, adminUserId)) + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) { - return; //Pass, because CSRF level is not completed because the CSRF Counter for the default admin is 0 + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, csrfChallengeTwo, userId).isEmpty()) + { + if(!Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeTwo, userId)) + { + return; //Pass, because CSRF level is not completed because the CSRF Counter for the user is 0 + } + else + { + fail("CSRF 2 marked completed without increment"); // CSRF 2 Challenge should have a counter of 0 and should not return true. + } + } + else + { + fail("Could not Mark CSRF 2 as opened by user"); + } } else { - fail("CSRF 2 marked completed without increment"); // CSRF 2 Challenge should have a counter of 0 and should not return true. + fail("Could not mark All Modules as Opened"); } } else { - fail("Could not Mark CSRF 2 as opened by default Admin"); + fail("Could not verify user (No Exception Failure)"); } } - else + catch(Exception e) { - fail("Could not mark All Modules as Opened"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @Test public void testFindPlayerById() { - String userName = "UserForPlayerIdSearch"; + String userName = new String("UserForPlayerIdSearch"); try { - //Create player with Null Class Id, and userName for name and password - Setter.userCreate(applicationRoot, null, userName, userName, "player", userName+"@test.com", false); - String userId = Getter.getUserIdFromName(applicationRoot, userName); - if(Getter.findPlayerById(applicationRoot, userId)) + if(verifyTestUser(applicationRoot, userName, userName)) { - return; + String userId = Getter.getUserIdFromName(applicationRoot, userName); + if(Getter.findPlayerById(applicationRoot, userId)) + { + log.debug("PASS: Found user"); + return; + } + else + { + fail("Could Not Find Player in Player Search"); + } } else { - fail("Could Not Find Player in Player Search"); + fail("Could not verify user (No Exception Failure)"); } } catch(Exception e) { - log.fatal("Could not create player: " + e.toString()); - fail("Could Not Create Player"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @Test public void testFindPlayerByIdWithAdminId() { - if(!Getter.findPlayerById(applicationRoot, adminUserId)) + + String userName = new String("playerSearchWithAdmin"); + try { - return; + if(verifyTestAdmin(applicationRoot, userName, userName)) + { + String userId = Getter.getUserIdFromName(applicationRoot, userName); + if(!Getter.findPlayerById(applicationRoot, userId)) + { + return; + } + else + { + fail("Found Admin in Player Search"); + } + } + else + { + fail("Could not verify user (No Exception Failure)"); + } } - else + catch(Exception e) { - fail("Found Admin in Player Search"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @@ -285,7 +640,7 @@ public void testFindPlayerByIdWithBadUserId() @Test - public void testGetAllModuleInfoWhenModulesOpen() + public void testGetAllModuleInfo() { ArrayList modules = Getter.getAllModuleInfo(applicationRoot); if(modules.size() > 75) //Shepherd v3.0 has 76 Modules. If less than All are Returned, then there is a problem with the Open Modules Function or the Retrieve data function @@ -304,67 +659,102 @@ public void testGetAllModuleInfoWhenModulesOpen() @Test public void testGetChallenges() { - //Open all Modules First so that the GetAllModuleInfo method will return data - if(Setter.openAllModules(applicationRoot)) + String userName = new String("testGetChallengesUser"); + try { - String modules = Getter.getChallenges(applicationRoot, adminUserId, lang); - if(!modules.isEmpty()) //Some Modules were included in response + if(verifyTestUser(applicationRoot, userName, userName)) { - //Get number of Challenges returned by getChallenges method - int numberofChallengesReturned = (modules.length() - modules.replace("class='lesson'", "").length()) / "class='lesson'".length(); - if(numberofChallengesReturned > 58) + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the GetAllModuleInfo method will return data + if(Setter.openAllModules(applicationRoot)) { - log.debug("PASS: Found " + numberofChallengesReturned + " modules"); - return; + String modules = Getter.getChallenges(applicationRoot, userId, locale); + if(!modules.isEmpty()) //Some Modules were included in response + { + //Get number of Challenges returned by getChallenges method + int numberofChallengesReturned = (modules.length() - modules.replace("class='lesson'", "").length()) / "class='lesson'".length(); + if(numberofChallengesReturned > 58) + { + log.debug("PASS: Found " + numberofChallengesReturned + " modules"); + return; + } + else + { + log.debug("Too Few Challenges Returned to pass: " + numberofChallengesReturned + " returned"); + fail("Too Few Challenges Returned to Pass"); + } + } + else + { + log.fatal("No Modules Found. Returned empty String"); + fail("No Modules Found"); + } } else { - log.debug("Too Few Challenges Returned to pass: " + numberofChallengesReturned + " returned"); - fail("Too Few Challenges Returned to Pass"); + fail("Could Not Mark Modules as Open Before Test"); } } else { - log.fatal("No Modules Found. Returned empty String"); - fail("No Modules Found"); + fail("Could not verify user (No Exception Failure)"); } } - else + catch(Exception e) { - fail("Could Not Mark Modules as Open Before Test"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @Test - public void testGetChallengesWhenModulesClosed() { - //Open all Modules First so that the GetAllModuleInfo method will return data - if(Setter.closeAllModules(applicationRoot)) + public void testGetChallengesWhenModulesClosed() + { + String userName = new String("getChallengesCLosedUser"); + try { - String modules = Getter.getChallenges(applicationRoot, adminUserId, lang); - if(!modules.isEmpty()) //Some Modules were included in response + if(verifyTestUser(applicationRoot, userName, userName)) { - //Get number of Challenges returned by getChallenges method - int numberofChallengesReturned = (modules.length() - modules.replace("class='lesson'", "").length()) / "class='lesson'".length(); - if(!(numberofChallengesReturned > 0)) + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the GetAllModuleInfo method will return data + if(Setter.closeAllModules(applicationRoot)) { - log.debug("PASS: Found " + numberofChallengesReturned + " modules"); - return; + String modules = Getter.getChallenges(applicationRoot, userId, locale); + if(!modules.isEmpty()) //Some Modules were included in response + { + //Get number of Challenges returned by getChallenges method + int numberofChallengesReturned = (modules.length() - modules.replace("class='lesson'", "").length()) / "class='lesson'".length(); + if(!(numberofChallengesReturned > 0)) + { + log.debug("PASS: Found " + numberofChallengesReturned + " modules"); + return; + } + else + { + log.debug("Too Many Challenges Returned to pass: " + numberofChallengesReturned + " returned"); + fail("Challenges Returned when all modules were closed"); + } + } + else + { + log.fatal("No Modules Found. Returned empty String"); + fail("No Modules Found"); + } } else { - log.debug("Too Many Challenges Returned to pass: " + numberofChallengesReturned + " returned"); - fail("Challenges Returned when all modules were closed"); + fail("Could Not Mark Modules as Open Before Test"); } } else { - log.fatal("No Modules Found. Returned empty String"); - fail("No Modules Found"); + fail("Could not verify user (No Exception Failure)"); } } - else + catch(Exception e) { - fail("Could Not Mark Modules as Open Before Test"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } @@ -516,10 +906,10 @@ public void testGetCsrfForumWithIframe() } else { + String userName = new String("userforiframeclass"); try { - String userName = new String("userforiframeclass"); - if(Setter.userCreate(applicationRoot, classId, userName, "password1726", "player", "iframeclass@gmail.com", false)) + if(verifyTestUser(applicationRoot, userName, userName, classId)) { //Open all Modules First so that the Module Can Be Opened by the user if(Setter.openAllModules(applicationRoot)) @@ -549,11 +939,15 @@ public void testGetCsrfForumWithIframe() fail("Could not Mark All Modules as Open"); } } + else + { + fail("Could not verify user (No Exception Failure)"); + } } catch(Exception e) { - log.fatal("Could not Create User: " + e.toString()); - fail("Could not create user in class"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } } @@ -569,7 +963,6 @@ public void testGetCsrfForumWithImg() { String classId = new String(); String moduleId = new String("0a37cb9296ff3763f7f3a45ff313bce47afa9384"); //CSRF Challenge 5 - Locale locale = new Locale("en_GB"); ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.csrf.csrfGenerics", locale); if(Setter.classCreate(applicationRoot, "NewClassForCsrfImgFourm", "2015")) { @@ -596,10 +989,10 @@ public void testGetCsrfForumWithImg() } else { + String userName = new String("userforimgclass"); try { - String userName = new String("userforimgclass"); - if(Setter.userCreate(applicationRoot, classId, userName, "password1726", "player", "imgclass@gmail.com", false)) + if(verifyTestUser(applicationRoot, userName, userName, classId)) { //Open all Modules First so that the Module Can Be Opened by the user if(Setter.openAllModules(applicationRoot)) @@ -629,11 +1022,15 @@ public void testGetCsrfForumWithImg() fail("Could not Mark All Modules as Open"); } } + else + { + fail("Could not verify user (No Exception Failure)"); + } } catch(Exception e) { - log.fatal("Could not Create User: " + e.toString()); - fail("Could not create user in class"); + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } } } @@ -643,17 +1040,255 @@ public void testGetCsrfForumWithImg() } } - /* @Test - public void testGetFeedback() { - fail("Not yet implemented"); + public void testGetFeedback() + { + String userName = new String("userGetFeedback"); + String dataStorageLessonId = new String("53a53a66cb3bf3e4c665c442425ca90e29536edd"); + try + { + if(verifyTestUser(applicationRoot, userName, userName)) + { + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, dataStorageLessonId, userId).isEmpty()) + { + //Then, Mark the Challenge Complete for user (Insecure Data Storage Lesson) + String feedbackSearchCode = "RwarUNiqueFeedbackCodeToSEARCHFor1182371723"; + String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, dataStorageLessonId, userId, feedbackSearchCode, 1, 1, 1); + if (markLevelCompleteTest != null) + { + String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, dataStorageLessonId, userId); + log.debug("checkPlayerResultTest" + checkPlayerResultTest); + if(checkPlayerResultTest == null) + { + log.debug("Checking to see if the feedback is included in the getFeeback response for the module"); + String feedback = Getter.getFeedback(applicationRoot, dataStorageLessonId); + if(feedback.indexOf(feedbackSearchCode) > -1) + { + log.debug("PASS: Detected the user's feedback"); + return; + } + else + { + log.fatal("User's Feedback '" + feedbackSearchCode + "' was not found in: " + feedback); + fail("Could not find user's feedback"); + } + } + else + { + fail("Function says user has not completed module"); //Even though this test just marked it as Completed + } + } + else + fail("Could not mark data storage lesson as complete for user"); + } + else + fail("Could not Mark Data Storage Lesson as Opened by Default admin"); + } + else + fail("Could not Open All Modules"); + } + else + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) + { + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); + } } - + @Test - public void testGetIncrementalModules() { - fail("Not yet implemented"); + public void testGetIncrementalModulesWithNoneComplete() + { + String userName = new String("testIncModuleMenu1"); + String lowestRankModuleId = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //This should be changed if an easier module is made + try + { + if(verifyTestUser(applicationRoot, userName, userName)) + { + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First + if(Setter.openAllModules(applicationRoot)) + { + String incrementalModules = Getter.getIncrementalModules(applicationRoot, userId, lang, "testingCSRFtoken"); + if(incrementalModules.indexOf("Completed") == -1) //User should not have completed any modules. The Completed Button should not be present + { + if(incrementalModules.indexOf(lowestRankModuleId) > -1) //The only module Id to be returned should be this one as it is the first presented (Lowest Incremental Rank) + { + if(incrementalModules.indexOf("Get Next Challenge") > -1) //This is the English string that should be included with the lang submitted in this unit test + { + log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test"); + return; + } + else + { + fail("Could not Detect i18n English Values in Menu"); + } + } + else + { + fail("The Module Id Returned was not the Known First Level. Ie not: " + lowestRankModuleId); + } + } + else + { + fail("CTF Menu Appears as if User Has Completed Modules When They Have Not"); + } + //Wont Log unless unit doesnt pass + log.debug(incrementalModules); + } + else + { + fail("Could not open All Modules"); + } + } + else + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) + { + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); + } } - + + @Test + public void testGetIncrementalModulesWithModulesClosed() + { + String userName = new String("testIncModuleMenu2"); + try + { + if(verifyTestUser(applicationRoot, userName, userName)) + { + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Close all Modules First + if(Setter.closeAllModules(applicationRoot)) + { + String incrementalModules = Getter.getIncrementalModules(applicationRoot, userId, lang, "testingCSRFtoken"); + if(incrementalModules.indexOf("You've Finished!") > -1) //IF no modules are open, this is the expected leading string + { + log.debug("PASS: Menu appears to have compiled correctly"); + } + else + { + log.debug("incrementalModules returned: " + incrementalModules); + fail("Could not Detect Finished Message"); + } + } + else + { + fail("Could not Close All Modules"); + } + } + else + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) + { + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); + } + } + + @Test + public void testGetIncrementalModulesWithOneModuleComplete() + { + String userName = new String("testIncModuleMenu3"); + String lowestRankModuleId = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //This should be changed if an easier module is made + String secondLowestRankModuleId = "b9d82aa7b46ddaddb6acfe470452a8362136a31e"; //This should be changed if an easier module is made or is orded before this + try + { + if(verifyTestUser(applicationRoot, userName, userName)) + { + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First + if(Setter.openAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, lowestRankModuleId, userId).isEmpty()) + { + //Then, Mark the Challenge Complete for user (Insecure Data Storage Lesson) + String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, lowestRankModuleId, userId, "Feedback is Not Enabled", 1, 1, 1); + if (markLevelCompleteTest != null) + { + String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, lowestRankModuleId, userId); + log.debug("checkPlayerResultTest" + checkPlayerResultTest); + if(checkPlayerResultTest == null) + { + String incrementalModules = Getter.getIncrementalModules(applicationRoot, userId, lang, "testingCSRFtoken"); + if(incrementalModules.indexOf("Completed") > -1) //User should have completed one module. The Completed Button should be present + { + if(incrementalModules.indexOf(lowestRankModuleId) > -1) //The only completed module Id to be returned should be this one + { + if(incrementalModules.indexOf(secondLowestRankModuleId) > -1) + { + if(incrementalModules.indexOf("Get Next Challenge") > -1) //This is the English string that should be included with the lang submitted in this unit test + { + log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test"); + return; + } + else + { + fail("Could not Detect i18n English Values in Menu"); + } + } + else + { + fail("The Module Id Returned to be Completed Next was not the Known 2nd Level. Ie not: " + secondLowestRankModuleId); + } + } + else + { + fail("The Module Id Returned was not the Known First Level. Ie not: " + lowestRankModuleId); + } + } + else + { + fail("CTF Menu Appears as if User Has Completed Modules When They Have Not"); + } + //Wont Log unless unit doesnt pass + log.debug(incrementalModules); + } + else + { + fail("checkPlayerResultTest says user has not completed module"); //Even though this test just marked it as Completed + } + } + else + fail("Could not mark data storage lesson as complete for user"); + } + else + fail("Could not Lowest Rank Lesson as Opened by User"); + } + else + { + fail("Could not open All Modules"); + } + } + else + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) + { + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); + } + } + + /* @Test public void testGetIncrementalModulesWithoutScript() { fail("Not yet implemented"); @@ -672,61 +1307,149 @@ public void testGetLessons() { @Test public void testGetModuleAddress() { + String userName = new String("userGetModuleAddress"); String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + try { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, insecureCryptoLesson, adminUserId).isEmpty()) + if(verifyTestUser(applicationRoot, userName, userName)) { - log.debug("PASS: Could mark level open when level was marked as open"); + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the Module Can Be Opened + if(Setter.openAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(!Getter.getModuleAddress(applicationRoot, insecureCryptoLesson, userId).isEmpty()) + { + log.debug("PASS: Could mark level open when level was marked as open"); + return; + } + else + fail("Could not Insecure Crypto Lesson as Opened by user"); + } + else + fail("Could not Open All Modules"); } else - fail("Could not Insecure Crypto Lesson as Opened by Default admin"); + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) + { + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); } - else - fail("Could not Open All Modules"); } @Test public void testGetModuleAddressWhenClosed() { + String userName = new String("userGetModuleAddressTwo"); String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); - //Open all Modules First so that the Module Can Be Opened - if(Setter.closeAllModules(applicationRoot)) + try { - //Simulate user Opening Level - if(Getter.getModuleAddress(applicationRoot, insecureCryptoLesson, adminUserId).isEmpty()) + if(verifyTestUser(applicationRoot, userName, userName)) { - log.debug("PASS: Could not mark level open when level was marked as open"); + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Close all modules first + if(Setter.closeAllModules(applicationRoot)) + { + //Simulate user Opening Level + if(Getter.getModuleAddress(applicationRoot, insecureCryptoLesson, userId).isEmpty()) + { + log.debug("PASS: Could not get Module URL when Module Closed"); + } + else + fail("Could Get Module Address when marked as closed"); + } + else + fail("Could not Close All Modules"); } else - fail("Could Mark Insecure Crypto Lesson as Opened by Default admin when marked as closed"); + { + fail("Could not verify user (No Exception Failure)"); + } + } + catch(Exception e) + { + log.fatal("Could not Verify User: " + e.toString()); + fail("Could not Verify User " + userName); + } + } + + @Test + public void testGetModuleCategory() + { + String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); + if(Getter.getModuleCategory(applicationRoot, insecureCryptoLesson).compareTo("Insecure Cryptographic Storage") != 0) + { + fail("Incorrect Category Returned for Insecure Crypto Lesson"); } else - fail("Could not Open All Modules"); + { + log.debug("PASS: Expected Category Returned"); + } } - /* + @Test - public void testGetModuleCategory() { - fail("Not yet implemented"); + public void testGetModuleHash() + { + String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); + if(Getter.getModuleHash(applicationRoot, insecureCryptoLesson).compareTo("if38ebb58ea2d245fa792709370c00ca655fded295c90ef36f3a6c5146c29ef2") != 0) + { + fail("Incorrect Hash Returned for Insecure Crypto Lesson"); + } + else + { + log.debug("PASS: Expected Hash Returned"); + } } - + @Test - public void testGetModuleHash() { - fail("Not yet implemented"); + public void testGetModuleIdFromHash() + { + String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); + if(Getter.getModuleIdFromHash(applicationRoot, Getter.getModuleHash(applicationRoot, insecureCryptoLesson)).compareTo(insecureCryptoLesson) != 0) + { + fail("Incorrect moduleId Returned for Insecure Crypto Lesson Hash Search"); + } + else + { + log.debug("PASS: Expected Id Returned"); + } } - + @Test - public void testGetModuleIdFromHash() { - fail("Not yet implemented"); + public void testGetModuleKeyTypeHardcodedKey() + { + String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); + if(Getter.getModuleKeyType(applicationRoot, insecureCryptoLesson)) + { + log.debug("PASS: Hardcoded Key Detected on Hardcoded Level"); + } + else + { + log.fatal("Encrypted Key Detected On Hardcoded Key Module"); + fail("Encrypted Key Detected On Hardcoded Key Module"); + } } - + @Test - public void testGetModuleKeyType() { - fail("Not yet implemented"); + public void testGetModuleKeyTypeEncryptedKey() + { + String csrfChallengeThree = new String("5ca9115f3279b9b9f3308eb6a59a4fcd374846d6"); + if(!Getter.getModuleKeyType(applicationRoot, csrfChallengeThree)) + { + log.debug("PASS: Encrypted Key Detected on Encrypted Level"); + } + else + { + log.fatal("Hardcoded Key Detected On Encrypted Key Module"); + fail("Hardcoded Key Detected On Encrypted Key Module"); + } } + /* @Test public void testGetModuleResult() { fail("Not yet implemented"); From 8005e8f756ffceecd2f1b81ba0ae49ef49344500 Mon Sep 17 00:00:00 2001 From: SeanDuggan Date: Sat, 3 Oct 2015 12:59:34 +0100 Subject: [PATCH 041/112] Fixed wayward RequestFocus tag in BrokenAuth. Fixed DataLeakage Crash. Made error messages on login less verbose --- .../app/src/main/res/layout/broken.xml | 157 +++--- .../com/mobshep/shepherdlogin/LoggedIn.java | 9 +- .../mobshep/shepherdlogin/MainActivity.java | 36 +- .../shepherdlogin/SessionProvider.java | 21 +- .../shepherdresolver/MainActivity.java | 38 +- .../mobshep/udataleakage/UDataLeakage.java | 464 +++++++++--------- 6 files changed, 368 insertions(+), 357 deletions(-) diff --git a/MobileShepherd/PoorAuthentication/app/src/main/res/layout/broken.xml b/MobileShepherd/PoorAuthentication/app/src/main/res/layout/broken.xml index 748a27ab5..17fc61e3f 100644 --- a/MobileShepherd/PoorAuthentication/app/src/main/res/layout/broken.xml +++ b/MobileShepherd/PoorAuthentication/app/src/main/res/layout/broken.xml @@ -1,79 +1,80 @@ - - - - - - - - - - - - -

    From 7c8706869d29928ec1d17f921f3daf3eba057a2f Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Sat, 3 Oct 2015 14:52:25 +0100 Subject: [PATCH 044/112] Poor Data Validation Challenge Translation Support --- .../poorValidationStrings.properties | 14 ++++++++ .../poorValidationStrings.properties | 5 +++ ...af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp | 32 ++++++++++-------- ...f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp | 33 +++++++++++-------- .../module/challenge/CsrfChallengeJSON.java | 2 +- .../module/challenge/PoorValidation1.java | 19 ++++++----- .../module/challenge/PoorValidation2.java | 19 ++++++----- 7 files changed, 79 insertions(+), 45 deletions(-) create mode 100644 SecurityShepherdCore/src/i18n/challenges/poorValidation/poorValidationStrings.properties create mode 100644 SecurityShepherdCore/src/i18n/servlets/challenges/poorValidation/poorValidationStrings.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/poorValidation/poorValidationStrings.properties b/SecurityShepherdCore/src/i18n/challenges/poorValidation/poorValidationStrings.properties new file mode 100644 index 000000000..039cf51d6 --- /dev/null +++ b/SecurityShepherdCore/src/i18n/challenges/poorValidation/poorValidationStrings.properties @@ -0,0 +1,14 @@ +poorValidation.1.challengeName = Poor Validation One +poorValidation.2.challengeName = Poor Validation Two + +poorValidation.whatToDo = If you can buy trolls for free you'll receive the key for this level! +poorValidation.shopping = Super Meme Shopping +poorValidation.shopping.whatToDo = Use this shop to buy whatever old memes you like! +poorValidation.howToShop = Please select how many items you would like to buy and click submit +poorValidation.picture = Picture +poorValidation.cost = Cost +poorValidation.quantity = Quantity +poorValidation.placeOrder = Place Order +poorValidation.loading = Loading... + +error.occurred = An Error Occurred \ No newline at end of file diff --git a/SecurityShepherdCore/src/i18n/servlets/challenges/poorValidation/poorValidationStrings.properties b/SecurityShepherdCore/src/i18n/servlets/challenges/poorValidation/poorValidationStrings.properties new file mode 100644 index 000000000..b52bdd206 --- /dev/null +++ b/SecurityShepherdCore/src/i18n/servlets/challenges/poorValidation/poorValidationStrings.properties @@ -0,0 +1,5 @@ +poorValidation.orderComplete = Order Complete +poorValidation.orderComplete.message = Your order has been made and has been sent to our magic shipping department that knows where you want this to be delivered via brain wave sniffing techniques. +poorValidation.orderTotal = Your order comes to a total of +poorValidation.freeTrolls = Trolls were free, Well Done +poorValidation.badOrder = Order Failed - Please try again later \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp b/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp index 7e6b665a7..a8ecc808c 100644 --- a/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp @@ -24,8 +24,12 @@ String levelName = "Poor Validation 2"; //Alphanumeric Only String levelHash = "20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5"; -//Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = "Not used - See Below"; + +//Translation Stuff +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.poorValidation.poorValidationStrings", locale); +//Used more than once translations +String i18nLevelName = bundle.getString("poorValidation.2.challengeName"); ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) @@ -50,29 +54,29 @@ if (request.getSession() != null) - Security Shepherd - <%= levelName %> + Security Shepherd - <%= i18nLevelName %>
    -

    <%= levelName %>

    +

    <%= i18nLevelName %>

    - If you can buy trolls for free you'll receive the key for this level! + <%= bundle.getString("poorValidation.whatToDo") %>

    -

    Super Meme Shopping

    - Use this shop to buy whatever old memes you like! +

    <%= bundle.getString("poorValidation.shopping") %>

    +

    <%= bundle.getString("poorValidation.shopping.whatToDo") %>



    - - - + + + @@ -99,12 +103,12 @@ if (request.getSession() != null)
    PictureCostQuantity<%= bundle.getString("poorValidation.picture") %><%= bundle.getString("poorValidation.cost") %><%= bundle.getString("poorValidation.quantity") %>
    - Please select how many items you would like to buy and click submit +

    <%= bundle.getString("poorValidation.howToShop") %>

    -
    - + "/> +
    @@ -138,7 +142,7 @@ if (request.getSession() != null) } else { - $("#resultsDiv").html("

    An Error Occurred: " + ajaxCall.status + " " + ajaxCall.statusText + "

    "); + $("#resultsDiv").html("

    <%= bundle.getString("error.occurred") %>: " + ajaxCall.status + " " + ajaxCall.statusText + "

    "); } $("#resultsDiv").show("slow", function(){ $("#loadingSign").hide("fast", function(){ diff --git a/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp b/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp index 87674f34b..3793310c4 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp @@ -1,4 +1,5 @@ <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    @@ -23,8 +24,12 @@ String levelName = "Poor Validation 1"; //Alphanumeric Only String levelHash = "ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e"; -//Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = "Not used - See Below"; + +//Translation Stuff +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.poorValidation.poorValidationStrings", locale); +//Used more than once translations +String i18nLevelName = bundle.getString("poorValidation.1.challengeName"); ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) @@ -49,29 +54,29 @@ if (request.getSession() != null) - Security Shepherd - <%= levelName %> + Security Shepherd - <%= i18nLevelName %>
    -

    <%= levelName %>

    +

    <%= i18nLevelName %>

    - If you can buy trolls for free you'll receive the key for this level! + <%= bundle.getString("poorValidation.whatToDo") %>

    -

    Super Meme Shopping

    - Use this shop to buy whatever old memes you like! +

    <%= bundle.getString("poorValidation.shopping") %>

    +

    <%= bundle.getString("poorValidation.shopping.whatToDo") %>



    - - - + + + @@ -98,12 +103,12 @@ if (request.getSession() != null)
    PictureCostQuantity<%= bundle.getString("poorValidation.picture") %><%= bundle.getString("poorValidation.cost") %><%= bundle.getString("poorValidation.quantity") %>
    - Please select how many items you would like to buy and click submit + <%= bundle.getString("poorValidation.howToShop") %>
    -
    - + "/> +
    @@ -137,7 +142,7 @@ if (request.getSession() != null) } else { - $("#resultsDiv").html("

    An Error Occurred: " + ajaxCall.status + " " + ajaxCall.statusText + "

    "); + $("#resultsDiv").html("

    <%= bundle.getString("error.occurred") %>: " + ajaxCall.status + " " + ajaxCall.statusText + "

    "); } $("#resultsDiv").show("slow", function(){ $("#loadingSign").hide("fast", function(){ diff --git a/SecurityShepherdCore/src/servlets/module/challenge/CsrfChallengeJSON.java b/SecurityShepherdCore/src/servlets/module/challenge/CsrfChallengeJSON.java index 45e75b30c..5589d4a97 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/CsrfChallengeJSON.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/CsrfChallengeJSON.java @@ -20,7 +20,7 @@ import dbProcs.Setter; /** - * Cross Site Request Forgery Challenge New - Does not return result Key + * Cross Site Request Forgery Challenge JSON - Does not return result Key, just sets URL for Class Forum *

    * This file is part of the Security Shepherd Project. * diff --git a/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation1.java b/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation1.java index e8c4813b6..8c7ab04aa 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation1.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation1.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.io.PrintWriter; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -10,8 +12,6 @@ import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Encoder; import utils.Hash; import utils.ShepherdLogManager; @@ -55,13 +55,16 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) HttpSession ses = request.getSession(true); if(Validate.validateSession(ses)) { + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.poorValidation.poorValidationStrings", locale); + String currentUser = ses.getAttribute("userName").toString(); ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), currentUser); log.debug(levelName + " servlet accessed by: " + ses.getAttribute("userName").toString()); PrintWriter out = response.getWriter(); out.print(getServletInfo()); String htmlOutput = new String(); - Encoder encoder = ESAPI.encoder(); try { int megustaAmount = Integer.parseInt(request.getParameter("megustaAmount")); @@ -85,18 +88,18 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) int finalCost = megustaCost + rageCost + notBadCost + trollCost; //Output Order - htmlOutput = "

    Order Complete

    " - + "Your order has been made and has been sent to our magic shipping department that knows where you want this to be delivered via brain wave sniffing techniques.

    " - + "Your order comes to a total of $" + finalCost + ""; + htmlOutput = "

    " + bundle.getString("poorValidation.orderComplete")+ "

    " + + "

    " + bundle.getString("poorValidation.orderComplete.message")+ "

    " + + "

    " + bundle.getString("poorValidation.orderTotal")+ " $" + finalCost + "

    "; if (finalCost <= 0 && trollAmount > 0) { - htmlOutput += "

    Trolls were free, Well Done - " + encoder.encodeForHTML(Hash.generateUserSolution(levelSolution, currentUser)) + ""; + htmlOutput += "

    " + bundle.getString("poorValidation.freeTrolls")+ " - " + Hash.generateUserSolution(levelSolution, currentUser) + "

    "; } } catch(Exception e) { log.debug("Didn't complete order: " + e.toString()); - htmlOutput += "

    Order Failed - Please try again later

    "; + htmlOutput += "

    " + bundle.getString("poorValidation.badOrder")+ "

    "; } try { diff --git a/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation2.java b/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation2.java index 0e2c493ba..a053280c1 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation2.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/PoorValidation2.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.io.PrintWriter; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -10,8 +12,6 @@ import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Encoder; import utils.Hash; import utils.ShepherdLogManager; @@ -55,13 +55,16 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) HttpSession ses = request.getSession(true); if(Validate.validateSession(ses)) { + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.poorValidation.poorValidationStrings", locale); + String currentUser = ses.getAttribute("userName").toString(); ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), currentUser); log.debug(levelName + " servlet accessed by: " + ses.getAttribute("userName").toString()); PrintWriter out = response.getWriter(); out.print(getServletInfo()); String htmlOutput = new String(); - Encoder encoder = ESAPI.encoder(); try { int megustaAmount = validateAmount(Integer.parseInt(request.getParameter("megustaAmount"))); @@ -85,18 +88,18 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) int finalCost = megustaCost + rageCost + notBadCost + trollCost; //Output Order - htmlOutput = "

    Order Complete

    " - + "Your order has been made and has been sent to our magic shipping department that knows where you want this to be delivered via brain wave sniffing techniques.

    " - + "Your order comes to a total of $" + finalCost + ""; + htmlOutput = "

    " + bundle.getString("poorValidation.orderComplete") + "

    " + + "

    " + bundle.getString("poorValidation.orderComplete.message") + "


    " + + "

    " + bundle.getString("poorValidation.orderTotal") + " $" + finalCost + "

    "; if (finalCost <= 0 && trollAmount > 0) { - htmlOutput += "

    Trolls were free, Well Done - " + encoder.encodeForHTML(Hash.generateUserSolution(levelSolution, currentUser)) + ""; + htmlOutput += "

    " + bundle.getString("poorValidation.freeTrolls") + " - " + Hash.generateUserSolution(levelSolution, currentUser) + "

    "; } } catch(Exception e) { log.debug("Didn't complete order: " + e.toString()); - htmlOutput += "

    Order Failed - Please try again later

    "; + htmlOutput += "

    " + bundle.getString("poorValidation.badOrder") + "

    "; } try { From 81e26d77e31d5b000115f6aca547d85e9a198dbd Mon Sep 17 00:00:00 2001 From: SeanDuggan Date: Sat, 3 Oct 2015 15:07:35 +0100 Subject: [PATCH 045/112] InsecureData Translation Support Complete --- .../insecureData/insecureDataStrings.properties | 7 +++++++ ...b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp | 9 ++++++++- ...e961ab769967bdc75740ad2363803168b7907c794cd4.jsp | 13 ++++++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/SecurityShepherdCore/src/i18n/challenges/mobile/insecureData/insecureDataStrings.properties b/SecurityShepherdCore/src/i18n/challenges/mobile/insecureData/insecureDataStrings.properties index 5bdecace9..d6e8dd35f 100644 --- a/SecurityShepherdCore/src/i18n/challenges/mobile/insecureData/insecureDataStrings.properties +++ b/SecurityShepherdCore/src/i18n/challenges/mobile/insecureData/insecureDataStrings.properties @@ -1,2 +1,9 @@ challenge1.challengeName = Mobile Insecure Data Storage 1 challenge1.para1 = The App for this challenge stores user credentials within a database, the data has not been stored in plain text but it also has not been encrypted. Make the credentials readable to get the key. The key is the Admin's password. + +challenge2.challengeName = Mobile Insecure Data Storage 2 +challenge2.para1 = The App for this challenge, InsecureData2 uses a hashing algorithm on stored user credentials. However, there are two issues here, firstly the user is using a bad password. Secondly, the App does not use a salt when hashing the data. The result key to this challenge is the user's password. + + +challenge3.challengeName = Mobile Insecure Data Storage 3 +challenge3.para1 = Not all Apps will use sqlite to store user data, in some cases SharedPreferences is used. The key to this level can be gained once you log in as a legitimate user. diff --git a/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp b/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp index 713d11649..93910e646 100644 --- a/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp @@ -27,6 +27,13 @@ String levelHash = "11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32d //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; +//Translation Stuff +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.insecureData.insecureDataStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge3.challengeName"); +String paragraph1 = bundle.getString("challenge3.para1"); + ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -62,7 +69,7 @@ if (request.getSession() != null)


    - Not all Apps will use sqlite to store user data, in some cases SharedPreferences is used. The key to this level can be gained once you log in as a legitimate user. + <%= paragraph1 %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp b/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp index 00eb48836..5f60dd5e2 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp @@ -1,4 +1,5 @@ <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** *

    @@ -26,6 +27,13 @@ String levelHash = "ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c79 //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; +//Translation Stuff +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.insecureData.insecureDataStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge2.challengeName"); +String paragraph1 = bundle.getString("challenge2.para1"); + ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -60,12 +68,11 @@ if (request.getSession() != null)

    <%= levelName %>


    - The App for this challenge, InsecureData3 uses a hashing algorithm on stored user credentials. However, there are two issues here, firstly the user is using a bad password. Secondly, the App does not use a salt when hashing the data. The result key to this challenge is the user's password. - + <%= paragraph1 %>

    - <%= Analytics.getMobileLevelBlurb("InsecureData3.apk") %> + <%= Analytics.getMobileLevelBlurb("InsecureData2.apk") %>

    From bd8496657186094932400739513b891120f5e14f Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Sat, 3 Oct 2015 15:13:39 +0100 Subject: [PATCH 046/112] Moving Mobile Blurb From Analytics.java to i18n for Mobile Lessons --- .../src/i18n/moduleGenerics/mobileGenericStrings.properties | 2 ++ ...b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp | 3 ++- ...c535845d93c32fd99b94f70afe9cca3f78c1e4766fee1cc08c035ec.jsp | 3 ++- ...b81c88f7eb761c1975481c4ce397b80291d99307cfad69662277d39.jsp | 3 ++- ...d5b56a17c1f30550dd34e8d6bd8b037f05341e64e94f5411c10ac8e.jsp | 3 ++- ...32e096d6a74a0623842c4157e29b9bcc44e8a827be3bb7e58c9a212.jsp | 3 ++- ...41f59e6bed7325f56576e1dc140393185afca8975fbd6822ebf392f.jsp | 3 ++- ...1ec4452cc0707e546a7c0f68abc6ef2ab747ea87e0892767152eae1.jsp | 3 ++- 8 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 SecurityShepherdCore/src/i18n/moduleGenerics/mobileGenericStrings.properties diff --git a/SecurityShepherdCore/src/i18n/moduleGenerics/mobileGenericStrings.properties b/SecurityShepherdCore/src/i18n/moduleGenerics/mobileGenericStrings.properties new file mode 100644 index 000000000..cf929bc08 --- /dev/null +++ b/SecurityShepherdCore/src/i18n/moduleGenerics/mobileGenericStrings.properties @@ -0,0 +1,2 @@ +mobileBlurb.vmLink.1 = To complete this challenge you'll need to use the +mobileBlurb.vmLink.2 = app found in the Security Shepherd Android Virtual Machine. \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp b/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp index a713b3c82..ed2f83dfa 100644 --- a/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp @@ -9,6 +9,7 @@ //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.m_reverse_engineering." + levelHash, locale); + ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); //Used more than once translations String translatedLevelName = bundle.getString("title.question.m_reverse_engineering"); /** @@ -88,7 +89,7 @@ <%= bundle.getString("challenge.description") %>

    - <%= Analytics.getMobileLevelBlurb("ReverseEngineer.apk") %> + <%= mobile.getString("mobileBlurb.vmLink.1") + " ReverseEngineer.apk " + mobile.getString("mobileBlurb.vmLink.2") %> "; public static String sourceForgeMobileVmLinkBlurb = new String("" + "To complete this challenge you'll need to use the Security Shepherd Android Virtual Machine that contains the app. "); - private static String mobileVmLinkBlurb1 = new String("To complete this challenge you'll need to use the "); - private static String mobileVmLinkBlurb2 = new String(" app found in the Security Shepherd Android Virtual Machine."); public static String sponsorshipMessage = new String("

    Project Sponsors

    " + "

    " + "The OWASP Security Shepherd project would like to acknowledge and thank the generous support of our sponsors. Please check out their web pages and follow them on twitter." + @@ -32,11 +30,5 @@ public class Analytics "" + "

    " + "The OWASP Security Shepherd Project would also like to thank Dr. Anthony Keane and the ITB Security Research Lab for hosting the public https://owasp.securityShepherd.eu!" + - "

    "); - - public static String getMobileLevelBlurb (String appName) - { - return mobileVmLinkBlurb1 + appName + mobileVmLinkBlurb2; - //TODO Extract strings for translation - } + "

    "); } From 203aa7f2980df4c9b8c6c69cc38e20dec5840cce Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Sat, 3 Oct 2015 15:41:27 +0100 Subject: [PATCH 049/112] Merging Conflicts --- ...4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp b/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp index bc945e069..0e8983740 100644 --- a/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp @@ -24,19 +24,14 @@ String levelName = "Mobile Poor Authentication 1"; //Alphanumeric Only String levelHash = "efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467"; - -//Translation Stuff -Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.insecureData.insecureDataStrings", locale); -ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); - //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; - //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.insecureData.insecureDataStrings", locale); +ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); + //Used more than once translations String LevelName = bundle.getString("challenge1.challengeName"); String paragraph1 = bundle.getString("challenge1.para1"); @@ -75,12 +70,10 @@ if (request.getSession() != null)

    <%= levelName %>


    - You must log into the App PoorAuthentication2.apk to get the key. The Username and Password have been saved but this App uses a specially generated Authentication Code. + <%= paragraph1 %>

    - <%= mobile.getString("mobileBlurb.vmLink.1") + " PoorAuthentication2.apk " + mobile.getString("mobileBlurb.vmLink.2") %> -

    From e690e0eed4de57397776d29cef54ca5e0c72cfd9 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Sat, 3 Oct 2015 15:45:33 +0100 Subject: [PATCH 050/112] Core Schema Typo Fix --- SecurityShepherdCore/database/coreSchema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityShepherdCore/database/coreSchema.sql b/SecurityShepherdCore/database/coreSchema.sql index 77382deb8..5ceab89a7 100644 --- a/SecurityShepherdCore/database/coreSchema.sql +++ b/SecurityShepherdCore/database/coreSchema.sql @@ -1427,7 +1427,7 @@ INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleT INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('63bc4811a2e72a7c833962e5d47a41251cd90de3', 'Broken Crypto 2', 'broken.crypto.2', 'challenge', 'Mobile Broken Crypto', 'mobile.broken.crypto', 'DancingRobotChilliSauce', 'fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc', 'open', '149', '75', '5', 1); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('e635fce334aa61fdaa459c21c286d6332eddcdd3', 'Client Side Injection 2', 'client.side.injection.2', 'challenge', 'Mobile Injection', 'mobile.injection', 'BurpingChimneys', 'cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425', 'open', '155', '80', '5', 1); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('0a37cb9296ff3763f7f3a45ff313bce47afa9384', 'CSRF 5', 'csrf.5', 'challenge', 'CSRF', 'csrf', '8f34078ef3e53f619618d9def1ede8a6a9117c77c2fad22f76bba633da83e6d4', '70b96195472adf3bf347cbc37c34489287969d5ba504ac2439915184d6e5dc49', 'open', '156', '80', '5', 0); -INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('3b14ca3c8f9b90c9b2c8cd1fba9fa67add1272a3', 'Poor Data Validation 2', 'poor.data.validation.2', 'challenge', 'Poor Data Validation', 'poor.data.validation', '05adf1e4afeb5550faf7edbec99170b40e79168ecb3a5da19943f05a3fe08c8e', '20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5', 'open', '157', '80', '5', 0); +INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('3b14ca3c8f9b90c9b2c8cd1fba9fa67add1272a3', 'Poor Data Validation 2', 'poor.data.validation.2', 'challenge', 'Poor Data Validation', 'poor.data.validation', '05adf1e4afeb5550faf7edbec99170b40e79168ecb3a5da19943f05a3fe08c8e', '20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5', 'open', '157', '80', '5', 0); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('ba6e65e4881c8499b5e53eb33b5be6b5d0f1fb2c', 'Poor Authentication 1', 'poor.authentication.1', 'challenge', 'Mobile Poor Authentication', 'mobile.poor.authentication', 'MegaKillerExtremeCheese', 'efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467', 'open', '160', '60', '5', 1); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('c7ac1e05faa2d4b1016cfcc726e0689419662784', 'Failure to Restrict URL Access 2', 'failure.to.restrict.url.access.2', 'challenge', 'Failure to Restrict URL Access', 'failure.to.restrict.url.access', '40b675e3d404c52b36abe31d05842b283975ec62e8', '278fa30ee727b74b9a2522a5ca3bf993087de5a0ac72adff216002abf79146fa', 'open', '165', '85', '5', 0); INSERT INTO modules (`moduleId`, `moduleName`, `moduleNameLangPointer`, `moduleType`, `moduleCategory`, `moduleCategoryLangPointer`, `moduleResult`, `moduleHash`, `moduleStatus`, `incrementalRank`, `scoreValue`, `scoreBonus`, `hardcodedKey`) VALUES ('fccf8e4d5372ee5a73af5f862dc810545d19b176', 'Cross Site Scripting 5', 'cross.site.scripting.5', 'challenge', 'XSS', 'xss', '7d7cc278c30cca985ab027e9f9e09e2f759e5a3d1f63293', 'f37d45f597832cdc6e91358dca3f53039d4489c94df2ee280d6203b389dd5671', 'open', '166', '85', '5', 0); From d2f5ba267021ee0bad6c4e27ca644c0db2c3c3c8 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Sat, 3 Oct 2015 17:35:45 +0100 Subject: [PATCH 051/112] Updating Version Number to 3.0 --- SecurityShepherdCore/src/i18n/text.properties | 9 ++++----- SecurityShepherdCore/src/i18n/text_es.properties | 6 +++--- SecurityShepherdCore/src/i18n/text_ga.properties | 6 +++--- SecurityShepherdCore/src/i18n/text_pt.properties | 2 +- SecurityShepherdCore/src/i18n/text_zh.properties | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/SecurityShepherdCore/src/i18n/text.properties b/SecurityShepherdCore/src/i18n/text.properties index 72fad5cdb..362d54dd6 100644 --- a/SecurityShepherdCore/src/i18n/text.properties +++ b/SecurityShepherdCore/src/i18n/text.properties @@ -21,7 +21,7 @@ generic.text.pleaseWait = Please Wait! generic.text.register = Register generic.text.scoreboard = Scoreboard generic.text.searchModules = Search Modules -generic.text.shepherdVersion = Security Shepherd Version: 2.4 +generic.text.shepherdVersion = Security Shepherd Version: 3.0 generic.text.sorryError = Sorry but there was an error generic.text.submitResult = Submit Result Key Here generic.text.submit = Submit @@ -100,7 +100,6 @@ login.text.thanks = The OWASP Security Shepherd Project would also like to thank readyToPlay.text.info.notReady = Refresh the home page! If this error persists; Log out and back in! If this error continues to persist, please contact an Administrator! readyToPlay.text.info.enteredGame = Now that you can see this, you're good to go! Get cracking on lessons and challenges!

    Remember, the levels you are playing are sub applications. Keep the game play in these applications! Stay away from your session ID's! You'll just log yourself out of you change them!

    If you havn't already configured a web proxy, you better! It makes things much easier! - - - - +sponsorship.title = Project Sponsors +sponsorship.message.1 = The OWASP Security Shepherd project would like to acknowledge and thank the generous support of our sponsors. Please check out their web pages and follow them on twitter. +sponsorship.message.2 = The OWASP Security Shepherd Project would also like to thank Dr. Anthony Keane and the ITB Security Research Lab for hosting the public https://owasp.securityShepherd.eu! \ No newline at end of file diff --git a/SecurityShepherdCore/src/i18n/text_es.properties b/SecurityShepherdCore/src/i18n/text_es.properties index 6c41857e1..0183f5886 100644 --- a/SecurityShepherdCore/src/i18n/text_es.properties +++ b/SecurityShepherdCore/src/i18n/text_es.properties @@ -21,9 +21,9 @@ generic.text.pleaseWait = Por favor, espera! generic.text.register = Registro generic.text.scoreboard = Marcador generic.text.searchModules = Search Modules (Translation Required) -generic.text.shepherdVersion = Security Shepherd Version: 2.4 (Translation Required) -generic.text.sorryError = Sorry but there was an error (Translation Required) -generic.text.submitResult = Submit Result Key Here (Translation Required) +generic.text.shepherdVersion = Security Shepherd Version: 3.0 +generic.text.sorryError = Sorry but there was an error +generic.text.submitResult = Submit Result Key Here generic.text.username = Nombre de usario generic.text.welcome = Bienvenida diff --git a/SecurityShepherdCore/src/i18n/text_ga.properties b/SecurityShepherdCore/src/i18n/text_ga.properties index 5dea63f6e..ecd6ba01f 100644 --- a/SecurityShepherdCore/src/i18n/text_ga.properties +++ b/SecurityShepherdCore/src/i18n/text_ga.properties @@ -21,9 +21,9 @@ generic.text.pleaseWait = D generic.text.register = Cláraigh generic.text.scoreboard = Scórchlár generic.text.searchModules = Search Modules (Translation Required) -generic.text.shepherdVersion = Security Shepherd Version: 2.4 (Translation Required) -generic.text.sorryError = Sorry but there was an error (Translation Required) -generic.text.submitResult = Submit Result Key Here (Translation Required) +generic.text.shepherdVersion = Security Shepherd Version: 3.0 +generic.text.sorryError = Sorry but there was an error +generic.text.submitResult = Submit Result Key Here generic.text.submit = Cúir Isteach generic.text.username = Ainm Úsáideora generic.text.welcome = Fáilte diff --git a/SecurityShepherdCore/src/i18n/text_pt.properties b/SecurityShepherdCore/src/i18n/text_pt.properties index d3550413a..f08d9c3dc 100644 --- a/SecurityShepherdCore/src/i18n/text_pt.properties +++ b/SecurityShepherdCore/src/i18n/text_pt.properties @@ -21,7 +21,7 @@ generic.text.pleaseWait = Esperar por favor generic.text.register = Registrar generic.text.scoreboard = Placar generic.text.searchModules = Busca liçãos -generic.text.shepherdVersion = Security Shepherd Versão: 2.4 +generic.text.shepherdVersion = Security Shepherd Versão: 3.0 generic.text.sorryError = Descupla, mas havia um erro generic.text.submitResult = Enviar seu chave resultado aqui generic.text.submit = Enviar diff --git a/SecurityShepherdCore/src/i18n/text_zh.properties b/SecurityShepherdCore/src/i18n/text_zh.properties index 902c0b15f..f33979090 100644 --- a/SecurityShepherdCore/src/i18n/text_zh.properties +++ b/SecurityShepherdCore/src/i18n/text_zh.properties @@ -21,7 +21,7 @@ generic.text.pleaseWait = \u8BF7\u7A0D\u4FAF! generic.text.register = \u6CE8\u518C generic.text.scoreboard = \u8BB0\u5206\u677F generic.text.searchModules = Search Modules -generic.text.shepherdVersion = Security Shepherd \u7248\u672C: 2.4 +generic.text.shepherdVersion = Security Shepherd \u7248\u672C: 3.0 generic.text.sorryError = Sorry but there was an error generic.text.submitResult = \u5728\u8FD9\u63D0\u4EA4\u7ED3\u679C\u94A5\u5319 generic.text.submit = \u63D0\u4EA4 From c980c663c6d4822e8e0ed5c67e4b7ed9c45c1882 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Sat, 3 Oct 2015 17:36:23 +0100 Subject: [PATCH 052/112] Sponsorship Message Translation Support --- .../src/jsp/admin/config/aboutShepherd.jsp | 6 +++--- SecurityShepherdCore/src/jsp/login.jsp | 2 +- SecurityShepherdCore/src/jsp/readyToPlay.jsp | 2 +- SecurityShepherdCore/src/utils/Analytics.java | 14 +++++++++++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/SecurityShepherdCore/src/jsp/admin/config/aboutShepherd.jsp b/SecurityShepherdCore/src/jsp/admin/config/aboutShepherd.jsp index bb3560969..ca319659d 100644 --- a/SecurityShepherdCore/src/jsp/admin/config/aboutShepherd.jsp +++ b/SecurityShepherdCore/src/jsp/admin/config/aboutShepherd.jsp @@ -1,4 +1,4 @@ -<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> +<%@ page contentType="text/html; charset=iso-8859-1" language="java" import="java.util.Locale, java.util.ResourceBundle, java.sql.*,java.io.*,java.net.*,org.owasp.esapi.ESAPI, org.owasp.esapi.Encoder, dbProcs.*, utils.*" errorPage="" %> <% ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), "DEBUG: aboutShepherd.jsp *************************"); @@ -52,14 +52,14 @@ String ApplicationRoot = getServletContext().getRealPath(""); %>

    The OWASP Security Shepherd Project

    - You are currently using Security Shepherd Version 2.4 + You are currently using Security Shepherd Version 3.0

    The OWASP Security Shepherd project is a web and mobile application security training platform. Security Shepherd has been designed to foster and improve security awareness among a varied skill-set demographic. The aim of this project is to take AppSec novices or experienced engineers and sharpen their penetration testing skill set to security expert status. For More information, please visit the OWASP Security Shepherd Wiki Page.

    Please report any bugs or any feature requests on the OWASP Security Shepherd Git Repository.

    - <%= Analytics.sponsorshipMessage %> + <%= Analytics.sponsorshipMessage(new Locale(Validate.validateLanguage(request.getSession()))) %>

    diff --git a/SecurityShepherdCore/src/jsp/login.jsp b/SecurityShepherdCore/src/jsp/login.jsp index ee2388edd..ce069f3b7 100644 --- a/SecurityShepherdCore/src/jsp/login.jsp +++ b/SecurityShepherdCore/src/jsp/login.jsp @@ -124,7 +124,7 @@ if(ses.getAttribute("loginFailed") != null) diff --git a/SecurityShepherdCore/src/jsp/readyToPlay.jsp b/SecurityShepherdCore/src/jsp/readyToPlay.jsp index b91029d3a..cacd251e9 100644 --- a/SecurityShepherdCore/src/jsp/readyToPlay.jsp +++ b/SecurityShepherdCore/src/jsp/readyToPlay.jsp @@ -75,7 +75,7 @@

    <% } %> - <%= Analytics.sponsorshipMessage %> + <%= Analytics.sponsorshipMessage(new Locale(Validate.validateLanguage(request.getSession()))) %> <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> diff --git a/SecurityShepherdCore/src/utils/Analytics.java b/SecurityShepherdCore/src/utils/Analytics.java index 976f4f98e..233a5fb6b 100644 --- a/SecurityShepherdCore/src/utils/Analytics.java +++ b/SecurityShepherdCore/src/utils/Analytics.java @@ -1,5 +1,8 @@ package utils; +import java.util.Locale; +import java.util.ResourceBundle; + /** * Manages What Google Analytics is used by the Shepherd instance. If Any * @author Mark Denihan @@ -20,15 +23,20 @@ public class Analytics ""; public static String sourceForgeMobileVmLinkBlurb = new String("" + "To complete this challenge you'll need to use the Security Shepherd Android Virtual Machine that contains the app. "); - public static String sponsorshipMessage = new String("

    Project Sponsors

    " + + public static String sponsorshipMessage(Locale locale) + { + //Get Language Bundle + ResourceBundle bundle = ResourceBundle.getBundle("i18n.text", locale); + return new String("

    " + bundle.getString("sponsorship.title") + "

    " + "

    " + - "The OWASP Security Shepherd project would like to acknowledge and thank the generous support of our sponsors. Please check out their web pages and follow them on twitter." + + bundle.getString("sponsorship.message.1") + "

    " + "\"BCC" + "\"EdgeScan\"" + "
    " + "" + "

    " + - "The OWASP Security Shepherd Project would also like to thank Dr. Anthony Keane and the ITB Security Research Lab for hosting the public https://owasp.securityShepherd.eu!" + + bundle.getString("sponsorship.message.2") + "

    "); + } } From 89d7ec53a2c75165e8393a4fb272780bf37c87c3 Mon Sep 17 00:00:00 2001 From: SeanDuggan Date: Sat, 3 Oct 2015 17:49:40 +0100 Subject: [PATCH 053/112] Translation Support for Client Side Injection --- .../clientSideInjection.properties | 8 ++++++++ ...4562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp | 14 +++++++++----- ...67b859322dd2750f633246842280dc68c858d208425.jsp | 14 +++++++++----- 3 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 SecurityShepherdCore/src/i18n/challenges/mobile/clientSideInjection/clientSideInjection.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/mobile/clientSideInjection/clientSideInjection.properties b/SecurityShepherdCore/src/i18n/challenges/mobile/clientSideInjection/clientSideInjection.properties new file mode 100644 index 000000000..ec9333ec6 --- /dev/null +++ b/SecurityShepherdCore/src/i18n/challenges/mobile/clientSideInjection/clientSideInjection.properties @@ -0,0 +1,8 @@ +challenge1.challengeName = Mobile Client Side Injection 1 +challenge1.para1 = The App for this challenge uses sloppy filters in an attempt to thwart attackers. These filters can be easily bypassed. Login to get the key. + + +//TODO + +challenge2.challengeName = Mobile Client Side Injection 2 +challenge2.para1 = This App has an upgraded form of filtering. Bypass the filtering, and perform SQL Injection to sign in as a legitimate user. diff --git a/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp b/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp index 5d19c937a..fd1065271 100644 --- a/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp @@ -25,11 +25,16 @@ String levelName = "Mobile Client Side Injection 1"; //Alphanumeric Only String levelHash = "8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef"; + //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.clientSideInjection.clientSideInjection", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge1.challengeName"); +String paragraph1 = bundle.getString("challenge1.para1"); + //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; @@ -64,17 +69,16 @@ if (request.getSession() != null)
    -

    <%= levelName %>

    +

    <%= LevelName %>

    <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> - <%= levelBlurb %>
    + <%= paragraph1 %> - The App for this challenge uses sloppy filters in an attempt to thwart attackers. These filters can be easily bypassed. Login to get the key.

    - <%= mobile.getString("mobileBlurb.vmLink.1") + " CSInjection2.apk " + mobile.getString("mobileBlurb.vmLink.2") %> + <%= mobile.getString("mobileBlurb.vmLink.1") + " CSInjection1.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp b/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp index c5fc012ea..e0fffa728 100644 --- a/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp @@ -27,9 +27,14 @@ String levelHash = "cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d20 //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.clientSideInjection.clientSideInjection", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge2.challengeName"); +String paragraph1 = bundle.getString("challenge2.para1"); + + //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); @@ -63,17 +68,16 @@ if (request.getSession() != null)
    -

    <%= levelName %>

    +

    <%= LevelName %>

    <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> <%= levelBlurb %>
    - This App has an upgraded form of filtering. Bypass the filtering, and perform SQL Injection to sign in as a legitimate user. - + <%= paragraph1 %>

    - <%= mobile.getString("mobileBlurb.vmLink.1") + " CSInjection3.apk " + mobile.getString("mobileBlurb.vmLink.2") %> + <%= mobile.getString("mobileBlurb.vmLink.1") + " CSInjection2.apk " + mobile.getString("mobileBlurb.vmLink.2") %> <% /* IF you need a form - Present it like this */ %> <% From 68c38e68a7ab713104be7f4fb31ff75d7dc199a8 Mon Sep 17 00:00:00 2001 From: SeanDuggan Date: Sat, 3 Oct 2015 18:27:39 +0100 Subject: [PATCH 054/112] Translation Support for Broken Crypto --- .../mobile/brokenCrypto/brokenCrypto.properties | 10 ++++++++++ ...65465590b499ceca941ab848805c00f5bf0a40c9717.jsp | 14 +++++++++----- ...a65fa90791bb312a3044b3110acb8a65d165376bf34.jsp | 12 +++++++----- ...fd317befff7427f6610ed626dfd43abf35295f106bc.jsp | 14 +++++++++----- 4 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 SecurityShepherdCore/src/i18n/challenges/mobile/brokenCrypto/brokenCrypto.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/mobile/brokenCrypto/brokenCrypto.properties b/SecurityShepherdCore/src/i18n/challenges/mobile/brokenCrypto/brokenCrypto.properties new file mode 100644 index 000000000..0187da821 --- /dev/null +++ b/SecurityShepherdCore/src/i18n/challenges/mobile/brokenCrypto/brokenCrypto.properties @@ -0,0 +1,10 @@ +challenge1.challengeName = Mobile Broken Crypto 1 +challenge1.para1 = This App uses a deprecated encryption algorithm (DES) and breaks a vital rule of key management. The key is in the conversation. Decrypt the chat to get the key. + + +challenge2.challengeName = Mobile Broken Crypto 2 +challenge2.para1 = This App uses DES to encrypt it's chat however it's developers have implemented this poorly. The key is in the conversation. Decrypt the chat to get the key. + + +challenge3.challengeName = Mobile Broken Crypto 3 +challenge3.para1 = The key for this challenge can be found in the client side database. Get the key to pass this challenge. diff --git a/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp b/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp index 02315a9f3..402a9b562 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp @@ -28,9 +28,13 @@ String levelHash = "d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.brokenCrypto.brokenCrypto", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge1.challengeName"); +String paragraph1 = bundle.getString("challenge1.para1"); + //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; @@ -57,7 +61,7 @@ if (request.getSession() != null) - Security Shepherd - <%= levelName %> + Security Shepherd - <%= LevelName %> @@ -65,16 +69,16 @@ if (request.getSession() != null)

    -

    <%= levelName %>

    +

    <%= LevelName %>

    <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> <%= levelBlurb %>
    - This App uses a deprecated encryption algorithm (DES) and breaks a vital rule of key management. The key is in the conversation. Decrypt the chat to get the key. + <%= paragraph1 %>

    - <%= mobile.getString("mobileBlurb.vmLink.1") + " BrokenCrypto2.apk " + mobile.getString("mobileBlurb.vmLink.2") %> + <%= mobile.getString("mobileBlurb.vmLink.1") + " BrokenCrypto1.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp b/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp index 72049ea11..13d681142 100644 --- a/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp @@ -25,12 +25,14 @@ String levelName = "Mobile Broken Crypto 3"; //Alphanumeric Only String levelHash = "fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc"; - //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.brokenCrypto.brokenCrypto", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge3.challengeName"); +String paragraph1 = bundle.getString("challenge3.para1"); //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; @@ -57,7 +59,7 @@ if (request.getSession() != null) - Security Shepherd - <%= levelName %> + Security Shepherd - <%= LevelName %> @@ -65,13 +67,13 @@ if (request.getSession() != null)
    -

    <%= levelName %>

    +

    <%= LevelName %>

    <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> <%= levelBlurb %>
    - The key for this challenge can be found in the client side database. Get the key to pass this challenge. + <%= paragraph1 %>

    <%= mobile.getString("mobileBlurb.vmLink.1") + " BrokenCrypto4.apk " + mobile.getString("mobileBlurb.vmLink.2") %> diff --git a/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp b/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp index 4115ba9cf..8091d5c2d 100644 --- a/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp @@ -27,9 +27,13 @@ String levelHash = "fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f1 //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.brokenCrypto.brokenCrypto", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge2.challengeName"); +String paragraph1 = bundle.getString("challenge2.para1"); + //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; @@ -56,7 +60,7 @@ if (request.getSession() != null) - Security Shepherd - <%= levelName %> + Security Shepherd - <%= LevelName %> @@ -64,16 +68,16 @@ if (request.getSession() != null)

    -

    <%= levelName %>

    +

    <%= LevelName %>

    <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> <%= levelBlurb %>
    - This App uses DES to encrypt it's chat however it's developers have implemented this poorly. The key is in the conversation. Decrypt the chat to get the key. + <%= paragraph1 %>

    - <%= mobile.getString("mobileBlurb.vmLink.1") + " BrokenCrypto3.apk " + mobile.getString("mobileBlurb.vmLink.2") %> + <%= mobile.getString("mobileBlurb.vmLink.1") + " BrokenCrypto2.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    From ccd8a162658c82ef620411209c5853660c604a9e Mon Sep 17 00:00:00 2001 From: SeanDuggan Date: Sat, 3 Oct 2015 18:52:48 +0100 Subject: [PATCH 055/112] Translation Support for Unintended Data Leakage --- .../reverseEngineer.properties | 8 +++++++- .../dataLeakage.properties | 5 +++++ ...40b48f53aad7b41390fe46c6f324fee748d136.jsp | 20 ++++++++++++------- ...a634194bf5da440282227c15954bbdfb54f0c7.jsp | 12 ++++++++--- 4 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 SecurityShepherdCore/src/i18n/challenges/mobile/unintendedDataLeakage/dataLeakage.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties b/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties index 4aa53f230..d1bb70f89 100644 --- a/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties +++ b/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties @@ -1,2 +1,8 @@ challenge1.challengeName = Mobile Reverse Engineer 1 -challenge1.para1 = This key to this challenge is the App author's name. Find it to complete the challenge. \ No newline at end of file +challenge1.para1 = This key to this challenge is the App author's name. Find it to complete the challenge. + +challenge1.challengeName = Mobile Reverse Engineer 2 +challenge1.para1 = + +challenge1.challengeName = Mobile Reverse Engineer 3 +challenge1.para1 = \ No newline at end of file diff --git a/SecurityShepherdCore/src/i18n/challenges/mobile/unintendedDataLeakage/dataLeakage.properties b/SecurityShepherdCore/src/i18n/challenges/mobile/unintendedDataLeakage/dataLeakage.properties new file mode 100644 index 000000000..7e3ca6982 --- /dev/null +++ b/SecurityShepherdCore/src/i18n/challenges/mobile/unintendedDataLeakage/dataLeakage.properties @@ -0,0 +1,5 @@ +challenge1.challengeName = Unintended Data Leakage 1 +challenge1.para1 = Log in as the user of this App to get the key for this challenge. Some data has been logged but it is up to the attacker to know what to do with this data. + +challenge2.challengeName = Unintended Data Leakage 2 +challenge2.para1 = This App is leaking logs. The Key is the winning lotto number! \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp b/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp index 092e0969c..e79ad5191 100644 --- a/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp @@ -27,9 +27,15 @@ String levelHash = "517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748 //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -// ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.unintendedDataLeakage.dataLeakage", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); + +//Used more than once translations +String LevelName = bundle.getString("challenge1.challengeName"); +String paragraph1 = bundle.getString("challenge1.para1"); + + //Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; @@ -57,7 +63,7 @@ if (request.getSession() != null) -Security Shepherd - <%=levelName%> +Security Shepherd - <%=LevelName%> @@ -65,18 +71,18 @@ if (request.getSession() != null)
    -

    <%=levelName%>

    +

    <%=LevelName%>

    <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> <%=levelBlurb%> -
    Log in as the user of this App to get the key for this - challenge. Some data has been logged but it is up to the - attacker to know what to do with this data.

    +
    + <%= paragraph1 %> +

    - <%=mobile.getString("mobileBlurb.vmLink.1") + " UDataLeakage2.apk " + mobile.getString("mobileBlurb.vmLink.2") %> + <%=mobile.getString("mobileBlurb.vmLink.1") + " UDataLeakage1.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    <% diff --git a/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp b/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp index 41b15bb66..64a4eede1 100644 --- a/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp @@ -27,9 +27,14 @@ String levelHash = "85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54 //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.unintendedDataLeakage.dataLeakage", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); + +//Used more than once translations +String LevelName = bundle.getString("challenge2.challengeName"); +String paragraph1 = bundle.getString("challenge2.para1"); + //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); @@ -63,13 +68,14 @@ if (request.getSession() != null)
    -

    <%= levelName %>

    +

    <%= LevelName %>

    <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> <%= levelBlurb %>
    -This App is leaking logs. The Key is the winning lotto number!
    + <%= paragraph1 %> +

    <%= mobile.getString("mobileBlurb.vmLink.1") + " UDataLeakage3.apk " + mobile.getString("mobileBlurb.vmLink.2") %> From 4f9c4ff307396871893e3bc7d9f4afc5438bb95d Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Sat, 3 Oct 2015 18:53:50 +0100 Subject: [PATCH 056/112] InsecureCryptoStorage Challenge Translation Support --- .../insecureCryptoStorage.properties | 36 ++++++++++++++++++ .../insecureCryptoStorage.properties | 8 ++++ ...14d422ea56705a7e3fc405a77bc269948ccae1.jsp | 28 +++++++++----- ...0cc18533a88b2363054a1f391fe855954d12f9.jsp | 37 +++++++++++-------- ...cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp | 29 +++++++++------ ...544edb2ee8f624249f3e920663edb733f15cd7.jsp | 17 ++++++--- .../module/challenge/BrokenCrypto3.java | 19 +++++++--- .../module/challenge/BrokenCrypto4.java | 21 ++++++----- 8 files changed, 137 insertions(+), 58 deletions(-) create mode 100644 SecurityShepherdCore/src/i18n/servlets/challenges/insecureCryptoStorage/insecureCryptoStorage.properties diff --git a/SecurityShepherdCore/src/i18n/challenges/insecureCryptoStorage/insecureCryptoStorage.properties b/SecurityShepherdCore/src/i18n/challenges/insecureCryptoStorage/insecureCryptoStorage.properties index e69de29bb..826b60c7e 100644 --- a/SecurityShepherdCore/src/i18n/challenges/insecureCryptoStorage/insecureCryptoStorage.properties +++ b/SecurityShepherdCore/src/i18n/challenges/insecureCryptoStorage/insecureCryptoStorage.properties @@ -0,0 +1,36 @@ +insecureCryptoStorage.1.challengename = Insecure Cryptographic Storage Challenge 1 +insecureCryptoStorage.2.challengename = Insecure Cryptographic Storage Challenge 2 +insecureCryptoStorage.3.challengename = Insecure Cryptographic Storage Challenge 3 +insecureCryptoStorage.4.challengename = Insecure Crypto Storage Challenge 4 + +insecureCryptoStorage.1.whatToDo = The result key has been encrypted to ensure that nobody can finish the challenge without knowing the secret key to decrypt it. However, the result key has been encrypted with a famous, but easily broken, Roman cipher. The Plain text is in English. + +insecureCryptoStorage.2.whatToDo = The result key has been encrypted to ensure that nobody can finish the challenge without knowing the secret key to decrypt it. The following form can be used to check if you have the correct result key. +insecureCryptoStorage.2.checkKey = Check Result Key +insecureCryptoStorage.2.hint = 2D Encryption +insecureCyrptoStorage.2.commentedCode.1 = Validate theKey: +insecureCyrptoStorage.2.commentedCode.2 = Transform input: +insecureCyrptoStorage.2.commentedCode.3 = Check result for validity +insecureCyrptoStorage.2.commentedCode.4 = Output the "output" variable to the HTML for viewing + +insecureCyrptoStorage.3.whatToDo = The result key to this level is the same as the encryption key used in the following sub application. Break the cipher and recover the encryption key! The result key is in all capital letters and is in English. +insecureCyrptoStorage.3.ciphertextToDecrypt = Cipher text to decrypt: +insecureCyrptoStorage.3.ciphertextExample = Cipher text Example +insecureCyrptoStorage.3.tryDecryptThis = Try to decrypt this: + +insecureCyrptoStorage.4.whatToDo = If you can buy trolls for free you'll receive the key for this level! +insecureCyrptoStorage.4.shop = Super Meme Shopping +insecureCyrptoStorage.4.shop.message.1 = Hey customers: Due to a shipping mistake we are completely over stocked in rage Memes. Use the coupon code +insecureCyrptoStorage.4.shop.message.2 = or +insecureCyrptoStorage.4.shop.message.3 = to get yours for free!!! +insecureCyrptoStorage.4.shop.picture = Picture +insecureCyrptoStorage.4.shop.cost = Cost +insecureCyrptoStorage.4.shop.quantity = Quantity +insecureCyrptoStorage.4.shop.howToShop = Please select how many things you would like to buy and click submit +insecureCyrptoStorage.4.shop.couponCode = Coupon Code: +insecureCyrptoStorage.4.shop.submit = Place Order + +insecureCyrptoStorage.loading = Loading... +insecureCyrptoStorage.errorOccurred = An Error Occurred +insecureCyrptoStorage.decrypt = Decrypt + diff --git a/SecurityShepherdCore/src/i18n/servlets/challenges/insecureCryptoStorage/insecureCryptoStorage.properties b/SecurityShepherdCore/src/i18n/servlets/challenges/insecureCryptoStorage/insecureCryptoStorage.properties new file mode 100644 index 000000000..3d7895c5d --- /dev/null +++ b/SecurityShepherdCore/src/i18n/servlets/challenges/insecureCryptoStorage/insecureCryptoStorage.properties @@ -0,0 +1,8 @@ +insecureCyrptoStorage.3.plaintextResult = Plain text Result: +insecureCyrptoStorage.3.plaintextResult.message = Your cipher text was decrypted to the following: + +insecureCyrptoStorage.4.orderComplete = Order Complete +insecureCyrptoStorage.4.orderShipped = Your order has been made and has been sent to our magic shipping department that knows where you want this to be delivered via brain wave sniffing techniques. +insecureCyrptoStorage.4.totalCost = Your order comes to a total of +insecureCyrptoStorage.4.freeTrolls = Trolls were free, Well Done +insecureCyrptoStorage.4.orderFailed = Order Failed - Please try again later \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp b/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp index bcac9fc38..4b4b3464e 100644 --- a/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1.jsp @@ -25,8 +25,15 @@ String levelName = "Insecure Cyrpto Challenge 3"; //Alphanumeric Only String levelHash = "2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1"; + +//Translation Stuff +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.insecureCryptoStorage.insecureCryptoStorage", locale); +//Used more than once translations +String i18nLevelName = bundle.getString("insecureCryptoStorage.3.challengename"); //Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = "The result key to this level is the same as the encryption key used in the following sub application. Break the cipher and recover the encryption key! The result key is in all caps."; +String levelBlurb = bundle.getString("insecureCyrptoStorage.3.whatToDo"); + //Logs the IP, Forwarded IP that acceeded this level with the level name in the debug for convience. If you want to log more stuff in the JSP use this as an example ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) @@ -52,35 +59,36 @@ if (request.getSession() != null) - Security Shepherd - <%= levelName %> + Security Shepherd - <%= i18nLevelName %>

    -

    <%= levelName %>

    +

    <%= i18nLevelName %>

    <%= levelBlurb %>

    - Cipher text to decrypt: + <%= bundle.getString("insecureCyrptoStorage.3.ciphertextToDecrypt") %>
    -
    - - + "/> +
    -

    Cipher text Example

    Try to decrypt this: IAAAAEkQBhEVBwpDHAFJGhYHSBYEGgocAw==

    +
    +

    <%= bundle.getString("insecureCyrptoStorage.3.ciphertextExample") %>

    +

    <%= bundle.getString("insecureCyrptoStorage.3.tryDecryptThis") %> IAAAAEkQBhEVBwpDHAFJGhYHSBYEGgocAw==

    +

    - <% /*If you need to call the Server Do it like this */ %>
    -

    <%= levelName %>

    +

    <%= i18nLevelName %>

    - If you can buy trolls for free you'll receive the key for this level! + <%= bundle.getString("insecureCyrptoStorage.4.whatToDo") %>

    -

    Super Meme Shopping

    - Hey customers: Due to a shipping mistake we are completely over stocked in rage Memes. - Use the coupon code PleaseTakeARage or RageMemeForFree to get yours for free!!!. +

    <%= bundle.getString("insecureCyrptoStorage.4.shop") %>

    +

    <%= bundle.getString("insecureCyrptoStorage.4.shop.message.1") %> PleaseTakeARage <%= bundle.getString("insecureCyrptoStorage.4.shop.message.2") %> RageMemeForFree <%= bundle.getString("insecureCyrptoStorage.4.shop.message.3") %>



    - - - + + + @@ -99,13 +104,13 @@ if (request.getSession() != null)
    PictureCostQuantity<%= bundle.getString("insecureCyrptoStorage.4.shop.picture") %><%= bundle.getString("insecureCyrptoStorage.4.shop.cost") %><%= bundle.getString("insecureCyrptoStorage.4.shop.quantity") %>
    - Please select how many things you would like to buy and click submit +

    <%= bundle.getString("insecureCyrptoStorage.4.shop.howToShop") %>

    - +
    Coupon Code:
    <%= bundle.getString("insecureCyrptoStorage.4.shop.couponCode") %>
    -
    - + "/> +
    @@ -141,7 +146,7 @@ if (request.getSession() != null) } else { - $("#resultsDiv").html("

    An Error Occurred: " + ajaxCall.status + " " + ajaxCall.statusText + "

    "); + $("#resultsDiv").html("

    <%= bundle.getString("insecureCyrptoStorage.errorOccurred") %>: " + ajaxCall.status + " " + ajaxCall.statusText + "

    "); } $("#resultsDiv").show("slow", function(){ $("#loadingSign").hide("fast", function(){ diff --git a/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp b/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp index bf8e5845f..53f0a222b 100644 --- a/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/h8aa0fdc145fb8089661997214cc0e685e5f86a87f30c2ca641e1dde15b01177.jsp @@ -1,7 +1,8 @@ <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage="" %> +<%@ page import="java.util.Locale, java.util.ResourceBundle"%> <% /** - * Insecure Cryptographic Storage Challenge 2 Accessed + * Insecure Cryptographic Storage Challenge 2 *

    * This file is part of the Security Shepherd Project. * @@ -22,6 +23,12 @@ */ String levelName = "Insecure Cryptographic Storage Challenge 2"; + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.insecureCryptoStorage.insecureCryptoStorage", locale); + //Used more than once translations + String i18nLevelName = bundle.getString("insecureCryptoStorage.2.challengename"); ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -46,23 +53,21 @@ String levelName = "Insecure Cryptographic Storage Challenge 2"; - Security Shepherd - Insecure Cryptographic Storage Challenge Two + Security Shepherd - <%= i18nLevelName %>
    -

    Insecure Cryptographic Storage Challenge Two

    +

    <%= i18nLevelName %>

    - The result key has been encrypted to ensure that nobody can finish the - challenge without knowing the secret key to decrypt it. The following form - can be used to check if you have the correct result key. + <%= bundle.getString("insecureCryptoStorage.2.whatToDo") %>

    - + "/>
    @@ -73,12 +78,12 @@ String levelName = "Insecure Cryptographic Storage Challenge 2";
    -

    Insecure Cryptographic Storage Challenge One

    +

    <%= i18nLevelName %>

    - The result key has been encrypted to ensure that nobody can finish the challenge without knowing the secret key to decrypt it. However, the result key has been encrypted with a famous, but easily broken, Roman cipher. + <%= bundle.getString("insecureCryptoStorage.1.whatToDo") %>

    Ymj wjxzqy pjd ktw ymnx qjxxts nx ymj ktqqtbnsl xywnsl; rdqtajqdmtwxjwzssnslymwtzlmymjknjqibmjwjfwjdtzltnslbnymdtzwgnlf diff --git a/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto3.java b/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto3.java index 6774738cd..2b084d3e1 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto3.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto3.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.io.PrintWriter; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -42,7 +44,7 @@ public class BrokenCrypto3 extends HttpServlet private static final long serialVersionUID = 1L; private static org.apache.log4j.Logger log = Logger.getLogger(BrokenCrypto3.class); private static String levelName = "Broken Crypto Challenge 3"; - private static String levelHash = "2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1"; + public static String levelHash = "2da053b4afb1530a500120a49a14d422ea56705a7e3fc405a77bc269948ccae1"; public static String levelResult = "thisisthesecurityshepherdabcencryptionkey"; //Is used as encryption key in this level public void doPost (HttpServletRequest request, HttpServletResponse response) @@ -53,13 +55,18 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) HttpSession ses = request.getSession(true); if(Validate.validateSession(ses)) - { + { ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); log.debug(levelName + " servlet accessed by: " + ses.getAttribute("userName").toString()); - + String htmlOutput = new String(); + PrintWriter out = response.getWriter(); out.print(getServletInfo()); - String htmlOutput = new String(); + + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle errors = ResourceBundle.getBundle("i18n.servlets.errors", locale); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.insecureCryptoStorage.insecureCryptoStorage", locale); try { String userData = request.getParameter("userData"); @@ -70,14 +77,14 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) String decryptedUserData = decrypt(userData, levelResult); log.debug("Decrypted to: " + decryptedUserData); Encoder encoder = ESAPI.encoder(); - htmlOutput = "

    Plain text Result:

    Your cipher text was decrypted to the following:

    " + htmlOutput = "

    " + bundle.getString("insecureCyrptoStorage.3.plaintextResult") + "

    " + bundle.getString("insecureCyrptoStorage.3.plaintextResult.message") + "

    " + encoder.encodeForHTML(decryptedUserData) + "

    "; } catch(Exception e) { - htmlOutput = "An Error Occurred! You must be getting funky!"; log.fatal(levelName + " - " + e.toString()); + htmlOutput = errors.getString("error.funky"); } out.write(htmlOutput); } diff --git a/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto4.java b/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto4.java index 27d2e610e..961b98aa7 100644 --- a/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto4.java +++ b/SecurityShepherdCore/src/servlets/module/challenge/BrokenCrypto4.java @@ -5,6 +5,8 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -13,8 +15,6 @@ import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Encoder; import utils.Hash; import utils.ShepherdLogManager; @@ -22,7 +22,7 @@ import dbProcs.Database; import dbProcs.Getter; /** - * Level : Broken Crypto NEW + * Level : Broken Crypto 4 *

    * * This file is part of the Security Shepherd Project. @@ -57,13 +57,16 @@ public void doPost (HttpServletRequest request, HttpServletResponse response) HttpSession ses = request.getSession(true); if(Validate.validateSession(ses)) { + //Translation Stuff + Locale locale = new Locale(Validate.validateLanguage(request.getSession())); + ResourceBundle bundle = ResourceBundle.getBundle("i18n.servlets.challenges.insecureCryptoStorage.insecureCryptoStorage", locale); + ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), ses.getAttribute("userName").toString()); log.debug(levelName + " servlet accessed by: " + ses.getAttribute("userName").toString()); PrintWriter out = response.getWriter(); out.print(getServletInfo()); String htmlOutput = new String(); String applicationRoot = getServletContext().getRealPath(""); - Encoder encoder = ESAPI.encoder(); try { //Get and validate cart amounts @@ -138,18 +141,18 @@ else if (coupons.getInt(1) == 4) // NotBad int finalCost = megustaCost + rageCost + notBadAmount + trollCost; //Output Order - htmlOutput = "

    Order Complete

    " - + "Your order has been made and has been sent to our magic shipping department that knows where you want this to be delivered via brain wave sniffing techniques.

    " - + "Your order comes to a total of $" + finalCost + ""; + htmlOutput = "

    " + bundle.getString("insecureCyrptoStorage.4.orderComplete") + "

    " + + "

    " + bundle.getString("insecureCyrptoStorage.4.orderShipped") + "

    " + + "

    " + bundle.getString("insecureCyrptoStorage.4.totalCost") + " $" + finalCost + "

    "; if (trollAmount > 0 && trollCost == 0) { - htmlOutput += "

    Trolls were free, Well Done - " + encoder.encodeForHTML(Hash.generateUserSolution(Getter.getModuleResultFromHash(getServletContext().getRealPath(""), levelHash), (String)ses.getAttribute("userName"))) + ""; + htmlOutput += "

    " + bundle.getString("insecureCyrptoStorage.4.freeTrolls") + " - " + Hash.generateUserSolution(Getter.getModuleResultFromHash(getServletContext().getRealPath(""), levelHash), (String)ses.getAttribute("userName")) + "

    "; } } catch(Exception e) { log.debug("Didn't complete order: " + e.toString()); - htmlOutput += "

    Order Failed - Please try again later

    "; + htmlOutput += "

    " + bundle.getString("insecureCyrptoStorage.4.orderFailed") + "

    "; } try { From a0ac27fd7ac0bcdc6ee7f865bd0cc195969c18c6 Mon Sep 17 00:00:00 2001 From: SeanDuggan Date: Sun, 4 Oct 2015 11:18:07 +0100 Subject: [PATCH 057/112] Translation Support for Reverse Engineering --- .../reverseEngineer.properties | 8 +++---- ...fd8fbb5469a60209b44fa3485c18794df4d5b1.jsp | 22 ++++++++++--------- ...d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp | 14 +++++++----- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties b/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties index d1bb70f89..ed16354dd 100644 --- a/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties +++ b/SecurityShepherdCore/src/i18n/challenges/mobile/reverseEngineer/reverseEngineer.properties @@ -1,8 +1,8 @@ challenge1.challengeName = Mobile Reverse Engineer 1 challenge1.para1 = This key to this challenge is the App author's name. Find it to complete the challenge. -challenge1.challengeName = Mobile Reverse Engineer 2 -challenge1.para1 = +challenge2.challengeName = Mobile Reverse Engineer 2 +challenge2.para1 = When an attacker reverse engineers an APK, they usually find multiple packages containing multiple activities, containing multiple methods which have all been obfuscated to crawl through. The key for this challenge has been hard coded into the APK for this challenge. To get to the key reverse engineer the APK and find the correct Activity which performs a conditional statement to check the validity of the key. -challenge1.challengeName = Mobile Reverse Engineer 3 -challenge1.para1 = \ No newline at end of file +challenge3.challengeName = Mobile Reverse Engineer 3 +challenge3.para1 = The developers of this App, ReverseEngineer3, have decided to use an algorithm to confirm or deny the authenticity of the key. You must Reverse Engineer ReverseEngineer3.APK to find this algorithm. If you are unsure whether or not the key is correct, there is a key validity checker in the App. \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp b/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp index 9539e317d..5d846a571 100644 --- a/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp @@ -25,11 +25,16 @@ String levelName = "Mobile Reverse Engineering 2"; //Alphanumeric Only String levelHash = "5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1"; + //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.reverseEngineer.reverseEngineer", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge2.challengeName"); +String paragraph1 = bundle.getString("challenge2.para1"); + //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); @@ -56,7 +61,7 @@ if (request.getSession() != null) -Security Shepherd - <%=levelName%> +Security Shepherd - <%=LevelName%> @@ -65,17 +70,14 @@ if (request.getSession() != null)
    -

    <%=levelName%>

    +

    <%=LevelName%>

    <%=levelBlurb%> -
    When an attacker reverse engineers an APK, they usually find - multiple packages containing multiple activities, - containing multiple methods which have all been obfuscated to - crawl through. The key for this challenge has been hard coded into the APK for this challenge. To get to - the key reverse engineer the APK and find the correct Activity which - performs a conditional statement to check the validity of the key.
    +
    + <%= paragraph1 %> +

    - <%= mobile.getString("mobileBlurb.vmLink.1") + " ReverseEngineer3.apk " + mobile.getString("mobileBlurb.vmLink.2") %> + <%= mobile.getString("mobileBlurb.vmLink.1") + " ReverseEngineer2.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> diff --git a/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp b/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp index c1729c4a3..58791b935 100644 --- a/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp @@ -27,9 +27,13 @@ String levelHash = "dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -//ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.example." + levelHash, locale); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.challenges.mobile.reverseEngineer.reverseEngineer", locale); ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String LevelName = bundle.getString("challenge3.challengeName"); +String paragraph1 = bundle.getString("challenge3.para1"); + //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); @@ -55,7 +59,7 @@ if (request.getSession() != null) -Security Shepherd - <%=levelName%> +Security Shepherd - <%=LevelName%> @@ -64,14 +68,14 @@ if (request.getSession() != null)
    -

    <%=levelName%>

    +

    <%=LevelName%>

    <%=levelBlurb%> -
    The developers of this App, ReverseEngineer4, have decided to use an algorithm to confirm or deny the authenticity of the key. You must Reverse Engineer ReverseEngineer3.APK to find this algorithm. If you are unsure whether or not the key is correct, there is a key validity checker in the App.
    +
    <%= paragraph1 %>


    - <%= mobile.getString("mobileBlurb.vmLink.1") + " ReverseEngineer4.apk " + mobile.getString("mobileBlurb.vmLink.2") %> + <%= mobile.getString("mobileBlurb.vmLink.1") + " ReverseEngineer3.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    <% if (Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> From a1fe5b0bcb23ff2012ad28a436c4ca079a8c4386 Mon Sep 17 00:00:00 2001 From: SeanDuggan Date: Sun, 4 Oct 2015 22:16:29 +0100 Subject: [PATCH 058/112] Created challenge App for Content Provider Leakage Created layout for Poor Auth 2 --- .../app/src/main/res/layout/activity_main.xml | 1 + MobileShepherd/CProviderLeakage1/.gitignore | 7 + .../CProviderLeakage1/app/.gitignore | 1 + .../CProviderLeakage1/app/build.gradle | 25 +++ .../CProviderLeakage1/app/proguard-rules.pro | 17 ++ .../app/src/main/AndroidManifest.xml | 29 +++ .../java/com/app/module/MainActivity.java | 38 ++++ .../main/java/com/app/module/mProvider.java | 191 ++++++++++++++++++ .../app/src/main/res/layout/activity_main.xml | 32 +++ .../app/src/main/res/menu/menu_main.xml | 6 + .../src/main/res/mipmap-hdpi/background.jpg | Bin 0 -> 38902 bytes .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 11141 bytes .../src/main/res/mipmap-mdpi/background.jpg | Bin 0 -> 38902 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 11141 bytes .../src/main/res/mipmap-xhdpi/background.jpg | Bin 0 -> 38902 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 11141 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 11141 bytes .../app/src/main/res/values-w820dp/dimens.xml | 6 + .../app/src/main/res/values/dimens.xml | 5 + .../app/src/main/res/values/strings.xml | 5 + .../app/src/main/res/values/styles.xml | 8 + .../app/src/main/res/xml/box.xml | 30 +++ .../app/src/main/res/xml/button.xml | 37 ++++ .../app/src/main/res/xml/edittext.xml | 27 +++ MobileShepherd/CProviderLeakage1/build.gradle | 19 ++ .../CProviderLeakage1/gradle.properties | 18 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + MobileShepherd/CProviderLeakage1/gradlew | 164 +++++++++++++++ MobileShepherd/CProviderLeakage1/gradlew.bat | 90 +++++++++ .../CProviderLeakage1/settings.gradle | 1 + .../app/src/main/res/layout/activity_main.xml | 87 +++++++- .../src/main/res/mipmap-hdpi/background.jpg | Bin 0 -> 38902 bytes .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3418 -> 11141 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2206 -> 11141 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4842 -> 11141 bytes .../src/main/res/mipmap-xxhdpi/background.jpg | Bin 0 -> 38902 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7718 -> 11141 bytes .../app/src/main/res/xml/box.xml | 30 +++ .../app/src/main/res/xml/button.xml | 37 ++++ .../app/src/main/res/xml/edittext.xml | 27 +++ ...e397b80291d99307cfad69662277d39.properties | 1 + 42 files changed, 936 insertions(+), 9 deletions(-) create mode 100644 MobileShepherd/CProviderLeakage1/.gitignore create mode 100644 MobileShepherd/CProviderLeakage1/app/.gitignore create mode 100644 MobileShepherd/CProviderLeakage1/app/build.gradle create mode 100644 MobileShepherd/CProviderLeakage1/app/proguard-rules.pro create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/AndroidManifest.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/MainActivity.java create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/mProvider.java create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/layout/activity_main.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/menu/menu_main.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/mipmap-hdpi/background.jpg create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/mipmap-mdpi/background.jpg create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/mipmap-xhdpi/background.jpg create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/values-w820dp/dimens.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/values/dimens.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/values/strings.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/values/styles.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/xml/box.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/xml/button.xml create mode 100644 MobileShepherd/CProviderLeakage1/app/src/main/res/xml/edittext.xml create mode 100644 MobileShepherd/CProviderLeakage1/build.gradle create mode 100644 MobileShepherd/CProviderLeakage1/gradle.properties create mode 100644 MobileShepherd/CProviderLeakage1/gradle/wrapper/gradle-wrapper.jar create mode 100644 MobileShepherd/CProviderLeakage1/gradle/wrapper/gradle-wrapper.properties create mode 100644 MobileShepherd/CProviderLeakage1/gradlew create mode 100644 MobileShepherd/CProviderLeakage1/gradlew.bat create mode 100644 MobileShepherd/CProviderLeakage1/settings.gradle create mode 100644 MobileShepherd/PoorAuthentication2/app/src/main/res/mipmap-hdpi/background.jpg create mode 100644 MobileShepherd/PoorAuthentication2/app/src/main/res/mipmap-xxhdpi/background.jpg create mode 100644 MobileShepherd/PoorAuthentication2/app/src/main/res/xml/box.xml create mode 100644 MobileShepherd/PoorAuthentication2/app/src/main/res/xml/button.xml create mode 100644 MobileShepherd/PoorAuthentication2/app/src/main/res/xml/edittext.xml diff --git a/MobileShepherd/CProviderLeakage/app/src/main/res/layout/activity_main.xml b/MobileShepherd/CProviderLeakage/app/src/main/res/layout/activity_main.xml index 635201149..e29d5505e 100644 --- a/MobileShepherd/CProviderLeakage/app/src/main/res/layout/activity_main.xml +++ b/MobileShepherd/CProviderLeakage/app/src/main/res/layout/activity_main.xml @@ -11,6 +11,7 @@ android:layout_height="wrap_content" android:id="@+id/keyEditText" android:textColorHint="#FFFFFF" + android:textColor='#FFFFFF' android:background="@xml/edittext" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" diff --git a/MobileShepherd/CProviderLeakage1/.gitignore b/MobileShepherd/CProviderLeakage1/.gitignore new file mode 100644 index 000000000..ec870c9fd --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/.gitignore @@ -0,0 +1,7 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/MobileShepherd/CProviderLeakage1/app/.gitignore b/MobileShepherd/CProviderLeakage1/app/.gitignore new file mode 100644 index 000000000..3543521e9 --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/MobileShepherd/CProviderLeakage1/app/build.gradle b/MobileShepherd/CProviderLeakage1/app/build.gradle new file mode 100644 index 000000000..3e1e7ae75 --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/app/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "com.somewhere.hidden" + minSdkVersion 15 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:22.2.0' +} diff --git a/MobileShepherd/CProviderLeakage1/app/proguard-rules.pro b/MobileShepherd/CProviderLeakage1/app/proguard-rules.pro new file mode 100644 index 000000000..c1ae58556 --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\sean\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/MobileShepherd/CProviderLeakage1/app/src/main/AndroidManifest.xml b/MobileShepherd/CProviderLeakage1/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..6f549d7f8 --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + diff --git a/MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/MainActivity.java b/MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/MainActivity.java new file mode 100644 index 000000000..066b02b24 --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/MainActivity.java @@ -0,0 +1,38 @@ +package com.app.module; + +import android.content.ContentValues; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; + + +public class MainActivity extends AppCompatActivity { + + EditText keyEditText; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + keyEditText = (EditText) findViewById(R.id.keyEditText); + } + + public void addKey(View view) { + + String key = keyEditText.getText().toString(); + + ContentValues values = new ContentValues(); + values.put(mProvider.key, key); + + // Provides access to other applications Content Providers + Uri uri = getContentResolver().insert(mProvider.CONTENT_URL, values); + + Toast.makeText(getBaseContext(), "New Secret Added", Toast.LENGTH_LONG) + .show(); + } + +} diff --git a/MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/mProvider.java b/MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/mProvider.java new file mode 100644 index 000000000..3eb58fb44 --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/app/src/main/java/com/app/module/mProvider.java @@ -0,0 +1,191 @@ +package com.app.module; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.widget.Toast; + +import java.util.HashMap; + + +public class mProvider extends ContentProvider { + + + static final String PROVIDER_NAME = "com.app.module.mProvider"; + + static final String URL = "content://" + PROVIDER_NAME + "/data"; + static final Uri CONTENT_URL = Uri.parse(URL); + + static final String id = "id"; + static final String key = "key"; + static final int uriCode = 1; + + private static HashMap values; + + // Used to match uris with Content Providers + static final UriMatcher uriMatcher; + + private SQLiteDatabase sqlDB; + static final String DATABASE_NAME = "hiddenData"; + static final String TABLE_NAME = "keys"; + static final int DATABASE_VERSION = 1; + static final String CREATE_DB_TABLE = " CREATE TABLE " + TABLE_NAME + + " (id INTEGER PRIMARY KEY AUTOINCREMENT, " + + " key TEXT NOT NULL);"; + + + static { + uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + uriMatcher.addURI(PROVIDER_NAME, "data", uriCode); + } + + @Override + public boolean onCreate() { + DatabaseHelper dbHelper = new DatabaseHelper(getContext()); + sqlDB = dbHelper.getWritableDatabase(); + if (sqlDB != null) { + return true; + } + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + // Used to create a SQL query + SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); + + // Set table to query + queryBuilder.setTables(TABLE_NAME); + + // Used to match uris with Content Providers + switch (uriMatcher.match(uri)) { + case uriCode: + + // A projection map maps from passed column names to database column names + queryBuilder.setProjectionMap(values); + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + // Cursor provides read and write access to the database + Cursor cursor = queryBuilder.query(sqlDB, projection, selection, selectionArgs, null, + null, sortOrder); + + // Register to watch for URI changes + cursor.setNotificationUri(getContext().getContentResolver(), uri); + return cursor; + } + + // Handles requests for the MIME type (Type of Data) of the data at the URI + @Override + public String getType(Uri uri) { + + // Used to match uris with Content Providers + switch (uriMatcher.match(uri)) { + + case uriCode: + return "vnd.android.cursor.dir/data"; + + default: + throw new IllegalArgumentException("Unsupported URI: " + uri); + } + } + + // Used to insert a new row into the provider + // Receives the URI (Uniform Resource Identifier) for the Content Provider and a set of values + @Override + public Uri insert(Uri uri, ContentValues values) { + + // Gets the row id after inserting a map with the keys representing the the column + // names and their values. The second attribute is used when you try to insert + // an empty row + long rowID = sqlDB.insert(TABLE_NAME, null, values); + + // Verify a row has been added + if (rowID > 0) { + + // Append the given id to the path and return a Builder used to manipulate URI + // references + Uri _uri = ContentUris.withAppendedId(CONTENT_URL, rowID); + + // getContentResolver provides access to the content model + // notifyChange notifies all observers that a row was updated + getContext().getContentResolver().notifyChange(_uri, null); + + // Return the Builder used to manipulate the URI + return _uri; + } + Toast.makeText(getContext(), "Row Insert Failed", Toast.LENGTH_LONG).show(); + return null; + } + + // Deletes a row or a selection of rows + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + int rowsDeleted = 0; + + // Used to match uris with Content Providers + switch (uriMatcher.match(uri)) { + case uriCode: + rowsDeleted = sqlDB.delete(TABLE_NAME, selection, selectionArgs); + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + // getContentResolver provides access to the content model + // notifyChange notifies all observers that a row was updated + getContext().getContentResolver().notifyChange(uri, null); + return rowsDeleted; + } + + // Used to update a row or a selection of rows + // Returns to number of rows updated + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + int rowsUpdated = 0; + + // Used to match uris with Content Providers + switch (uriMatcher.match(uri)) { + case uriCode: + + // Update the row or rows of data + rowsUpdated = sqlDB.update(TABLE_NAME, values, selection, selectionArgs); + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + // getContentResolver provides access to the content model + // notifyChange notifies all observers that a row was updated + getContext().getContentResolver().notifyChange(uri, null); + return rowsUpdated; + } + + private static class DatabaseHelper extends SQLiteOpenHelper { + DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase sqlDB) { + sqlDB.execSQL(CREATE_DB_TABLE); + } + + // Recreates the table when the database needs to be upgraded + @Override + public void onUpgrade(SQLiteDatabase sqlDB, int oldVersion, int newVersion) { + sqlDB.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); + onCreate(sqlDB); + } + + } +} diff --git a/MobileShepherd/CProviderLeakage1/app/src/main/res/layout/activity_main.xml b/MobileShepherd/CProviderLeakage1/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..db483408e --- /dev/null +++ b/MobileShepherd/CProviderLeakage1/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,32 @@ + + + + + " From 82f56b4d755cce78eaf40b2f350ba5e4ca442da5 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Fri, 9 Oct 2015 19:57:34 +0100 Subject: [PATCH 075/112] Fixing Various JSP coding discrepancies --- ...a55050e3e1cfbbbe1d597b0537513ac8665295.jsp | 3 - ...5e0ed26b11af978523e34528cf0bb9d32de851.jsp | 4 - ...fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp | 1 - ...af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp | 1 - ...bee31e9291aaa5367594c29b3af542a7572c01.jsp | 5 - ...40b48f53aad7b41390fe46c6f324fee748d136.jsp | 18 +--- ...fd8fbb5469a60209b44fa3485c18794df4d5b1.jsp | 12 +-- ...200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp | 31 +++--- ...a634194bf5da440282227c15954bbdfb54f0c7.jsp | 8 -- ...da550520e29f7a82400a317c579eb3a5a0a8ef.jsp | 10 +- ...6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp | 3 - ...0dc2e875344035e38608fcfb7c1ab8924923f6.jsp | 3 +- ...0cc18533a88b2363054a1f391fe855954d12f9.jsp | 1 - ...f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp | 1 - ...9322dd2750f633246842280dc68c858d208425.jsp | 9 -- ...21aae15d28c36c7981222eb59f7fc8d8f212a2.jsp | 1 - ...590b499ceca941ab848805c00f5bf0a40c9717.jsp | 10 +- ...d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp | 8 +- ...769967bdc75740ad2363803168b7907c794cd4.jsp | 4 - ...bb18ac934667971fa275bd7d234589bd8a8467.jsp | 4 - ...90791bb312a3044b3110acb8a65d165376bf34.jsp | 8 -- ...befff7427f6610ed626dfd43abf35295f106bc.jsp | 9 -- ...0adb59928158da5994a39f584cb799f25a95b9.jsp | 12 +-- ...b94f70afe9cca3f78c1e4766fee1cc08c035ec.jsp | 8 +- ...5481c4ce397b80291d99307cfad69662277d39.jsp | 12 +-- ...d34e8d6bd8b037f05341e64e94f5411c10ac8e.jsp | 12 +-- ...42c4157e29b9bcc44e8a827be3bb7e58c9a212.jsp | 8 +- ...897149819a159a8114d4867c7f8f327f5453a8.jsp | 94 ------------------- ...576e1dc140393185afca8975fbd6822ebf392f.jsp | 12 +-- ...6016a0a7f6879283c873d9476a8e7e94ea736f.jsp | 14 +-- ...a7c0f68abc6ef2ab747ea87e0892767152eae1.jsp | 13 +-- 31 files changed, 57 insertions(+), 282 deletions(-) delete mode 100644 SecurityShepherdCore/src/jsp/lessons/bf16081ed057b2d1bc97f4b9da897149819a159a8114d4867c7f8f327f5453a8.jsp diff --git a/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp b/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp index 5266a1884..21dabb333 100644 --- a/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp @@ -24,7 +24,6 @@ String levelName = "Mobile Reverse Engineer 1"; //Alphanumeric Only String levelHash = "072a9e4fc888562563adf8a89fa55050e3e1cfbbbe1d597b0537513ac8665295.jsp"; -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); @@ -61,8 +60,6 @@ if (request.getSession() != null) Security Shepherd - <%= i18nLevelName %> - - diff --git a/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp b/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp index 8a35ae965..0520f06a9 100644 --- a/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851.jsp @@ -24,8 +24,6 @@ String levelName = "Mobile Insecure Data Storage 3"; //Alphanumeric Only String levelHash = "11ccaf2f3b2aa4f88265b9cacb5e0ed26b11af978523e34528cf0bb9d32de851"; -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); @@ -61,8 +59,6 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - - diff --git a/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp b/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp index a3a2c52d2..1d8a85e33 100644 --- a/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/1f0935baec6ba69d79cfb2eba5fdfa6ac5d77fadee08585eb98b130ec524d00c.jsp @@ -66,7 +66,6 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - diff --git a/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp b/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp index 839b82fa5..7924f1008 100644 --- a/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/20e8c4bb50180fed9c1c8d1bf6af5eac154e97d3ce97e43257c76e73e3bbe5d5.jsp @@ -57,7 +57,6 @@ if (request.getSession() != null) Security Shepherd - <%= i18nLevelName %> - diff --git a/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp b/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp index 6aa0f2f91..8977299ad 100644 --- a/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/362f84cf26bf96aeae358d5d0bbee31e9291aaa5367594c29b3af542a7572c01.jsp @@ -34,9 +34,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge1.challengeName"); String paragraph1 = bundle.getString("challenge1.para1"); - -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -63,8 +60,6 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - - diff --git a/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp b/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp index b743667e0..467f8d977 100644 --- a/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/517622a535ff89f7d90674862740b48f53aad7b41390fe46c6f324fee748d136.jsp @@ -35,10 +35,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge1.challengeName"); String paragraph1 = bundle.getString("challenge1.para1"); - -//Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; - ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -62,26 +58,18 @@ if (request.getSession() != null) %> - -Security Shepherd - <%=LevelName%> - + + Security Shepherd - <%=LevelName%> + -

    <%=LevelName%>

    - <% - /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ - %> - - <%=levelBlurb%> -
    <%= paragraph1 %>

    - <%=mobile.getString("mobileBlurb.vmLink.1") + " UDataLeakage1.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp b/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp index 5acf14fde..4b12ca264 100644 --- a/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/5bc811f9e744a71393a277c51bfd8fbb5469a60209b44fa3485c18794df4d5b1.jsp @@ -35,8 +35,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge2.challengeName"); String paragraph1 = bundle.getString("challenge2.para1"); -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -60,20 +58,16 @@ if (request.getSession() != null) %> - -Security Shepherd - <%=LevelName%> - + + Security Shepherd - <%=LevelName%> + - -

    <%=LevelName%>

    - <%=levelBlurb%> -
    <%= paragraph1 %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp b/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp index 9cc77c586..b6aa3a2fd 100644 --- a/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf.jsp @@ -24,8 +24,12 @@ String levelName = "Mobile Unintended Data Leakage 1"; //Alphanumeric Only String levelHash = "83dee43e50f65876d9c24a9355200f7c10569dc94e51349f7b857fb68b4e6bdf"; -//Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; +//Translation Stuff +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.mobile.unintendedDataLeakage.dataLeakage" + levelHash, locale); +ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); +//Used more than once translations +String translatedLevelName = bundle.getString("challenge1.challengeName"); ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) @@ -50,27 +54,20 @@ if (request.getSession() != null) %> - -Security Shepherd - <%=levelName%> - + + Security Shepherd - <%=translatedLevelName%> + -

    -

    <%=levelName%>

    +

    <%=translatedLevelName%>

    - <% - /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ - %> - - <%=levelBlurb%> -
    Log in as the user of this App to get the key for this - challenge. Some data has been logged but it is up to the - attacker to know what to do with this data.

    - - <%=Analytics.getMobileLevelBlurb("UDataLeakage2.apk")%> + <%= bundle.getString("challenge1.para1") %> +
    +
    + <%= mobile.getString("mobileBlurb.vmLink.1") + " UDataLeakage2.apk " + mobile.getString("mobileBlurb.vmLink.2") %>

    <% diff --git a/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp b/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp index 118d09226..f7562e059 100644 --- a/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/85ceae7ec397c8f4448be51c33a634194bf5da440282227c15954bbdfb54f0c7.jsp @@ -35,8 +35,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge2.challengeName"); String paragraph1 = bundle.getString("challenge2.para1"); -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -63,18 +61,12 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - -

    <%= LevelName %>

    - <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> - - <%= levelBlurb %> -
    <%= paragraph1 %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp b/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp index 699bd98a4..14bd3faa2 100644 --- a/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/8855c8bb9df4446a546414562eda550520e29f7a82400a317c579eb3a5a0a8ef.jsp @@ -35,9 +35,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge1.challengeName"); String paragraph1 = bundle.getString("challenge1.para1"); - -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -64,17 +61,12 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - -

    <%= LevelName %>

    -

    - <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> - -
    +

    <%= paragraph1 %>
    diff --git a/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp b/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp index 3b43ac2e7..1393608d9 100644 --- a/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62.jsp @@ -24,8 +24,6 @@ String levelName = "SQL Injection Challenge 5"; //Alphanumeric Only String levelHash = "8edf0a8ed891e6fef1b650935a6c46b03379a0eebab36afcd1d9076f65d4ce62"; -//Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = "Not used - See Below"; //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); @@ -59,7 +57,6 @@ if (request.getSession() != null) Security Shepherd - <%= i18nLevelName %> - diff --git a/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp b/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp index 71251f0e5..1db6eba18 100644 --- a/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/b7327828a90da59df54b27499c0dc2e875344035e38608fcfb7c1ab8924923f6.jsp @@ -56,8 +56,7 @@ if (request.getSession() != null) - Security Shepherd - <%= i18nLevelName %> - + Security Shepherd - <%= i18nLevelName %> diff --git a/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp b/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp index ca3449183..e0adcc046 100644 --- a/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/b927fc4d8c9f70a78f8b6fc46a0cc18533a88b2363054a1f391fe855954d12f9.jsp @@ -58,7 +58,6 @@ if (request.getSession() != null) Security Shepherd - <%= i18nLevelName %> - diff --git a/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp b/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp index d75189199..0f347ed58 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ca0e89caf3c50dbf9239a0b3c6f6c17869b2a1e2edc3aa6f029fd30925d66c7e.jsp @@ -57,7 +57,6 @@ if (request.getSession() != null) Security Shepherd - <%= i18nLevelName %> - diff --git a/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp b/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp index a398af27a..858e69bf3 100644 --- a/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/cfe68711def42bb0b201467b859322dd2750f633246842280dc68c858d208425.jsp @@ -34,9 +34,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge2.challengeName"); String paragraph1 = bundle.getString("challenge2.para1"); - -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -63,18 +60,12 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - -

    <%= LevelName %>

    - <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> - - <%= levelBlurb %> -
    <%= paragraph1 %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp b/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp index 244d73a9c..0657dd057 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d0e12e91dafdba4825b261ad5221aae15d28c36c7981222eb59f7fc8d8f212a2.jsp @@ -63,7 +63,6 @@ if (request.getSession() != null) Security Shepherd - <%= i18nLevelName %> - diff --git a/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp b/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp index c76a0c11a..8e8ea6795 100644 --- a/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/d2f8519f8264f9479f56165465590b499ceca941ab848805c00f5bf0a40c9717.jsp @@ -36,8 +36,6 @@ String LevelName = bundle.getString("challenge1.challengeName"); String paragraph1 = bundle.getString("challenge1.para1"); -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -64,18 +62,12 @@ if (request.getSession() != null) Security Shepherd - <%= LevelName %> - -

    <%= LevelName %>

    -

    - <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> - - <%= levelBlurb %> -
    +

    <%= paragraph1 %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp b/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp index 25f126cae..409ff61ba 100644 --- a/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/dbae0baa3f71f196c4d2c6c984d45a6c1c635bf1b482dccfe32e9b01b69a042b.jsp @@ -58,12 +58,10 @@ if (request.getSession() != null) %> - -Security Shepherd - <%=LevelName%> - + + Security Shepherd - <%=LevelName%> + - - diff --git a/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp b/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp index 2efcff7cf..6fb22108e 100644 --- a/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4.jsp @@ -24,8 +24,6 @@ String levelName = "Mobile Insecure Data Storage 2"; //Alphanumeric Only String levelHash = "ec09515a304d2de1f552e961ab769967bdc75740ad2363803168b7907c794cd4"; -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); @@ -62,8 +60,6 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - - diff --git a/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp b/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp index 673ed13ad..55c91d129 100644 --- a/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467.jsp @@ -24,8 +24,6 @@ String levelName = "Mobile Poor Authentication 1"; //Alphanumeric Only String levelHash = "efa08298fc6a4add4b9a4bbdbbbb18ac934667971fa275bd7d234589bd8a8467"; -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); @@ -62,8 +60,6 @@ if (request.getSession() != null) Security Shepherd - <%= levelName %> - - diff --git a/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp b/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp index 3d97ee54d..fa5f5b5d4 100644 --- a/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/f5a3f19dd44b53c6d29dda65fa90791bb312a3044b3110acb8a65d165376bf34.jsp @@ -34,8 +34,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge3.challengeName"); String paragraph1 = bundle.getString("challenge3.para1"); -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -62,18 +60,12 @@ if (request.getSession() != null) Security Shepherd - <%= LevelName %> - -

    <%= LevelName %>

    - <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> - - <%= levelBlurb %> -
    <%= paragraph1 %>

    diff --git a/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp b/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp index a25ef620d..9c16bfc8b 100644 --- a/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp +++ b/SecurityShepherdCore/src/jsp/challenges/fb5c9ce0f5539b737e534fd317befff7427f6610ed626dfd43abf35295f106bc.jsp @@ -34,9 +34,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGene String LevelName = bundle.getString("challenge2.challengeName"); String paragraph1 = bundle.getString("challenge2.para1"); - -//Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -63,18 +60,12 @@ if (request.getSession() != null) Security Shepherd - <%= LevelName %> - -

    <%= LevelName %>

    - <% /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ %> - - <%= levelBlurb %> -
    <%= paragraph1 %>

    diff --git a/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp b/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp index 3bedb4952..45b6c123f 100644 --- a/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/19753b944b63232812b7af1a0e0adb59928158da5994a39f584cb799f25a95b9.jsp @@ -31,9 +31,7 @@ * * @author Sean Duggan */ - //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you - String levelBlurb = ""; - + ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -62,12 +60,10 @@ %> - -Security Shepherd - <%=translatedLevelName%> - + + Security Shepherd - <%=translatedLevelName%> + - - diff --git a/SecurityShepherdCore/src/jsp/lessons/392c20397c535845d93c32fd99b94f70afe9cca3f78c1e4766fee1cc08c035ec.jsp b/SecurityShepherdCore/src/jsp/lessons/392c20397c535845d93c32fd99b94f70afe9cca3f78c1e4766fee1cc08c035ec.jsp index e9b013a58..8c936e7fe 100644 --- a/SecurityShepherdCore/src/jsp/lessons/392c20397c535845d93c32fd99b94f70afe9cca3f78c1e4766fee1cc08c035ec.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/392c20397c535845d93c32fd99b94f70afe9cca3f78c1e4766fee1cc08c035ec.jsp @@ -56,12 +56,10 @@ if (request.getSession() != null) %> - -Security Shepherd - <%= translatedLevelName %> - + + Security Shepherd - <%= translatedLevelName %> + - - diff --git a/SecurityShepherdCore/src/jsp/lessons/4d41997b5b81c88f7eb761c1975481c4ce397b80291d99307cfad69662277d39.jsp b/SecurityShepherdCore/src/jsp/lessons/4d41997b5b81c88f7eb761c1975481c4ce397b80291d99307cfad69662277d39.jsp index e5a5b98fc..3311eefdc 100644 --- a/SecurityShepherdCore/src/jsp/lessons/4d41997b5b81c88f7eb761c1975481c4ce397b80291d99307cfad69662277d39.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/4d41997b5b81c88f7eb761c1975481c4ce397b80291d99307cfad69662277d39.jsp @@ -7,8 +7,6 @@ String levelName = "Content Provider Leakage Lesson"; //Alphanumeric Only String levelHash = "4d41997b5b81c88f7eb761c1975481c4ce397b80291d99307cfad69662277d39"; - //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you - String levelBlurb = ""; //Translation Stuff Locale locale = new Locale(Validate.validateLanguage(request.getSession())); @@ -59,12 +57,10 @@ %> - -Security Shepherd - <%= translatedLevelName %> - - - - + + Security Shepherd - <%= translatedLevelName %> + + diff --git a/SecurityShepherdCore/src/jsp/lessons/77777b312d5b56a17c1f30550dd34e8d6bd8b037f05341e64e94f5411c10ac8e.jsp b/SecurityShepherdCore/src/jsp/lessons/77777b312d5b56a17c1f30550dd34e8d6bd8b037f05341e64e94f5411c10ac8e.jsp index 0a772e9d1..b0c6b5d8d 100644 --- a/SecurityShepherdCore/src/jsp/lessons/77777b312d5b56a17c1f30550dd34e8d6bd8b037f05341e64e94f5411c10ac8e.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/77777b312d5b56a17c1f30550dd34e8d6bd8b037f05341e64e94f5411c10ac8e.jsp @@ -56,21 +56,15 @@ if (request.getSession() != null) %> - -Security Shepherd - <%= translatedLevelName %> - + + Security Shepherd - <%= translatedLevelName %> + - -

    - <% - /* Put Your Blurb Here Instead of the following scriptlet. Not this comment Bren. Jeesh*/ - %> -

    <%= translatedLevelName %>

    diff --git a/SecurityShepherdCore/src/jsp/lessons/911fa7f4232e096d6a74a0623842c4157e29b9bcc44e8a827be3bb7e58c9a212.jsp b/SecurityShepherdCore/src/jsp/lessons/911fa7f4232e096d6a74a0623842c4157e29b9bcc44e8a827be3bb7e58c9a212.jsp index 642a33531..7be8c959b 100644 --- a/SecurityShepherdCore/src/jsp/lessons/911fa7f4232e096d6a74a0623842c4157e29b9bcc44e8a827be3bb7e58c9a212.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/911fa7f4232e096d6a74a0623842c4157e29b9bcc44e8a827be3bb7e58c9a212.jsp @@ -56,12 +56,10 @@ %> - -Security Shepherd - <%= translatedLevelName %> - + + Security Shepherd - <%= translatedLevelName %> + - - diff --git a/SecurityShepherdCore/src/jsp/lessons/bf16081ed057b2d1bc97f4b9da897149819a159a8114d4867c7f8f327f5453a8.jsp b/SecurityShepherdCore/src/jsp/lessons/bf16081ed057b2d1bc97f4b9da897149819a159a8114d4867c7f8f327f5453a8.jsp deleted file mode 100644 index 43cae10df..000000000 --- a/SecurityShepherdCore/src/jsp/lessons/bf16081ed057b2d1bc97f4b9da897149819a159a8114d4867c7f8f327f5453a8.jsp +++ /dev/null @@ -1,94 +0,0 @@ -<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" import="utils.*" errorPage=""%> -<%@ page import="java.util.Locale, java.util.ResourceBundle"%> - -<% -//No Quotes In level Name -String levelName = "What is Mobile Unintended Data Leakage?"; -//Alphanumeric Only -String levelHash = "bf16081ed057b2d1bc97f4b9da897149819a159a8114d4867c7f8f327f5453a8"; - -//Translation Stuff -Locale locale = new Locale(Validate.validateLanguage(request.getSession())); -ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.m_uninteded_data_leak." + levelHash, locale); -//Used more than once translations -String translatedLevelName = bundle.getString("title.question.m_uninteded_data_leakage"); - -/** - *

    - * This file is part of the Security Shepherd Project. - * - * The Security Shepherd project is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version.
    - * - * The Security Shepherd project is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details.
    - * - * You should have received a copy of the GNU General Public License - * along with the Security Shepherd project. If not, see . - * - * @author Sean Duggan - */ - -//Level blurb can be writen here in HTML OR go into the HTML body and write it there. Nobody will update this but you -String levelBlurb = ""; - -ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); -if (request.getSession() != null) -{ - HttpSession ses = request.getSession(); - //Getting CSRF Token from client - Cookie tokenCookie = null; - try - { - tokenCookie = Validate.getToken(request.getCookies()); - } - catch(Exception htmlE) - { - ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName +".jsp: tokenCookie Error:" + htmlE.toString()); - } - // validateSession ensures a valid session, and valid role credentials - // If tokenCookie == null, then the page is not going to continue loading - if (Validate.validateSession(ses) && tokenCookie != null) - { - ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " has been accessed by " + ses.getAttribute("userName").toString(), ses.getAttribute("userName")); - -%> - - - - Security Shepherd - <%= translatedLevelName %> - - - - - - - -
    -

    <%= translatedLevelName %>

    -

    - The App for this Challenge was rushed to completion, as a result some features which should not have made it to the final version were included. The result key can be found in App logs only intended for debugging. Submit it to complete this challenge. -
    -
    - <%= Analytics.getMobileLevelBlurb("UDataLeakage1.apk") %> -

    -
    - <% if(Analytics.googleAnalyticsOn) { %><%= Analytics.googleAnalyticsScript %><% } %> - - -<% - } - else - { - response.sendRedirect("../loggedOutSheep.html"); - } -} -else -{ - response.sendRedirect("../loggedOutSheep.html"); -} -%> \ No newline at end of file diff --git a/SecurityShepherdCore/src/jsp/lessons/ecfad0a5d41f59e6bed7325f56576e1dc140393185afca8975fbd6822ebf392f.jsp b/SecurityShepherdCore/src/jsp/lessons/ecfad0a5d41f59e6bed7325f56576e1dc140393185afca8975fbd6822ebf392f.jsp index 60bd4574b..8d7a6c6fa 100644 --- a/SecurityShepherdCore/src/jsp/lessons/ecfad0a5d41f59e6bed7325f56576e1dc140393185afca8975fbd6822ebf392f.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/ecfad0a5d41f59e6bed7325f56576e1dc140393185afca8975fbd6822ebf392f.jsp @@ -32,10 +32,6 @@ * * @author Sean Duggan */ - - //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you - String levelBlurb = ""; - ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) { @@ -58,12 +54,10 @@ %> - -Security Shepherd - <%= translatedLevelName %> - + + Security Shepherd - <%= translatedLevelName %> + - - diff --git a/SecurityShepherdCore/src/jsp/lessons/f15f2766c971e16e68aa26043e6016a0a7f6879283c873d9476a8e7e94ea736f.jsp b/SecurityShepherdCore/src/jsp/lessons/f15f2766c971e16e68aa26043e6016a0a7f6879283c873d9476a8e7e94ea736f.jsp index 5850e4a54..4f771de90 100644 --- a/SecurityShepherdCore/src/jsp/lessons/f15f2766c971e16e68aa26043e6016a0a7f6879283c873d9476a8e7e94ea736f.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/f15f2766c971e16e68aa26043e6016a0a7f6879283c873d9476a8e7e94ea736f.jsp @@ -6,10 +6,10 @@ String levelName = new String("Unvalidated Redirects and Forwards Lesson"); String levelHash = new String("f15f2766c971e16e68aa26043e6016a0a7f6879283c873d9476a8e7e94ea736f"); //Translation Stuff - Locale locale = new Locale(Validate.validateLanguage(request.getSession())); - ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.unvalidated_redirects_forwards." + levelHash, locale); - //Used more than once translations - String translatedLevelName = bundle.getString("title.question.unvalidated_redirects_forwards"); +Locale locale = new Locale(Validate.validateLanguage(request.getSession())); +ResourceBundle bundle = ResourceBundle.getBundle("i18n.lessons.unvalidated_redirects_forwards." + levelHash, locale); +//Used more than once translations +String translatedLevelName = bundle.getString("title.question.unvalidated_redirects_forwards"); /** * This file is part of the Security Shepherd Project. @@ -50,9 +50,9 @@ String levelHash = new String("f15f2766c971e16e68aa26043e6016a0a7f6879283c873d94 //This encoder should escape all output to prevent XSS attacks. This should be performed everywhere for safety Encoder encoder = ESAPI.encoder(); String csrfToken = encoder.encodeForHTML(tokenCookie.getValue()); - String hex = (String) ses.getAttribute("userName"); - String tempId = new Integer(hex.getBytes().hashCode() + hex.substring(0, hex.length() / 2).hashCode()).toString(); - ses.setAttribute("tempId", tempId); + String hex = (String) ses.getAttribute("userName"); + String tempId = new Integer(hex.getBytes().hashCode() + hex.substring(0, hex.length() / 2).hashCode()).toString(); + ses.setAttribute("tempId", tempId); %> diff --git a/SecurityShepherdCore/src/jsp/lessons/f758a97011ec4452cc0707e546a7c0f68abc6ef2ab747ea87e0892767152eae1.jsp b/SecurityShepherdCore/src/jsp/lessons/f758a97011ec4452cc0707e546a7c0f68abc6ef2ab747ea87e0892767152eae1.jsp index a9234a220..7502f56af 100644 --- a/SecurityShepherdCore/src/jsp/lessons/f758a97011ec4452cc0707e546a7c0f68abc6ef2ab747ea87e0892767152eae1.jsp +++ b/SecurityShepherdCore/src/jsp/lessons/f758a97011ec4452cc0707e546a7c0f68abc6ef2ab747ea87e0892767152eae1.jsp @@ -13,9 +13,6 @@ ResourceBundle mobile = ResourceBundle.getBundle("i18n.moduleGenerics.mobileGenericStrings", locale); //Used more than once translations String translatedLevelName = bundle.getString("title.question.csi"); - - //Level blurb can be written here in HTML OR go into the HTML body and write it there. Nobody will update this but you - String levelBlurb = ""; ShepherdLogManager.logEvent(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"), levelName + " Accessed"); if (request.getSession() != null) @@ -59,12 +56,10 @@ %> - -Security Shepherd - <%= translatedLevelName %> - - - - + + Security Shepherd - <%= translatedLevelName %> + + From ab0b2837eced9e7761e1988b28052d46eb7b0d05 Mon Sep 17 00:00:00 2001 From: Mark Denihan Date: Fri, 9 Oct 2015 21:15:25 +0100 Subject: [PATCH 076/112] Adding the last Getter.java JUnit Tests --- SecurityShepherdCore/database/coreSchema.sql | 18 - SecurityShepherdCore/src/dbProcs/Getter.java | 204 ++- .../test/dbProcs/GetterTest.java | 1140 +++++++++++------ 3 files changed, 818 insertions(+), 544 deletions(-) diff --git a/SecurityShepherdCore/database/coreSchema.sql b/SecurityShepherdCore/database/coreSchema.sql index 9b5ae4b0c..4616ea35f 100644 --- a/SecurityShepherdCore/database/coreSchema.sql +++ b/SecurityShepherdCore/database/coreSchema.sql @@ -152,25 +152,7 @@ END $$ DELIMITER ; --- ----------------------------------------------------- --- procedure userLocked --- ----------------------------------------------------- - -DELIMITER $$ -USE `core`$$ -CREATE PROCEDURE `core`.`userLocked` (IN theName VARCHAR(32)) -BEGIN -DECLARE theDate DATETIME; -COMMIT; -SELECT NOW() FROM DUAL INTO theDate; -SELECT userName FROM `users` - WHERE userName = theName - AND theDate > suspendedUntil; -END -$$ - -DELIMITER ; -- ----------------------------------------------------- -- procedure userLock -- ----------------------------------------------------- diff --git a/SecurityShepherdCore/src/dbProcs/Getter.java b/SecurityShepherdCore/src/dbProcs/Getter.java index ded4c1a89..0edd95917 100644 --- a/SecurityShepherdCore/src/dbProcs/Getter.java +++ b/SecurityShepherdCore/src/dbProcs/Getter.java @@ -43,6 +43,20 @@ public class Getter * Used for scoreboards / progress bars */ private static int widthOfUnitBar = 11; //px + private static int fieldTrainingCap = 45; + + private static int privateCap = 80; + + private static int corporalCap = 105; + + private static int sergeantCap = 130; + + private static int lieutenantCap = 145; + + private static int majorCap = 175; + + private static int admiralCap = 999; //everything above Major is Admiral + /** * This method hashes the user submitted password and sends it to the database. * The database does the rest of the work, including Brute Force prevention. @@ -180,47 +194,6 @@ public static String checkPlayerResult(String ApplicationRoot, String moduleId, return result; } - /** - * This method is used to determine if a CSRF level has been completed. - * A call is made to the DB that returns the CSRF counter for a level. - * If this counter is greater than 0, the level has been completed - * @param applicationRoot Running context of the application - * @param moduleHash Hash ID of the CSRF module you wish to check if a user has completed - * @param userId the ID of the user to check - * @return True or False value depicting if the user has completed the module - */ - public static boolean isCsrfLevelComplete (String applicationRoot, String moduleId, String userId) - { - log.debug("*** Setter.isCsrfLevelComplete ***"); - - boolean result = false; - - Connection conn = Database.getCoreConnection(applicationRoot); - try - { - log.debug("Preparing csrfLevelComplete call"); - CallableStatement callstmnt = conn.prepareCall("call csrfLevelComplete(?, ?)"); - callstmnt.setString(1, moduleId); - callstmnt.setString(2, userId); - log.debug("moduleId: " + moduleId); - log.debug("userId: " + userId); - log.debug("Executing csrfLevelComplete"); - ResultSet resultSet = callstmnt.executeQuery(); - resultSet.next(); - result = resultSet.getInt(1) > 0; // If Result is > 0, then the CSRF level is complete - if(result) - log.debug("CSRF Level is complete"); - } - catch(SQLException e) - { - log.error("csrfLevelComplete Failure: " + e.toString()); - result = false; - } - Database.closeConnection(conn); - log.debug("*** END isCsrfLevelComplete ***"); - return result; - } - /** * Used to decipher whether or not a user exists as a player * @param userId The user identifier of the player to be found @@ -1457,7 +1430,6 @@ public static String getModuleStatusMenu (String ApplicationRoot) Database.closeConnection(conn); return output; } - /** * This method returns the module categories in option tags that are to be open or closed in a <select> element for administration manipulation * @param ApplicationRoot @@ -1492,7 +1464,6 @@ public static String getOpenCloseCategoryMenu (String ApplicationRoot) Database.closeConnection(conn); return output; } - /** * This method is used to gather users according by class. Thanks to MySQL syntax, where class = null will return nothing, is null must be used. *
    is 'validClass' will Error, = 'validclass' must be used.
    @@ -1535,7 +1506,6 @@ public static ResultSet getPlayersByClass(String ApplicationRoot, String classId log.debug("*** END getPlayersByClass"); return result; } - /** * Used to present the progress of a class in a series of loading bars * @param applicationRoot The current running context of the application @@ -1586,7 +1556,6 @@ public static String getProgress(String applicationRoot, String classId) log.debug("*** END getProgress ***"); return result; } - /** * Use to return the current progress of a class in JSON format with information like user name, score and completed modules * @param applicationRoot The current running context of the application @@ -1644,7 +1613,25 @@ public static String getProgressJSON(String applicationRoot, String classId) log.debug("*** END getProgressJSON ***"); return result; } - + private static int getTounnamentSectionFromRankNumber (int rankNumber) + { + if(rankNumber < fieldTrainingCap) + return 1; + else if (rankNumber < privateCap) + return 2; + else if (rankNumber < corporalCap) + return 3; + else if (rankNumber < sergeantCap) + return 4; + else if (rankNumber < lieutenantCap) + return 5; + else if (rankNumber < majorCap) + return 6; + else if (rankNumber < admiralCap) + return 7; + else + return 7; //Max level is 7. + } /** * This method prepares the Tournament module menu. This is when Security Shepherd is in "Tournament Mode". * Users are presented with a list of that are specified as open. @@ -1769,61 +1756,34 @@ public static String getTournamentModules (String ApplicationRoot, String userId Database.closeConnection(conn); return levelMasterList; } - - private static int fieldTrainingCap = 45; - private static int privateCap = 80; - private static int corporalCap = 105; - private static int sergeantCap = 130; - private static int lieutenantCap = 145; - private static int majorCap = 175; - private static int admiralCap = 999; //everything above Major is Admiral - private static int getTounnamentSectionFromRankNumber (int rankNumber) - { - if(rankNumber < fieldTrainingCap) - return 1; - else if (rankNumber < privateCap) - return 2; - else if (rankNumber < corporalCap) - return 3; - else if (rankNumber < sergeantCap) - return 4; - else if (rankNumber < lieutenantCap) - return 5; - else if (rankNumber < majorCap) - return 6; - else if (rankNumber < admiralCap) - return 7; - else - return 7; //Max level is 7. - } - /** * @param ApplicationRoot The current running context of the application - * @param userId The identifier of a user - * @return The user name of the submitted user identifier + * @param userName The username of the user + * @return The class id of the submitted user name */ - public static String getUserName (String ApplicationRoot, String userId) + public static String getUserClassFromName (String ApplicationRoot, String userName) { - log.debug("*** Getter.getUserName ***"); + log.debug("*** Getter.getUserClass ***"); String result = new String(); Connection conn = Database.getCoreConnection(ApplicationRoot); try { - CallableStatement callstmt = conn.prepareCall("call userGetNameById(?)"); - log.debug("Gathering userGetNameById ResultSet"); - callstmt.setString(1, userId); + CallableStatement callstmt = conn.prepareCall("call userClassId(?)"); + log.debug("Gathering userClassId ResultSet"); + callstmt.setString(1, userName); ResultSet resultSet = callstmt.executeQuery(); - log.debug("Opening Result Set from userGetNameById"); + log.debug("Opening Result Set from userClassId"); resultSet.next(); - result = resultSet.getString(1); + result = resultSet.getString(0); + log.debug("Found " + result); } catch (SQLException e) { - log.error("Could not execute query: " + e.toString()); - result = null; + log.error("Could not execute userClassId: " + e.toString()); + result = new String(); } Database.closeConnection(conn); - log.debug("*** END getUserName ***"); + log.debug("*** END getUserClass ***"); return result; } @@ -1859,62 +1819,72 @@ public static String getUserIdFromName (String ApplicationRoot, String userName) /** * @param ApplicationRoot The current running context of the application - * @param userName The username of the user - * @return The class id of the submitted user name + * @param userId The identifier of a user + * @return The user name of the submitted user identifier */ - public static String getUserClassFromName (String ApplicationRoot, String userName) + public static String getUserName (String ApplicationRoot, String userId) { - log.debug("*** Getter.getUserClass ***"); + log.debug("*** Getter.getUserName ***"); String result = new String(); Connection conn = Database.getCoreConnection(ApplicationRoot); try { - CallableStatement callstmt = conn.prepareCall("call userClassId(?)"); - log.debug("Gathering userClassId ResultSet"); - callstmt.setString(1, userName); + CallableStatement callstmt = conn.prepareCall("call userGetNameById(?)"); + log.debug("Gathering userGetNameById ResultSet"); + callstmt.setString(1, userId); ResultSet resultSet = callstmt.executeQuery(); - log.debug("Opening Result Set from userClassId"); + log.debug("Opening Result Set from userGetNameById"); resultSet.next(); - result = resultSet.getString(0); - log.debug("Found " + result); + result = resultSet.getString(1); } catch (SQLException e) { - log.error("Could not execute userClassId: " + e.toString()); - result = new String(); + log.error("Could not execute query: " + e.toString()); + result = null; } Database.closeConnection(conn); - log.debug("*** END getUserClass ***"); + log.debug("*** END getUserName ***"); return result; } /** - * Used by authentication to check if account is locked before continuing with authentication process. - * @param ApplicationRoot The current running context of the application - * @param userName The userName to use for check - * @return A boolean value of if the user account is locked + * This method is used to determine if a CSRF level has been completed. + * A call is made to the DB that returns the CSRF counter for a level. + * If this counter is greater than 0, the level has been completed + * @param applicationRoot Running context of the application + * @param moduleHash Hash ID of the CSRF module you wish to check if a user has completed + * @param userId the ID of the user to check + * @return True or False value depicting if the user has completed the module */ - public static boolean isUserLocked (String ApplicationRoot, String userName) + public static boolean isCsrfLevelComplete (String applicationRoot, String moduleId, String userId) { - log.debug("*** Getter.isUserLocked ***"); - boolean result = true; - Connection conn = Database.getCoreConnection(ApplicationRoot); + log.debug("*** Setter.isCsrfLevelComplete ***"); + + boolean result = false; + + Connection conn = Database.getCoreConnection(applicationRoot); try { - CallableStatement callstmt = conn.prepareCall("call userLocked(?)"); - log.debug("Gathering userLocked ResultSet"); - callstmt.setString(1, userName); - ResultSet userLocked = callstmt.executeQuery(); - log.debug("Opening Result Set from userLocked"); - userLocked.next(); - result = !userLocked.getString(1).equalsIgnoreCase(userName); + log.debug("Preparing csrfLevelComplete call"); + CallableStatement callstmnt = conn.prepareCall("call csrfLevelComplete(?, ?)"); + callstmnt.setString(1, moduleId); + callstmnt.setString(2, userId); + log.debug("moduleId: " + moduleId); + log.debug("userId: " + userId); + log.debug("Executing csrfLevelComplete"); + ResultSet resultSet = callstmnt.executeQuery(); + resultSet.next(); + result = resultSet.getInt(1) > 0; // If Result is > 0, then the CSRF level is complete + if(result) + log.debug("CSRF Level is complete"); } - catch (SQLException e) + catch(SQLException e) { - log.error("Could not execute query: " + e.toString()); + log.error("csrfLevelComplete Failure: " + e.toString()); + result = false; } Database.closeConnection(conn); - log.debug("*** END isUserLocked ***"); + log.debug("*** END isCsrfLevelComplete ***"); return result; } } diff --git a/SecurityShepherdCore/test/dbProcs/GetterTest.java b/SecurityShepherdCore/test/dbProcs/GetterTest.java index 2e86ff203..4987dd753 100644 --- a/SecurityShepherdCore/test/dbProcs/GetterTest.java +++ b/SecurityShepherdCore/test/dbProcs/GetterTest.java @@ -19,6 +19,11 @@ import utils.ScoreboardStatus; +/** + * Class is targeted to test all of the methods found in the src/dbprocs/Getter.java class, but does include some coverage of other classes, such as Setter.java and Database.java + * @author mark + * + */ public class GetterTest { private static org.apache.log4j.Logger log = Logger.getLogger(GetterTest.class); @@ -26,12 +31,7 @@ public class GetterTest private static String lang = new String("en_GB"); private static Locale locale = new Locale(lang); private static String applicationRoot = new String(); - - @Before - public void setUp() - { - applicationRoot = System.getProperty("user.dir") + propertiesFileDirectory; - } + private static final int totalNumberOfModulesInShepherd = 58; /** * Searches for class based on class name. If nothing is found, the class is created and the new class Id is returned @@ -68,6 +68,45 @@ private static String findCreateClassId(String className) throws Exception return classId; } + /** + * This method will sign in as an admin, or create the admin and sign in as them. If this fails it will throw an Exception. + * This function will pass if correct user credentials are passed as well + * @param applicationRoot Context of running application + * @param userName The user name of the admin you want to create or sign in as + * @param password The password of the admin you want to create or sign in as + * @return Boolean value depicting if the user exists and can be authenticated + * @throws Exception If admin Create function fails, an exception will be passed up + */ + private static boolean verifyTestAdmin(String applicationRoot, String userName, String password) throws Exception + { + boolean result = false; + try + { + String user[] = Getter.authUser(applicationRoot, userName, userName); + if(user == null || user[0].isEmpty()) + { + log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); + Setter.userCreate(applicationRoot, null, userName, userName, "admin", userName+"@test.com", false); + user = Getter.authUser(applicationRoot, userName, userName); + } + if(user != null && !user[0].isEmpty()) + { + log.debug(userName + " could authenticate. returning true"); + result = true; + } + else + { + log.error("Couldnt verify that " + userName + " could authenticate at all. Throwing Exception"); + throw new Exception("Could not Verify User " + userName + " could authenticate at all."); + } + } + catch(Exception e) + { + throw new Exception("Could not Create User " + userName + ": " + e.toString()); + } + return result; + } + /** * This method will sign in as a User, or create the user and sign in as them. If this fails it will throw an Exception * @param applicationRoot Context of running application @@ -163,43 +202,10 @@ private static boolean verifyTestUser(String applicationRoot, String userName, S return result; } - /** - * This method will sign in as an admin, or create the admin and sign in as them. If this fails it will throw an Exception. - * This function will pass if correct user credentials are passed as well - * @param applicationRoot Context of running application - * @param userName The user name of the admin you want to create or sign in as - * @param password The password of the admin you want to create or sign in as - * @return Boolean value depicting if the user exists and can be authenticated - * @throws Exception If admin Create function fails, an exception will be passed up - */ - private static boolean verifyTestAdmin(String applicationRoot, String userName, String password) throws Exception + @Before + public void setUp() { - boolean result = false; - try - { - String user[] = Getter.authUser(applicationRoot, userName, userName); - if(user == null || user[0].isEmpty()) - { - log.debug("Test Failed. User not found in DB. Adding user to DB and Retesting before reporting failure"); - Setter.userCreate(applicationRoot, null, userName, userName, "admin", userName+"@test.com", false); - user = Getter.authUser(applicationRoot, userName, userName); - } - if(user != null && !user[0].isEmpty()) - { - log.debug(userName + " could authenticate. returning true"); - result = true; - } - else - { - log.error("Couldnt verify that " + userName + " could authenticate at all. Throwing Exception"); - throw new Exception("Could not Verify User " + userName + " could authenticate at all."); - } - } - catch(Exception e) - { - throw new Exception("Could not Create User " + userName + ": " + e.toString()); - } - return result; + applicationRoot = System.getProperty("user.dir") + propertiesFileDirectory; } @Test @@ -347,67 +353,42 @@ public void testAuthUserSqlInjectionUserName() } @Test - public void testCheckPlayerResultWhenModuleNotOpened() - { - String userName = new String("noModulesOpened"); - String unOpenedModuleId = new String("0dbea4cb5811fff0527184f99bd5034ca9286f11"); //Insecure Direct Object References Module Id - try - { - if(verifyTestUser(applicationRoot, userName, userName)) - { - String test = Getter.checkPlayerResult(applicationRoot, unOpenedModuleId, userName); - if(test != null) - { - log.fatal("result should be null but it was: " + test); - fail("Function says User has opened module they should not have opened by default"); // User Should not have completed this module by default after running a fresh DB. ensure you have a fresh DB if this fails - } - else - { - log.debug("PASS: Function says user has not opened module"); - return; //Pass - } - } - else - { - fail("Could not verify user (No Exception Failure)"); - } - } - catch(Exception e) - { - log.fatal("Could not Verify User: " + e.toString()); - fail("Could not Verify User " + userName); - } - } - - @Test - public void testCheckPlayerResultWhenModuleWhenOpened() + public void testCheckPlayerResultWhenModuleComplete() { - String userName = new String("userHasModulesOpened"); - String csrfChallengeThree = new String("5ca9115f3279b9b9f3308eb6a59a4fcd374846d6"); + String userName = new String("userResultWhenComplete"); + String dataStorageLessonId = new String("53a53a66cb3bf3e4c665c442425ca90e29536edd"); try { if(verifyTestUser(applicationRoot, userName, userName)) { + String userId = Getter.getUserIdFromName(applicationRoot, userName); + //Open all Modules First so that the Module Can Be Opened if(Setter.openAllModules(applicationRoot)) { //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeThree, Getter.getUserIdFromName(applicationRoot, userName)).isEmpty()) + if(!Getter.getModuleAddress(applicationRoot, dataStorageLessonId, userId).isEmpty()) { - String test = Getter.checkPlayerResult(applicationRoot, csrfChallengeThree, Getter.getUserIdFromName(applicationRoot, userName)); - if(test == null) + //Then, Mark the Challenge Complete for user (Insecure Data Storage Lesson) + String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, dataStorageLessonId, userId, "Feedback is Disabled", 1, 1, 1); + if (markLevelCompleteTest != null) { - fail("Function says " + userName + " has not opened module"); // User Should have opened and not completed CSRF Three + String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, dataStorageLessonId, userId); + log.debug("checkPlayerResultTest" + checkPlayerResultTest); + if(checkPlayerResultTest == null) + return; //Pass + else + { + fail("Function says user has not completed module"); //Even though this test just marked it as Completed + } } else - return; //Pass + fail("Could not mark data storage lesson as complete for user"); } else - fail("Could not Mark CSRF 3 as Opened by " + userName); + fail("Could not Mark Data Storage Lesson as Opened by Default admin"); } else - { - fail("Could not Mark Modules As Opened"); - } + fail("Could not Open All Modules"); } else { @@ -466,42 +447,25 @@ public void testCheckPlayerResultWhenModuleNotComplete() } @Test - public void testCheckPlayerResultWhenModuleComplete() + public void testCheckPlayerResultWhenModuleNotOpened() { - String userName = new String("userResultWhenComplete"); - String dataStorageLessonId = new String("53a53a66cb3bf3e4c665c442425ca90e29536edd"); + String userName = new String("noModulesOpened"); + String unOpenedModuleId = new String("0dbea4cb5811fff0527184f99bd5034ca9286f11"); //Insecure Direct Object References Module Id try { if(verifyTestUser(applicationRoot, userName, userName)) { - String userId = Getter.getUserIdFromName(applicationRoot, userName); - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + String test = Getter.checkPlayerResult(applicationRoot, unOpenedModuleId, userName); + if(test != null) { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, dataStorageLessonId, userId).isEmpty()) - { - //Then, Mark the Challenge Complete for user (Insecure Data Storage Lesson) - String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, dataStorageLessonId, userId, "Feedback is Disabled", 1, 1, 1); - if (markLevelCompleteTest != null) - { - String checkPlayerResultTest = Getter.checkPlayerResult(applicationRoot, dataStorageLessonId, userId); - log.debug("checkPlayerResultTest" + checkPlayerResultTest); - if(checkPlayerResultTest == null) - return; //Pass - else - { - fail("Function says user has not completed module"); //Even though this test just marked it as Completed - } - } - else - fail("Could not mark data storage lesson as complete for user"); - } - else - fail("Could not Mark Data Storage Lesson as Opened by Default admin"); + log.fatal("result should be null but it was: " + test); + fail("Function says User has opened module they should not have opened by default"); // User Should not have completed this module by default after running a fresh DB. ensure you have a fresh DB if this fails } else - fail("Could not Open All Modules"); + { + log.debug("PASS: Function says user has not opened module"); + return; //Pass + } } else { @@ -515,48 +479,34 @@ public void testCheckPlayerResultWhenModuleComplete() } } - @Test - public void testIsCsrfLevelCompleteIncrementedCounter() + public void testCheckPlayerResultWhenModuleWhenOpened() { - String userName = new String("csrfCounterIncremented"); - String csrfChallengeOne = new String("20e755179a5840be5503d42bb3711716235005ea"); //CSRF Challenge 1 (Should have CSRF Counter of 0 for new user) + String userName = new String("userHasModulesOpened"); + String csrfChallengeThree = new String("5ca9115f3279b9b9f3308eb6a59a4fcd374846d6"); try { if(verifyTestUser(applicationRoot, userName, userName)) { - String userId = Getter.getUserIdFromName(applicationRoot, userName); - //Open all Modules First so that the Module Can Be Opened if(Setter.openAllModules(applicationRoot)) { //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeOne, userId).isEmpty()) + if(!Getter.getModuleAddress(applicationRoot, csrfChallengeThree, Getter.getUserIdFromName(applicationRoot, userName)).isEmpty()) { - //Increment Challenge CSRF Counter - if(Setter.updateCsrfCounter(applicationRoot, csrfChallengeOne, userId)) + String test = Getter.checkPlayerResult(applicationRoot, csrfChallengeThree, Getter.getUserIdFromName(applicationRoot, userName)); + if(test == null) { - if(Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeOne, userId)) - { - return; //Pass, because CSRF level is completed after the user CSRF counter was incremented - } - else - { - fail("CSRF 1 not completed after successful increment"); - } + fail("Function says " + userName + " has not opened module"); // User Should have opened and not completed CSRF Three } else - { - fail("Could not Increment user Counter for CSRF 1"); - } + return; //Pass } else - { - fail("Could not Mark CSRF 1 as opened by user"); - } + fail("Could not Mark CSRF 3 as Opened by " + userName); } else { - fail("Could not Mark Modules as Opened"); + fail("Could not Mark Modules As Opened"); } } else @@ -571,39 +521,24 @@ public void testIsCsrfLevelCompleteIncrementedCounter() } } + @Test - public void testIsCsrfLevelCompleteWithoutIncrementedCounter() - { - String userName = new String("csrfCounterWithoutInc"); - String csrfChallengeTwo = new String("94cd2de560d89ef59fc450ecc647ff4d4a55c15d"); //CSRF Challenge 2 (Should have CSRF Counter of 0 for new user + public void testFindPlayerById() + { + String userName = new String("UserForPlayerIdSearch"); try { if(verifyTestUser(applicationRoot, userName, userName)) { String userId = Getter.getUserIdFromName(applicationRoot, userName); - //Open all Modules First so that the Module Can Be Opened - if(Setter.openAllModules(applicationRoot)) + if(Getter.findPlayerById(applicationRoot, userId)) { - //Simulate user Opening Level - if(!Getter.getModuleAddress(applicationRoot, csrfChallengeTwo, userId).isEmpty()) - { - if(!Getter.isCsrfLevelComplete(applicationRoot, csrfChallengeTwo, userId)) - { - return; //Pass, because CSRF level is not completed because the CSRF Counter for the user is 0 - } - else - { - fail("CSRF 2 marked completed without increment"); // CSRF 2 Challenge should have a counter of 0 and should not return true. - } - } - else - { - fail("Could not Mark CSRF 2 as opened by user"); - } + log.debug("PASS: Found user"); + return; } else { - fail("Could not mark All Modules as Opened"); + fail("Could Not Find Player in Player Search"); } } else @@ -619,38 +554,7 @@ public void testIsCsrfLevelCompleteWithoutIncrementedCounter() } @Test - public void testFindPlayerById() - { - String userName = new String("UserForPlayerIdSearch"); - try - { - if(verifyTestUser(applicationRoot, userName, userName)) - { - String userId = Getter.getUserIdFromName(applicationRoot, userName); - if(Getter.findPlayerById(applicationRoot, userId)) - { - log.debug("PASS: Found user"); - return; - } - else - { - fail("Could Not Find Player in Player Search"); - } - } - else - { - fail("Could not verify user (No Exception Failure)"); - } - } - catch(Exception e) - { - log.fatal("Could not Verify User: " + e.toString()); - fail("Could not Verify User " + userName); - } - } - - @Test - public void testFindPlayerByIdWithAdminId() + public void testFindPlayerByIdWithAdminId() { String userName = new String("playerSearchWithAdmin"); @@ -693,7 +597,6 @@ public void testFindPlayerByIdWithBadUserId() fail("Found Player That Does Not Exist"); } } - @Test public void testGetAllModuleInfo() @@ -711,7 +614,6 @@ public void testGetAllModuleInfo() } } - @Test public void testGetChallenges() { @@ -729,7 +631,7 @@ public void testGetChallenges() { //Get number of Challenges returned by getChallenges method int numberofChallengesReturned = (modules.length() - modules.replace("class='lesson'", "").length()) / "class='lesson'".length(); - if(numberofChallengesReturned > 58) + if(numberofChallengesReturned > totalNumberOfModulesInShepherd) { log.debug("PASS: Found " + numberofChallengesReturned + " modules"); return; @@ -762,6 +664,7 @@ public void testGetChallenges() fail("Could not Verify User " + userName); } } + @Test public void testGetChallengesWhenModulesClosed() @@ -814,6 +717,7 @@ public void testGetChallengesWhenModulesClosed() } } + @Test public void testGetClassCount() { @@ -839,7 +743,6 @@ public void testGetClassCount() } } - @Test public void testGetClassInfoString() { try @@ -930,7 +833,8 @@ else if(!classInfo[1].equalsIgnoreCase("2015")) fail("Could not open ClassInfo Result Set"); } } - + + @Test public void testGetCsrfForumWithIframe() { @@ -984,7 +888,7 @@ public void testGetCsrfForumWithIframe() } log.debug("End of CSRF Iframe Forum Test"); } - + @Test public void testGetCsrfForumWithImg() { @@ -1051,7 +955,7 @@ public void testGetCsrfForumWithImg() } } } - + @Test public void testGetFeedback() { @@ -1115,50 +1019,33 @@ public void testGetFeedback() fail("Could not Verify User " + userName); } } - + @Test - public void testGetIncrementalModulesWithNoneComplete() + public void testGetIncrementalModulesWithModulesClosed() { - String userName = new String("testIncModuleMenu1"); - String lowestRankModuleId = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //This should be changed if an easier module is made + String userName = new String("testIncModuleMenu2"); try { if(verifyTestUser(applicationRoot, userName, userName)) { String userId = Getter.getUserIdFromName(applicationRoot, userName); - //Open all Modules First - if(Setter.openAllModules(applicationRoot)) + //Close all Modules First + if(Setter.closeAllModules(applicationRoot)) { String incrementalModules = Getter.getIncrementalModules(applicationRoot, userId, lang, "testingCSRFtoken"); - if(incrementalModules.indexOf("Completed") == -1) //User should not have completed any modules. The Completed Button should not be present + if(incrementalModules.indexOf("You've Finished!") > -1) //IF no modules are open, this is the expected leading string { - if(incrementalModules.indexOf(lowestRankModuleId) > -1) //The only module Id to be returned should be this one as it is the first presented (Lowest Incremental Rank) - { - if(incrementalModules.indexOf("Get Next Challenge") > -1) //This is the English string that should be included with the lang submitted in this unit test - { - log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test"); - return; - } - else - { - fail("Could not Detect i18n English Values in Menu"); - } - } - else - { - fail("The Module Id Returned was not the Known First Level. Ie not: " + lowestRankModuleId); - } + log.debug("PASS: Menu appears to have compiled correctly"); } else { - fail("CTF Menu Appears as if User Has Completed Modules When They Have Not"); + log.debug("incrementalModules returned: " + incrementalModules); + fail("Could not Detect Finished Message"); } - //Wont Log unless unit doesnt pass - log.debug(incrementalModules); } else { - fail("Could not open All Modules"); + fail("Could not Close All Modules"); } } else @@ -1174,31 +1061,48 @@ public void testGetIncrementalModulesWithNoneComplete() } @Test - public void testGetIncrementalModulesWithModulesClosed() + public void testGetIncrementalModulesWithNoneComplete() { - String userName = new String("testIncModuleMenu2"); + String userName = new String("testIncModuleMenu1"); + String lowestRankModuleId = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //This should be changed if an easier module is made try { if(verifyTestUser(applicationRoot, userName, userName)) { String userId = Getter.getUserIdFromName(applicationRoot, userName); - //Close all Modules First - if(Setter.closeAllModules(applicationRoot)) + //Open all Modules First + if(Setter.openAllModules(applicationRoot)) { String incrementalModules = Getter.getIncrementalModules(applicationRoot, userId, lang, "testingCSRFtoken"); - if(incrementalModules.indexOf("You've Finished!") > -1) //IF no modules are open, this is the expected leading string + if(incrementalModules.indexOf("Completed") == -1) //User should not have completed any modules. The Completed Button should not be present { - log.debug("PASS: Menu appears to have compiled correctly"); + if(incrementalModules.indexOf(lowestRankModuleId) > -1) //The only module Id to be returned should be this one as it is the first presented (Lowest Incremental Rank) + { + if(incrementalModules.indexOf("Get Next Challenge") > -1) //This is the English string that should be included with the lang submitted in this unit test + { + log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test"); + return; + } + else + { + fail("Could not Detect i18n English Values in Menu"); + } + } + else + { + fail("The Module Id Returned was not the Known First Level. Ie not: " + lowestRankModuleId); + } } else { - log.debug("incrementalModules returned: " + incrementalModules); - fail("Could not Detect Finished Message"); + fail("CTF Menu Appears as if User Has Completed Modules When They Have Not"); } + //Wont Log unless unit doesnt pass + log.debug(incrementalModules); } else { - fail("Could not Close All Modules"); + fail("Could not open All Modules"); } } else @@ -1301,57 +1205,40 @@ public void testGetIncrementalModulesWithOneModuleComplete() } @Test - public void testGetIncrementalModulesWithoutScriptWithNoneComplete() + public void testGetIncrementalModulesWithoutScriptWithModulesClosed() { - String userName = new String("testIncModuleMenuScript1"); - String lowestRankModuleId = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //This should be changed if an easier module is made + String userName = new String("testIncModuleMenuScript2"); try { if(verifyTestUser(applicationRoot, userName, userName)) { String userId = Getter.getUserIdFromName(applicationRoot, userName); - //Open all Modules First - if(Setter.openAllModules(applicationRoot)) + //Close all Modules First + if(Setter.closeAllModules(applicationRoot)) { String incrementalModules = Getter.getIncrementalModulesWithoutScript(applicationRoot, userId, lang, "testingCSRFtoken"); - if(incrementalModules.indexOf("Completed") == -1) //User should not have completed any modules. The Completed Button should not be present + if(incrementalModules.indexOf("You've Finished!") > -1) //IF no modules are open, this is the expected leading string { - if(incrementalModules.indexOf(lowestRankModuleId) > -1) //The only module Id to be returned should be this one as it is the first presented (Lowest Incremental Rank) + if(!incrementalModules.endsWith(";")) { - if(incrementalModules.indexOf("Get Next Challenge") > -1) //This is the English string that should be included with the lang submitted in this unit test - { - if(!incrementalModules.endsWith(";")) - { - log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test without ending in the button script"); - return; - } - else - { - log.debug("incrementalModules returned: " + incrementalModules); - fail("Function Ended in Unexpected Script"); - } - } - else - { - log.debug("incrementalModules returned: " + incrementalModules); - fail("Could not Detect i18n English Values in Menu"); - } + log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test without ending in the button script"); + return; } else { log.debug("incrementalModules returned: " + incrementalModules); - fail("The Module Id Returned was not the Known First Level. Ie not: " + lowestRankModuleId); + fail("Function Ended in Unexpected Script"); } } else { log.debug("incrementalModules returned: " + incrementalModules); - fail("CTF Menu Appears as if User Has Completed Modules When They Have Not"); + fail("Could not Detect Finished Message"); } } else { - fail("Could not open All Modules"); + fail("Could not Close All Modules"); } } else @@ -1367,40 +1254,57 @@ public void testGetIncrementalModulesWithoutScriptWithNoneComplete() } @Test - public void testGetIncrementalModulesWithoutScriptWithModulesClosed() + public void testGetIncrementalModulesWithoutScriptWithNoneComplete() { - String userName = new String("testIncModuleMenuScript2"); + String userName = new String("testIncModuleMenuScript1"); + String lowestRankModuleId = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //This should be changed if an easier module is made try { if(verifyTestUser(applicationRoot, userName, userName)) { String userId = Getter.getUserIdFromName(applicationRoot, userName); - //Close all Modules First - if(Setter.closeAllModules(applicationRoot)) + //Open all Modules First + if(Setter.openAllModules(applicationRoot)) { String incrementalModules = Getter.getIncrementalModulesWithoutScript(applicationRoot, userId, lang, "testingCSRFtoken"); - if(incrementalModules.indexOf("You've Finished!") > -1) //IF no modules are open, this is the expected leading string + if(incrementalModules.indexOf("Completed") == -1) //User should not have completed any modules. The Completed Button should not be present { - if(!incrementalModules.endsWith(";")) + if(incrementalModules.indexOf(lowestRankModuleId) > -1) //The only module Id to be returned should be this one as it is the first presented (Lowest Incremental Rank) { - log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test without ending in the button script"); - return; + if(incrementalModules.indexOf("Get Next Challenge") > -1) //This is the English string that should be included with the lang submitted in this unit test + { + if(!incrementalModules.endsWith(";")) + { + log.debug("PASS: Incremental Menu Appears to have Rendered correctly with the Preconditions of this test without ending in the button script"); + return; + } + else + { + log.debug("incrementalModules returned: " + incrementalModules); + fail("Function Ended in Unexpected Script"); + } + } + else + { + log.debug("incrementalModules returned: " + incrementalModules); + fail("Could not Detect i18n English Values in Menu"); + } } else { log.debug("incrementalModules returned: " + incrementalModules); - fail("Function Ended in Unexpected Script"); + fail("The Module Id Returned was not the Known First Level. Ie not: " + lowestRankModuleId); } } else { log.debug("incrementalModules returned: " + incrementalModules); - fail("Could not Detect Finished Message"); + fail("CTF Menu Appears as if User Has Completed Modules When They Have Not"); } } else { - fail("Could not Close All Modules"); + fail("Could not open All Modules"); } } else @@ -1513,15 +1417,15 @@ public void testGetIncrementalModulesWithoutScriptWithOneModuleComplete() } /** - * Test to see if Score board returns score for entire user base regardless of class + * Tests to ensure that user can only see their data in the scoreboard, and cannot see the data from users in other classes in the scoreboard */ @Test - public void testGetJsonScoreTotalOpen() + public void testGetJsonScoreClassSpecific() { - String userName = new String("scoreUserTotalScore"); - String className = new String("ScoreTotalScore"); - String otherUserName = new String("scoreUserTotalScoreb2"); - String otherClassName = new String("ScoreTotalScoreb2"); + String userName = new String("scoreUserClassSpecific"); + String className = new String("ScoreClassSpec"); + String otherUserName = new String("scoreUserClassSpecific2"); + String otherClassName = new String("ScoreClassSpec2"); String classId = new String(); String classId2 = new String(); String insecureDirectObjectRefLesson = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //Direct Object Reference Module @@ -1552,9 +1456,8 @@ public void testGetJsonScoreTotalOpen() if (markLevelCompleteTest != null) { boolean pass = false; - boolean user2 = false; //Configure Score board for class Specific - ScoreboardStatus.setScoreboeardOpen(); + ScoreboardStatus.setScoreboardClassSpecific(); //Get Score board Data String scoreboardData = Getter.getJsonScore(applicationRoot, classId); //Take the JSON String and make it Java JSON friendly @@ -1570,23 +1473,15 @@ public void testGetJsonScoreTotalOpen() } if(scoreRowJson.get("username").toString().compareTo(otherUserName) == 0) { - user2 = true; - log.debug("Found " + otherUserName + " in scoreboard"); + log.fatal("Found Class User that shouldn't be included in the output"); + log.debug("Found " + otherUserName + " in: " + scoreboardData); + fail("Found Class User that shouldn't be included in the Scoreboard Data"); } } - if(!(pass && user2)) + if(!pass) { - if(!pass) - { - log.error("Could not find " + userName + " in JSON Data: " + scoreboardData); - fail("Could not find user in scoreboard"); - } - else - { - log.error("Could not see users from other class in total scoreboard data"); - log.error("Could not find " + otherUserName + " in " + scoreboardData); - fail("Could not see users from other class in total scoreboard data"); - } + log.error("Could not find " + userName + " in JSON Data: " + scoreboardData); + fail("Could not find user in scoreboard"); } else { @@ -1616,80 +1511,76 @@ public void testGetJsonScoreTotalOpen() } /** - * Tests to ensure that user can only see their data in the scoreboard, and cannot see the data from users in other classes in the scoreboard + * Test to ensure users that have not scored any points, or are on negative points are not shown in the scoreboard */ @Test - public void testGetJsonScoreClassSpecific() + public void testGetJsonScoreTotalNoneOrNegPoints() { - String userName = new String("scoreUserClassSpecific"); - String className = new String("ScoreClassSpec"); - String otherUserName = new String("scoreUserClassSpecific2"); - String otherClassName = new String("ScoreClassSpec2"); + String userName = new String("userZero"); + String className = new String("LowScoreTeam"); + String otherUserName = new String("userMinusFive"); String classId = new String(); - String classId2 = new String(); - String insecureDirectObjectRefLesson = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //Direct Object Reference Module try { try { classId = findCreateClassId(className); - classId2 = findCreateClassId(otherClassName); + log.debug("Class Found"); } catch(Exception e) { log.fatal("Could not Find or Create Class : " + e.toString()); - fail("Could not Create or Find Classes"); + fail("Could not Create or Find Class"); } - if(verifyTestUser(applicationRoot, userName, userName, classId) && verifyTestUser(applicationRoot, otherUserName, otherUserName, classId2)) + if(verifyTestUser(applicationRoot, userName, userName, classId) && verifyTestUser(applicationRoot, otherUserName, otherUserName, classId)) { - String userId = Getter.getUserIdFromName(applicationRoot, userName); + log.debug("User's Verified"); String otherUserId = Getter.getUserIdFromName(applicationRoot, otherUserName); + log.debug("UserId retrieved"); //Open all Modules First if(Setter.openAllModules(applicationRoot)) { - String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, insecureDirectObjectRefLesson, userId, "Feedback is Disabled", 1, 1, 1); - if(markLevelCompleteTest != null) - markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, insecureDirectObjectRefLesson, otherUserId, "Feedback is Disabled", 1, 1, 1); - else - fail("Could Not Mark Level as complete by User 1"); - if (markLevelCompleteTest != null) - { - boolean pass = false; - //Configure Score board for class Specific - ScoreboardStatus.setScoreboardClassSpecific(); + log.debug("Opened All Modules"); + //Not Touching User Zero, But dropping five points from other user + if (Setter.updateUserPoints(applicationRoot, otherUserId, -5)) + { + log.debug("Updated Points of user Minus 5"); + //Configure Score board for total open + ScoreboardStatus.setScoreboeardOpen(); + log.debug("Scoreboard Set to Open"); //Get Score board Data String scoreboardData = Getter.getJsonScore(applicationRoot, classId); + if(scoreboardData.isEmpty()) + { + log.debug("PASS: The Scoreboard response was empty. Therefore the users are not valid to be returned"); + return; //PASS + } + log.debug("Got Scoreboard Data"); //Take the JSON String and make it Java JSON friendly JSONArray scoreboardJson = (JSONArray)JSONValue.parse(scoreboardData); + log.debug("Parsed Scoreboard Data"); + if(scoreboardJson == null) + log.debug("scoreboardJson is Null. json was: " + scoreboardData); //Loop through array to find Our user for(int i = 0; i < scoreboardJson.size(); i++) { + log.debug("Looping through Array " + i); JSONObject scoreRowJson = (JSONObject)scoreboardJson.get(i); if(scoreRowJson.get("username").toString().compareTo(userName) == 0) { - pass = true; - log.debug("Found " + userName + " in scoreboard"); + fail("Found " + userName + " in scoreboard"); } if(scoreRowJson.get("username").toString().compareTo(otherUserName) == 0) { - log.fatal("Found Class User that shouldn't be included in the output"); - log.debug("Found " + otherUserName + " in: " + scoreboardData); - fail("Found Class User that shouldn't be included in the Scoreboard Data"); + fail("Found " + otherUserName + " in scoreboard"); } } - if(!pass) - { - log.error("Could not find " + userName + " in JSON Data: " + scoreboardData); - fail("Could not find user in scoreboard"); - } - else - { - return; //PASS - } + log.debug("PASS: Did not ether user's in the response, therefore they were not included"); + return; //PASS } else { - fail("Failed to Mark Direct Object Level as Complete for 2nd User"); + fail("Failed to Subtract points from " + otherUserName); } } else @@ -1710,15 +1601,15 @@ public void testGetJsonScoreClassSpecific() } /** - * Ensuring HTML is encoded from untrusted user inputs in scoreboard + * Test to see if Score board returns score for entire user base regardless of class */ @Test - public void testGetJsonScoreTotalOpenHtmlChars() + public void testGetJsonScoreTotalOpen() { - String userName = new String(""); - String otherUserName = new String("\"onerror=\"alert('Name');//"); - String otherClassName = new String("\"onerror=\"alert('C');//"); + String userName = new String("scoreUserTotalScore"); + String className = new String("ScoreTotalScore"); + String otherUserName = new String("scoreUserTotalScoreb2"); + String otherClassName = new String("ScoreTotalScoreb2"); String classId = new String(); String classId2 = new String(); String insecureDirectObjectRefLesson = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //Direct Object Reference Module @@ -1748,7 +1639,9 @@ public void testGetJsonScoreTotalOpenHtmlChars() fail("Could Not Mark Level as complete by User 1"); if (markLevelCompleteTest != null) { - //Configure Score board for total open + boolean pass = false; + boolean user2 = false; + //Configure Score board for class Specific ScoreboardStatus.setScoreboeardOpen(); //Get Score board Data String scoreboardData = Getter.getJsonScore(applicationRoot, classId); @@ -1758,17 +1651,35 @@ public void testGetJsonScoreTotalOpenHtmlChars() for(int i = 0; i < scoreboardJson.size(); i++) { JSONObject scoreRowJson = (JSONObject)scoreboardJson.get(i); - if(scoreRowJson.get("username").toString().compareTo(userName) == 0) //Therefore not encoded for HTML + if(scoreRowJson.get("username").toString().compareTo(userName) == 0) { - fail("Found " + userName + " in scoreboard"); + pass = true; + log.debug("Found " + userName + " in scoreboard"); } - if(scoreRowJson.get("username").toString().compareTo(otherUserName) == 0) //Therefore not encoded for HTML + if(scoreRowJson.get("username").toString().compareTo(otherUserName) == 0) { - fail("Found " + otherUserName + " in scoreboard"); + user2 = true; + log.debug("Found " + otherUserName + " in scoreboard"); } } - log.debug("PASS: Did not find HTML Strings in Scoreboard Response. Therefore they are encoded"); - return; //PASS + if(!(pass && user2)) + { + if(!pass) + { + log.error("Could not find " + userName + " in JSON Data: " + scoreboardData); + fail("Could not find user in scoreboard"); + } + else + { + log.error("Could not see users from other class in total scoreboard data"); + log.error("Could not find " + otherUserName + " in " + scoreboardData); + fail("Could not see users from other class in total scoreboard data"); + } + } + else + { + return; //PASS + } } else { @@ -1793,76 +1704,69 @@ public void testGetJsonScoreTotalOpenHtmlChars() } /** - * Test to ensure users that have not scored any points, or are on negative points are not shown in the scoreboard + * Ensuring HTML is encoded from untrusted user inputs in scoreboard */ @Test - public void testGetJsonScoreTotalNoneOrNegPoints() + public void testGetJsonScoreTotalOpenHtmlChars() { - String userName = new String("userZero"); - String className = new String("LowScoreTeam"); - String otherUserName = new String("userMinusFive"); + String userName = new String(""); + String otherUserName = new String("\"onerror=\"alert('Name');//"); + String otherClassName = new String("\"onerror=\"alert('C');//"); String classId = new String(); + String classId2 = new String(); + String insecureDirectObjectRefLesson = "0dbea4cb5811fff0527184f99bd5034ca9286f11"; //Direct Object Reference Module try { try { classId = findCreateClassId(className); - log.debug("Class Found"); + classId2 = findCreateClassId(otherClassName); } catch(Exception e) { log.fatal("Could not Find or Create Class : " + e.toString()); - fail("Could not Create or Find Class"); + fail("Could not Create or Find Classes"); } - if(verifyTestUser(applicationRoot, userName, userName, classId) && verifyTestUser(applicationRoot, otherUserName, otherUserName, classId)) + if(verifyTestUser(applicationRoot, userName, userName, classId) && verifyTestUser(applicationRoot, otherUserName, otherUserName, classId2)) { - log.debug("User's Verified"); + String userId = Getter.getUserIdFromName(applicationRoot, userName); String otherUserId = Getter.getUserIdFromName(applicationRoot, otherUserName); - log.debug("UserId retrieved"); //Open all Modules First if(Setter.openAllModules(applicationRoot)) { - log.debug("Opened All Modules"); - //Not Touching User Zero, But dropping five points from other user - if (Setter.updateUserPoints(applicationRoot, otherUserId, -5)) + String markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, insecureDirectObjectRefLesson, userId, "Feedback is Disabled", 1, 1, 1); + if(markLevelCompleteTest != null) + markLevelCompleteTest = Setter.updatePlayerResult(applicationRoot, insecureDirectObjectRefLesson, otherUserId, "Feedback is Disabled", 1, 1, 1); + else + fail("Could Not Mark Level as complete by User 1"); + if (markLevelCompleteTest != null) { - log.debug("Updated Points of user Minus 5"); //Configure Score board for total open ScoreboardStatus.setScoreboeardOpen(); - log.debug("Scoreboard Set to Open"); //Get Score board Data String scoreboardData = Getter.getJsonScore(applicationRoot, classId); - if(scoreboardData.isEmpty()) - { - log.debug("PASS: The Scoreboard response was empty. Therefore the users are not valid to be returned"); - return; //PASS - } - log.debug("Got Scoreboard Data"); //Take the JSON String and make it Java JSON friendly JSONArray scoreboardJson = (JSONArray)JSONValue.parse(scoreboardData); - log.debug("Parsed Scoreboard Data"); - if(scoreboardJson == null) - log.debug("scoreboardJson is Null. json was: " + scoreboardData); //Loop through array to find Our user for(int i = 0; i < scoreboardJson.size(); i++) { - log.debug("Looping through Array " + i); JSONObject scoreRowJson = (JSONObject)scoreboardJson.get(i); - if(scoreRowJson.get("username").toString().compareTo(userName) == 0) + if(scoreRowJson.get("username").toString().compareTo(userName) == 0) //Therefore not encoded for HTML { fail("Found " + userName + " in scoreboard"); } - if(scoreRowJson.get("username").toString().compareTo(otherUserName) == 0) + if(scoreRowJson.get("username").toString().compareTo(otherUserName) == 0) //Therefore not encoded for HTML { fail("Found " + otherUserName + " in scoreboard"); } } - log.debug("PASS: Did not ether user's in the response, therefore they were not included"); + log.debug("PASS: Did not find HTML Strings in Scoreboard Response. Therefore they are encoded"); return; //PASS } else { - fail("Failed to Subtract points from " + otherUserName); + fail("Failed to Mark Direct Object Level as Complete for 2nd User"); } } else @@ -2018,7 +1922,7 @@ public void testGetLessonsWhenClosed() fail("Could not Verify User " + userName); } } - + @Test public void testGetModuleAddress() { @@ -2091,7 +1995,7 @@ public void testGetModuleAddressWhenClosed() fail("Could not Verify User " + userName); } } - + @Test public void testGetModuleCategory() { @@ -2135,35 +2039,35 @@ public void testGetModuleIdFromHash() } @Test - public void testGetModuleKeyTypeHardcodedKey() + public void testGetModuleKeyTypeEncryptedKey() { - String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); - if(Getter.getModuleKeyType(applicationRoot, insecureCryptoLesson)) + String csrfChallengeThree = new String("5ca9115f3279b9b9f3308eb6a59a4fcd374846d6"); + if(!Getter.getModuleKeyType(applicationRoot, csrfChallengeThree)) { - log.debug("PASS: Hardcoded Key Detected on Hardcoded Level"); + log.debug("PASS: Encrypted Key Detected on Encrypted Level"); } else { - log.fatal("Encrypted Key Detected On Hardcoded Key Module"); - fail("Encrypted Key Detected On Hardcoded Key Module"); + log.fatal("Hardcoded Key Detected On Encrypted Key Module"); + fail("Hardcoded Key Detected On Encrypted Key Module"); } } @Test - public void testGetModuleKeyTypeEncryptedKey() + public void testGetModuleKeyTypeHardcodedKey() { - String csrfChallengeThree = new String("5ca9115f3279b9b9f3308eb6a59a4fcd374846d6"); - if(!Getter.getModuleKeyType(applicationRoot, csrfChallengeThree)) + String insecureCryptoLesson = new String("201ae6f8c55ba3f3b5881806387fbf34b15c30c2"); + if(Getter.getModuleKeyType(applicationRoot, insecureCryptoLesson)) { - log.debug("PASS: Encrypted Key Detected on Encrypted Level"); + log.debug("PASS: Hardcoded Key Detected on Hardcoded Level"); } else { - log.fatal("Hardcoded Key Detected On Encrypted Key Module"); - fail("Hardcoded Key Detected On Encrypted Key Module"); + log.fatal("Encrypted Key Detected On Hardcoded Key Module"); + fail("Encrypted Key Detected On Hardcoded Key Module"); } } - + /** * Test to return stored result key from DB via getModuleResult Function */ @@ -2237,7 +2141,7 @@ else if(!modules.startsWith("