[libowasp-esapi-java] 01/03: Imported Upstream version 2.1.0

Matthew Vernon matthew at moszumanska.debian.org
Fri Mar 21 17:45:54 UTC 2014


This is an automated email from the git hooks/post-receive script.

matthew pushed a commit to branch master
in repository libowasp-esapi-java.

commit 1ff1e7be027cb4adc36e31a616dcd6ccb5d65b32
Author: Matthew Vernon <mcv21 at cam.ac.uk>
Date:   Fri Mar 21 17:31:07 2014 +0000

    Imported Upstream version 2.1.0
---
 .classpath                                         |   10 +
 .project                                           |   42 +
 .settings/.svn/all-wcprops                         |   35 +
 .settings/.svn/entries                             |  198 +++
 .../org.eclipse.core.resources.prefs.svn-base      |    3 +
 .../text-base/org.eclipse.jdt.core.prefs.svn-base  |    6 +
 .../org.eclipse.wst.common.component.svn-base      |   10 +
 ...ipse.wst.common.project.facet.core.xml.svn-base |    6 +
 .../text-base/org.maven.ide.eclipse.prefs.svn-base |    9 +
 .settings/org.eclipse.core.resources.prefs         |    3 +
 .settings/org.eclipse.jdt.core.prefs               |    6 +
 .settings/org.eclipse.wst.common.component         |   10 +
 .../org.eclipse.wst.common.project.facet.core.xml  |    6 +
 .settings/org.maven.ide.eclipse.prefs              |    9 +
 .svn/all-wcprops                                   |   59 +
 .svn/dir-prop-base                                 |   20 +
 .svn/entries                                       |  352 ++++
 .svn/text-base/.classpath.svn-base                 |   10 +
 .svn/text-base/.project.svn-base                   |   42 +
 .svn/text-base/LICENSE-CONTENT.svn-base            |   78 +
 .svn/text-base/LICENSE-README.svn-base             |    7 +
 .svn/text-base/LICENSE.svn-base                    |   12 +
 .svn/text-base/ant-javadoc.xml.svn-base            |    8 +
 .svn/text-base/esapi.iml.svn-base                  |   48 +
 .svn/text-base/javadoc.xml.svn-base                |    6 +
 .svn/text-base/pom.xml.svn-base                    |  504 ++++++
 LICENSE                                            |   12 +
 LICENSE-CONTENT                                    |   78 +
 LICENSE-README                                     |    7 +
 ant-javadoc.xml                                    |    8 +
 configuration/.svn/all-wcprops                     |   17 +
 configuration/.svn/entries                         |  105 ++
 configuration/.svn/text-base/log4j.dtd.svn-base    |  227 +++
 configuration/.svn/text-base/log4j.xml.svn-base    |   47 +
 configuration/META-INF/.svn/all-wcprops            |   11 +
 configuration/META-INF/.svn/entries                |   62 +
 .../META-INF/.svn/text-base/esapi.tld.svn-base     |  371 ++++
 configuration/META-INF/esapi.tld                   |  371 ++++
 configuration/esapi/.svn/all-wcprops               |   41 +
 configuration/esapi/.svn/entries                   |  235 +++
 .../esapi/.svn/prop-base/waf-policy.xsd.svn-base   |    5 +
 .../ESAPI-AccessControlPolicy.xml.svn-base         |  127 ++
 .../esapi/.svn/text-base/ESAPI.properties.svn-base |  452 +++++
 .../.svn/text-base/antisamy-esapi.xml.svn-base     |  492 ++++++
 .../esapi/.svn/text-base/users.txt.svn-base        |    0
 .../.svn/text-base/validation.properties.svn-base  |   29 +
 .../esapi/.svn/text-base/waf-policy.xsd.svn-base   |  Bin 0 -> 20582 bytes
 configuration/esapi/ESAPI-AccessControlPolicy.xml  |  127 ++
 configuration/esapi/ESAPI.properties               |  452 +++++
 configuration/esapi/antisamy-esapi.xml             |  492 ++++++
 configuration/esapi/users.txt                      |    0
 configuration/esapi/validation.properties          |   29 +
 configuration/esapi/waf-policies/.svn/all-wcprops  |  107 ++
 configuration/esapi/waf-policies/.svn/entries      |  606 +++++++
 .../.svn/text-base/add-header-policy.xml.svn-base  |   29 +
 .../text-base/add-httponly-policy.xml.svn-base     |   27 +
 .../.svn/text-base/add-secure-policy.xml.svn-base  |   27 +
 .../text-base/authentication-policy.xml.svn-base   |   30 +
 .../.svn/text-base/bean-shell-policy.xml.svn-base  |   30 +
 .../.svn/text-base/bean-shell-rule.bsh.svn-base    |    5 +
 .../text-base/detect-outbound-policy.xml.svn-base  |   27 +
 .../dynamic-insertion-policy.xml.svn-base          |   27 +
 .../text-base/enforce-https-policy.xml.svn-base    |   28 +
 .../.svn/text-base/must-match-policy.xml.svn-base  |   35 +
 .../text-base/replace-outbound-policy.xml.svn-base |   27 +
 .../restrict-content-type-policy.xml.svn-base      |   27 +
 .../restrict-extension-policy.xml.svn-base         |   25 +
 .../text-base/restrict-method-policy.xml.svn-base  |   25 +
 .../restrict-source-ip-policy.xml.svn-base         |   27 +
 .../restrict-user-agent-policy.xml.svn-base        |   26 +
 .../text-base/virtual-patch-policy.xml.svn-base    |   27 +
 .../esapi/waf-policies/add-header-policy.xml       |   29 +
 .../esapi/waf-policies/add-httponly-policy.xml     |   27 +
 .../esapi/waf-policies/add-secure-policy.xml       |   27 +
 .../esapi/waf-policies/authentication-policy.xml   |   30 +
 .../esapi/waf-policies/bean-shell-policy.xml       |   30 +
 .../esapi/waf-policies/bean-shell-rule.bsh         |    5 +
 .../esapi/waf-policies/detect-outbound-policy.xml  |   27 +
 .../waf-policies/dynamic-insertion-policy.xml      |   27 +
 .../esapi/waf-policies/enforce-https-policy.xml    |   28 +
 .../esapi/waf-policies/must-match-policy.xml       |   35 +
 .../esapi/waf-policies/replace-outbound-policy.xml |   27 +
 .../waf-policies/restrict-content-type-policy.xml  |   27 +
 .../waf-policies/restrict-extension-policy.xml     |   25 +
 .../esapi/waf-policies/restrict-method-policy.xml  |   25 +
 .../waf-policies/restrict-source-ip-policy.xml     |   27 +
 .../waf-policies/restrict-user-agent-policy.xml    |   26 +
 .../esapi/waf-policies/virtual-patch-policy.xml    |   27 +
 configuration/esapi/waf-policy.xsd                 |  Bin 0 -> 20582 bytes
 configuration/log4j.dtd                            |  227 +++
 configuration/log4j.xml                            |   47 +
 configuration/properties/.svn/all-wcprops          |   23 +
 configuration/properties/.svn/entries              |  130 ++
 .../.svn/text-base/ESAPI_en_US.properties.svn-base |    9 +
 .../.svn/text-base/ESAPI_fr_FR.properties.svn-base |    9 +
 .../text-base/ESAPI_zhs_CN.properties.svn-base     |    1 +
 configuration/properties/ESAPI_en_US.properties    |    9 +
 configuration/properties/ESAPI_fr_FR.properties    |    9 +
 configuration/properties/ESAPI_zhs_CN.properties   |    1 +
 documentation/.svn/all-wcprops                     |  143 ++
 documentation/.svn/entries                         |  810 +++++++++
 .../Analysis-of-ESAPI-2.0-KDF.odt.svn-base         |    5 +
 .../Analysis-of-ESAPI-2.0-KDF.pdf.svn-base         |    5 +
 documentation/.svn/prop-base/cc.JPG.svn-base       |    5 +
 .../esapi4java-2.0-javadoc-pictures.pptx.svn-base  |    5 +
 .../prop-base/esapi4java-big-duke.JPG.svn-base     |    5 +
 ...-core-2.0-ciphertext-serialization.pdf.svn-base |    5 +
 ...-core-2.0-ciphertext-serialization.xls.svn-base |    5 +
 ...4java-core-2.0-crypto-design-goals.doc.svn-base |    5 +
 .../esapi4java-core-2.0-install-guide.doc.svn-base |    5 +
 ...va-core-2.0-readme-crypto-changes.html.svn-base |    5 +
 .../esapi4java-core-2.0-release-notes.doc.svn-base |    5 +
 .../esapi4java-core-2.0-release-notes.pdf.svn-base |    5 +
 ...e-2.0-symmetric-crypto-user-guide.html.svn-base |    5 +
 .../.svn/prop-base/esapi4java-disa.JPG.svn-base    |    5 +
 .../prop-base/esapi4java-google-code.JPG.svn-base  |    5 +
 .../prop-base/esapi4java-google-code.doc.svn-base  |    5 +
 ...api4java-waf-2.0-policy-file-spec.docx.svn-base |    5 +
 ...sapi4java-waf-2.0-policy-file-spec.pdf.svn-base |    5 +
 .../.svn/prop-base/esapi4java-waf.JPG.svn-base     |    5 +
 .../Analysis-of-ESAPI-2.0-KDF.odt.svn-base         |  Bin 0 -> 22536 bytes
 .../Analysis-of-ESAPI-2.0-KDF.pdf.svn-base         |  Bin 0 -> 313772 bytes
 documentation/.svn/text-base/cc.JPG.svn-base       |  Bin 0 -> 4578 bytes
 .../esapi4java-2.0-javadoc-pictures.pptx.svn-base  |  Bin 0 -> 130846 bytes
 .../text-base/esapi4java-2.0-readme.txt.svn-base   |   60 +
 ....0rc6-override-log4jloggingfactory.txt.svn-base |   71 +
 .../text-base/esapi4java-big-duke.JPG.svn-base     |  Bin 0 -> 8350 bytes
 ...-core-2.0-ciphertext-serialization.pdf.svn-base |  Bin 0 -> 136825 bytes
 ...-core-2.0-ciphertext-serialization.xls.svn-base |  Bin 0 -> 16384 bytes
 ...4java-core-2.0-crypto-design-goals.doc.svn-base |  Bin 0 -> 119808 bytes
 .../esapi4java-core-2.0-install-guide.doc.svn-base |  Bin 0 -> 278016 bytes
 .../esapi4java-core-2.0-install-guide.pdf.svn-base |  Bin 0 -> 582255 bytes
 ...va-core-2.0-readme-crypto-changes.html.svn-base |  440 +++++
 .../esapi4java-core-2.0-release-notes.doc.svn-base |  Bin 0 -> 283648 bytes
 .../esapi4java-core-2.0-release-notes.pdf.svn-base |  Bin 0 -> 531816 bytes
 ...e-2.0-symmetric-crypto-user-guide.html.svn-base |  796 +++++++++
 .../esapi4java-core-2.1-release-notes.txt.svn-base |   68 +
 .../.svn/text-base/esapi4java-disa.JPG.svn-base    |  Bin 0 -> 47448 bytes
 .../text-base/esapi4java-google-code.JPG.svn-base  |  Bin 0 -> 2004 bytes
 .../text-base/esapi4java-google-code.doc.svn-base  |  Bin 0 -> 141824 bytes
 ...api4java-waf-2.0-policy-file-spec.docx.svn-base |  Bin 0 -> 216462 bytes
 ...sapi4java-waf-2.0-policy-file-spec.pdf.svn-base |  Bin 0 -> 758388 bytes
 .../.svn/text-base/esapi4java-waf.JPG.svn-base     |  Bin 0 -> 31960 bytes
 documentation/Analysis-of-ESAPI-2.0-KDF.odt        |  Bin 0 -> 22536 bytes
 documentation/Analysis-of-ESAPI-2.0-KDF.pdf        |  Bin 0 -> 313772 bytes
 documentation/cc.JPG                               |  Bin 0 -> 4578 bytes
 documentation/esapi4java-2.0-javadoc-pictures.pptx |  Bin 0 -> 130846 bytes
 documentation/esapi4java-2.0-readme.txt            |   60 +
 ...pi4java-2.0rc6-override-log4jloggingfactory.txt |   71 +
 documentation/esapi4java-big-duke.JPG              |  Bin 0 -> 8350 bytes
 ...sapi4java-core-2.0-ciphertext-serialization.pdf |  Bin 0 -> 136825 bytes
 ...sapi4java-core-2.0-ciphertext-serialization.xls |  Bin 0 -> 16384 bytes
 .../esapi4java-core-2.0-crypto-design-goals.doc    |  Bin 0 -> 119808 bytes
 .../esapi4java-core-2.0-install-guide.doc          |  Bin 0 -> 278016 bytes
 .../esapi4java-core-2.0-install-guide.pdf          |  Bin 0 -> 582255 bytes
 .../esapi4java-core-2.0-readme-crypto-changes.html |  440 +++++
 .../esapi4java-core-2.0-release-notes.doc          |  Bin 0 -> 283648 bytes
 .../esapi4java-core-2.0-release-notes.pdf          |  Bin 0 -> 531816 bytes
 ...4java-core-2.0-symmetric-crypto-user-guide.html |  796 +++++++++
 .../esapi4java-core-2.1-release-notes.txt          |   68 +
 documentation/esapi4java-disa.JPG                  |  Bin 0 -> 47448 bytes
 documentation/esapi4java-google-code.JPG           |  Bin 0 -> 2004 bytes
 documentation/esapi4java-google-code.doc           |  Bin 0 -> 141824 bytes
 .../esapi4java-waf-2.0-policy-file-spec.docx       |  Bin 0 -> 216462 bytes
 .../esapi4java-waf-2.0-policy-file-spec.pdf        |  Bin 0 -> 758388 bytes
 documentation/esapi4java-waf.JPG                   |  Bin 0 -> 31960 bytes
 esapi.iml                                          |   48 +
 javadoc.xml                                        |    6 +
 pom.xml                                            |  504 ++++++
 resources/.svn/all-wcprops                         |   17 +
 resources/.svn/entries                             |   96 +
 .../.svn/prop-base/owasp-esapi-dev.jks.svn-base    |    5 +
 .../.svn/text-base/owasp-esapi-dev.jks.svn-base    |  Bin 0 -> 1239 bytes
 resources/.svn/text-base/settings.xml.svn-base     |   17 +
 resources/owasp-esapi-dev.jks                      |  Bin 0 -> 1239 bytes
 resources/settings.xml                             |   17 +
 src/.svn/all-wcprops                               |    5 +
 src/.svn/entries                                   |   40 +
 src/examples/.svn/all-wcprops                      |   11 +
 src/examples/.svn/entries                          |   68 +
 src/examples/.svn/text-base/README.svn-base        |   37 +
 src/examples/README                                |   37 +
 src/examples/java/.svn/all-wcprops                 |   23 +
 src/examples/java/.svn/entries                     |  130 ++
 .../DisplayEncryptedProperties.java.svn-base       |    5 +
 .../java/.svn/prop-base/ESAPILogging.java.svn-base |    5 +
 .../prop-base/PersistedEncryptedData.java.svn-base |    5 +
 .../DisplayEncryptedProperties.java.svn-base       |   79 +
 .../java/.svn/text-base/ESAPILogging.java.svn-base |   23 +
 .../text-base/PersistedEncryptedData.java.svn-base |  185 ++
 src/examples/java/DisplayEncryptedProperties.java  |   79 +
 src/examples/java/ESAPILogging.java                |   23 +
 src/examples/java/PersistedEncryptedData.java      |  185 ++
 src/examples/scripts/.svn/all-wcprops              |   59 +
 src/examples/scripts/.svn/entries                  |  334 ++++
 .../scripts/.svn/prop-base/compile.sh.svn-base     |    5 +
 .../.svn/prop-base/encryptProperties.sh.svn-base   |    5 +
 .../.svn/prop-base/encrypted.properties.svn-base   |    5 +
 .../scripts/.svn/prop-base/findjar.sh.svn-base     |    5 +
 .../prop-base/persistEncryptedData.sh.svn-base     |    5 +
 .../scripts/.svn/prop-base/runClass.sh.svn-base    |    5 +
 .../.svn/prop-base/setMasterKey.sh.svn-base        |    5 +
 .../scripts/.svn/prop-base/setenv-svn.sh.svn-base  |    5 +
 .../scripts/.svn/prop-base/setenv-zip.sh.svn-base  |    5 +
 .../scripts/.svn/text-base/compile.sh.svn-base     |   14 +
 .../.svn/text-base/encryptProperties.sh.svn-base   |   73 +
 .../.svn/text-base/encrypted.properties.svn-base   |    6 +
 .../scripts/.svn/text-base/findjar.sh.svn-base     |   29 +
 .../text-base/persistEncryptedData.sh.svn-base     |   29 +
 .../scripts/.svn/text-base/runClass.sh.svn-base    |   28 +
 .../.svn/text-base/setMasterKey.sh.svn-base        |   22 +
 .../scripts/.svn/text-base/setenv-svn.sh.svn-base  |   52 +
 .../scripts/.svn/text-base/setenv-zip.sh.svn-base  |   50 +
 src/examples/scripts/compile.sh                    |   14 +
 src/examples/scripts/encryptProperties.sh          |   73 +
 src/examples/scripts/encrypted.properties          |    6 +
 src/examples/scripts/findjar.sh                    |   29 +
 src/examples/scripts/persistEncryptedData.sh       |   29 +
 src/examples/scripts/runClass.sh                   |   28 +
 src/examples/scripts/setMasterKey.sh               |   22 +
 src/examples/scripts/setenv-svn.sh                 |   52 +
 src/examples/scripts/setenv-zip.sh                 |   50 +
 src/main/.svn/all-wcprops                          |    5 +
 src/main/.svn/entries                              |   34 +
 src/main/assembly/.svn/all-wcprops                 |   11 +
 src/main/assembly/.svn/entries                     |   62 +
 src/main/assembly/.svn/text-base/dist.xml.svn-base |   55 +
 src/main/assembly/dist.xml                         |   55 +
 src/main/java/.svn/all-wcprops                     |    5 +
 src/main/java/.svn/entries                         |   34 +
 src/main/java/META-INF/.svn/all-wcprops            |   11 +
 src/main/java/META-INF/.svn/entries                |   62 +
 .../META-INF/.svn/text-base/MANIFEST.MF.svn-base   |    3 +
 src/main/java/META-INF/MANIFEST.MF                 |    3 +
 src/main/java/org/.svn/all-wcprops                 |    5 +
 src/main/java/org/.svn/entries                     |   31 +
 src/main/java/org/owasp/.svn/all-wcprops           |    5 +
 src/main/java/org/owasp/.svn/entries               |   31 +
 src/main/java/org/owasp/esapi/.svn/all-wcprops     |  155 ++
 src/main/java/org/owasp/esapi/.svn/entries         |  905 ++++++++++
 .../.svn/text-base/AccessControlRule.java.svn-base |    8 +
 .../.svn/text-base/AccessController.java.svn-base  |  351 ++++
 .../text-base/AccessReferenceMap.java.svn-base     |  176 ++
 .../.svn/text-base/Authenticator.java.svn-base     |  324 ++++
 .../owasp/esapi/.svn/text-base/ESAPI.java.svn-base |  224 +++
 .../esapi/.svn/text-base/Encoder.java.svn-base     |  516 ++++++
 .../.svn/text-base/EncoderConstants.java.svn-base  |  117 ++
 .../text-base/EncryptedProperties.java.svn-base    |  112 ++
 .../esapi/.svn/text-base/Encryptor.java.svn-base   |  318 ++++
 .../.svn/text-base/ExecuteResult.java.svn-base     |   75 +
 .../esapi/.svn/text-base/Executor.java.svn-base    |   78 +
 .../.svn/text-base/HTTPUtilities.java.svn-base     |  649 +++++++
 .../.svn/text-base/IntrusionDetector.java.svn-base |   63 +
 .../esapi/.svn/text-base/LogFactory.java.svn-base  |   60 +
 .../esapi/.svn/text-base/Logger.java.svn-base      |  423 +++++
 .../.svn/text-base/PreparedString.java.svn-base    |  139 ++
 .../esapi/.svn/text-base/Randomizer.java.svn-base  |  148 ++
 .../esapi/.svn/text-base/SafeFile.java.svn-base    |  107 ++
 .../text-base/SecurityConfiguration.java.svn-base  |  682 +++++++
 .../.svn/text-base/StringUtilities.java.svn-base   |  199 +++
 .../owasp/esapi/.svn/text-base/User.java.svn-base  |  760 ++++++++
 .../text-base/ValidationErrorList.java.svn-base    |  137 ++
 .../.svn/text-base/ValidationRule.java.svn-base    |   81 +
 .../esapi/.svn/text-base/Validator.java.svn-base   |  691 ++++++++
 .../esapi/.svn/text-base/package.html.svn-base     |   85 +
 .../java/org/owasp/esapi/AccessControlRule.java    |    8 +
 .../java/org/owasp/esapi/AccessController.java     |  351 ++++
 .../java/org/owasp/esapi/AccessReferenceMap.java   |  176 ++
 src/main/java/org/owasp/esapi/Authenticator.java   |  324 ++++
 src/main/java/org/owasp/esapi/ESAPI.java           |  224 +++
 src/main/java/org/owasp/esapi/Encoder.java         |  516 ++++++
 .../java/org/owasp/esapi/EncoderConstants.java     |  117 ++
 .../java/org/owasp/esapi/EncryptedProperties.java  |  112 ++
 src/main/java/org/owasp/esapi/Encryptor.java       |  318 ++++
 src/main/java/org/owasp/esapi/ExecuteResult.java   |   75 +
 src/main/java/org/owasp/esapi/Executor.java        |   78 +
 src/main/java/org/owasp/esapi/HTTPUtilities.java   |  649 +++++++
 .../java/org/owasp/esapi/IntrusionDetector.java    |   63 +
 src/main/java/org/owasp/esapi/LogFactory.java      |   60 +
 src/main/java/org/owasp/esapi/Logger.java          |  423 +++++
 src/main/java/org/owasp/esapi/PreparedString.java  |  139 ++
 src/main/java/org/owasp/esapi/Randomizer.java      |  148 ++
 src/main/java/org/owasp/esapi/SafeFile.java        |  107 ++
 .../org/owasp/esapi/SecurityConfiguration.java     |  682 +++++++
 src/main/java/org/owasp/esapi/StringUtilities.java |  199 +++
 src/main/java/org/owasp/esapi/User.java            |  760 ++++++++
 .../java/org/owasp/esapi/ValidationErrorList.java  |  137 ++
 src/main/java/org/owasp/esapi/ValidationRule.java  |   81 +
 src/main/java/org/owasp/esapi/Validator.java       |  691 ++++++++
 .../java/org/owasp/esapi/codecs/.svn/all-wcprops   |  113 ++
 src/main/java/org/owasp/esapi/codecs/.svn/entries  |  640 +++++++
 .../esapi/codecs/.svn/prop-base/Hex.java.svn-base  |    5 +
 .../codecs/.svn/text-base/Base64.java.svn-base     | 1855 ++++++++++++++++++++
 .../codecs/.svn/text-base/CSSCodec.java.svn-base   |  181 ++
 .../codecs/.svn/text-base/Codec.java.svn-base      |  159 ++
 .../codecs/.svn/text-base/DB2Codec.java.svn-base   |   68 +
 .../.svn/text-base/HTMLEntityCodec.java.svn-base   |  550 ++++++
 .../codecs/.svn/text-base/HashTrie.java.svn-base   |  612 +++++++
 .../esapi/codecs/.svn/text-base/Hex.java.svn-base  |   84 +
 .../.svn/text-base/JavaScriptCodec.java.svn-base   |  217 +++
 .../codecs/.svn/text-base/MySQLCodec.java.svn-base |  264 +++
 .../.svn/text-base/OracleCodec.java.svn-base       |   90 +
 .../.svn/text-base/PercentCodec.java.svn-base      |  154 ++
 .../.svn/text-base/PushbackString.java.svn-base    |  185 ++
 .../esapi/codecs/.svn/text-base/Trie.java.svn-base |  172 ++
 .../codecs/.svn/text-base/UnixCodec.java.svn-base  |   82 +
 .../.svn/text-base/VBScriptCodec.java.svn-base     |  117 ++
 .../.svn/text-base/WindowsCodec.java.svn-base      |   82 +
 .../.svn/text-base/XMLEntityCodec.java.svn-base    |  298 ++++
 .../codecs/.svn/text-base/package.html.svn-base    |   19 +
 src/main/java/org/owasp/esapi/codecs/Base64.java   | 1855 ++++++++++++++++++++
 src/main/java/org/owasp/esapi/codecs/CSSCodec.java |  181 ++
 src/main/java/org/owasp/esapi/codecs/Codec.java    |  159 ++
 src/main/java/org/owasp/esapi/codecs/DB2Codec.java |   68 +
 .../org/owasp/esapi/codecs/HTMLEntityCodec.java    |  550 ++++++
 src/main/java/org/owasp/esapi/codecs/HashTrie.java |  612 +++++++
 src/main/java/org/owasp/esapi/codecs/Hex.java      |   84 +
 .../org/owasp/esapi/codecs/JavaScriptCodec.java    |  217 +++
 .../java/org/owasp/esapi/codecs/MySQLCodec.java    |  264 +++
 .../java/org/owasp/esapi/codecs/OracleCodec.java   |   90 +
 .../java/org/owasp/esapi/codecs/PercentCodec.java  |  154 ++
 .../org/owasp/esapi/codecs/PushbackString.java     |  185 ++
 src/main/java/org/owasp/esapi/codecs/Trie.java     |  172 ++
 .../java/org/owasp/esapi/codecs/UnixCodec.java     |   82 +
 .../java/org/owasp/esapi/codecs/VBScriptCodec.java |  117 ++
 .../java/org/owasp/esapi/codecs/WindowsCodec.java  |   82 +
 .../org/owasp/esapi/codecs/XMLEntityCodec.java     |  298 ++++
 src/main/java/org/owasp/esapi/codecs/package.html  |   19 +
 .../java/org/owasp/esapi/crypto/.svn/all-wcprops   |   65 +
 src/main/java/org/owasp/esapi/crypto/.svn/entries  |  368 ++++
 .../crypto/.svn/prop-base/CipherSpec.java.svn-base |    5 +
 .../prop-base/CipherTextSerializer.java.svn-base   |    5 +
 .../.svn/prop-base/CryptoHelper.java.svn-base      |    5 +
 .../.svn/prop-base/CryptoToken.java.svn-base       |    5 +
 .../prop-base/KeyDerivationFunction.java.svn-base  |    5 +
 .../crypto/.svn/prop-base/PlainText.java.svn-base  |    5 +
 .../prop-base/SecurityProviderLoader.java.svn-base |    5 +
 .../crypto/.svn/prop-base/package.html.svn-base    |    5 +
 .../crypto/.svn/text-base/CipherSpec.java.svn-base |  388 ++++
 .../crypto/.svn/text-base/CipherText.java.svn-base |  873 +++++++++
 .../text-base/CipherTextSerializer.java.svn-base   |  415 +++++
 .../.svn/text-base/CryptoDiscoverer.java.svn-base  |   75 +
 .../.svn/text-base/CryptoHelper.java.svn-base      |  395 +++++
 .../.svn/text-base/CryptoToken.java.svn-base       |  722 ++++++++
 .../text-base/KeyDerivationFunction.java.svn-base  |  479 +++++
 .../crypto/.svn/text-base/PlainText.java.svn-base  |  159 ++
 .../text-base/SecurityProviderLoader.java.svn-base |  284 +++
 .../crypto/.svn/text-base/package.html.svn-base    |   10 +
 .../java/org/owasp/esapi/crypto/CipherSpec.java    |  388 ++++
 .../java/org/owasp/esapi/crypto/CipherText.java    |  873 +++++++++
 .../owasp/esapi/crypto/CipherTextSerializer.java   |  415 +++++
 .../org/owasp/esapi/crypto/CryptoDiscoverer.java   |   75 +
 .../java/org/owasp/esapi/crypto/CryptoHelper.java  |  395 +++++
 .../java/org/owasp/esapi/crypto/CryptoToken.java   |  722 ++++++++
 .../owasp/esapi/crypto/KeyDerivationFunction.java  |  479 +++++
 .../java/org/owasp/esapi/crypto/PlainText.java     |  159 ++
 .../owasp/esapi/crypto/SecurityProviderLoader.java |  284 +++
 src/main/java/org/owasp/esapi/crypto/package.html  |   10 +
 .../org/owasp/esapi/doc-files/.svn/all-wcprops     |   59 +
 .../java/org/owasp/esapi/doc-files/.svn/entries    |  334 ++++
 .../.svn/prop-base/AccessController.jpg.svn-base   |    5 +
 .../.svn/prop-base/AccessReferenceMap.jpg.svn-base |    5 +
 .../.svn/prop-base/Architecture.jpg.svn-base       |    5 +
 .../.svn/prop-base/Authenticator.jpg.svn-base      |    5 +
 .../.svn/prop-base/Encryptor.jpg.svn-base          |    5 +
 .../.svn/prop-base/HTTPUtilities.jpg.svn-base      |    5 +
 .../.svn/prop-base/IntrusionDetector.jpg.svn-base  |    5 +
 .../.svn/prop-base/OWASPTopTen.jpg.svn-base        |    5 +
 .../.svn/prop-base/Validator.jpg.svn-base          |    5 +
 .../.svn/text-base/AccessController.jpg.svn-base   |  Bin 0 -> 52532 bytes
 .../.svn/text-base/AccessReferenceMap.jpg.svn-base |  Bin 0 -> 48956 bytes
 .../.svn/text-base/Architecture.jpg.svn-base       |  Bin 0 -> 77232 bytes
 .../.svn/text-base/Authenticator.jpg.svn-base      |  Bin 0 -> 49988 bytes
 .../.svn/text-base/Encryptor.jpg.svn-base          |  Bin 0 -> 51236 bytes
 .../.svn/text-base/HTTPUtilities.jpg.svn-base      |  Bin 0 -> 36727 bytes
 .../.svn/text-base/IntrusionDetector.jpg.svn-base  |  Bin 0 -> 39551 bytes
 .../.svn/text-base/OWASPTopTen.jpg.svn-base        |  Bin 0 -> 96552 bytes
 .../.svn/text-base/Validator.jpg.svn-base          |  Bin 0 -> 45676 bytes
 .../org/owasp/esapi/doc-files/AccessController.jpg |  Bin 0 -> 52532 bytes
 .../owasp/esapi/doc-files/AccessReferenceMap.jpg   |  Bin 0 -> 48956 bytes
 .../org/owasp/esapi/doc-files/Architecture.jpg     |  Bin 0 -> 77232 bytes
 .../org/owasp/esapi/doc-files/Authenticator.jpg    |  Bin 0 -> 49988 bytes
 .../java/org/owasp/esapi/doc-files/Encryptor.jpg   |  Bin 0 -> 51236 bytes
 .../org/owasp/esapi/doc-files/HTTPUtilities.jpg    |  Bin 0 -> 36727 bytes
 .../owasp/esapi/doc-files/IntrusionDetector.jpg    |  Bin 0 -> 39551 bytes
 .../java/org/owasp/esapi/doc-files/OWASPTopTen.jpg |  Bin 0 -> 96552 bytes
 .../java/org/owasp/esapi/doc-files/Validator.jpg   |  Bin 0 -> 45676 bytes
 .../java/org/owasp/esapi/errors/.svn/all-wcprops   |  131 ++
 src/main/java/org/owasp/esapi/errors/.svn/entries  |  742 ++++++++
 .../text-base/AccessControlException.java.svn-base |   57 +
 .../AuthenticationAccountsException.java.svn-base  |   59 +
 ...uthenticationCredentialsException.java.svn-base |   58 +
 .../AuthenticationException.java.svn-base          |   58 +
 .../AuthenticationHostException.java.svn-base      |   57 +
 .../AuthenticationLoginException.java.svn-base     |   58 +
 .../text-base/AvailabilityException.java.svn-base  |   57 +
 .../text-base/CertificateException.java.svn-base   |   56 +
 .../text-base/ConfigurationException.java.svn-base |   34 +
 .../.svn/text-base/EncodingException.java.svn-base |   62 +
 .../text-base/EncryptionException.java.svn-base    |   61 +
 .../EncryptionRuntimeException.java.svn-base       |   63 +
 .../EnterpriseSecurityException.java.svn-base      |  124 ++
 ...nterpriseSecurityRuntimeException.java.svn-base |  126 ++
 .../.svn/text-base/ExecutorException.java.svn-base |   62 +
 .../text-base/IntegrityException.java.svn-base     |   62 +
 .../text-base/IntrusionException.java.svn-base     |   92 +
 .../ValidationAvailabilityException.java.svn-base  |   57 +
 .../text-base/ValidationException.java.svn-base    |  115 ++
 .../ValidationUploadException.java.svn-base        |   59 +
 .../errors/.svn/text-base/package.html.svn-base    |   17 +
 .../owasp/esapi/errors/AccessControlException.java |   57 +
 .../errors/AuthenticationAccountsException.java    |   59 +
 .../errors/AuthenticationCredentialsException.java |   58 +
 .../esapi/errors/AuthenticationException.java      |   58 +
 .../esapi/errors/AuthenticationHostException.java  |   57 +
 .../esapi/errors/AuthenticationLoginException.java |   58 +
 .../owasp/esapi/errors/AvailabilityException.java  |   57 +
 .../owasp/esapi/errors/CertificateException.java   |   56 +
 .../owasp/esapi/errors/ConfigurationException.java |   34 +
 .../org/owasp/esapi/errors/EncodingException.java  |   62 +
 .../owasp/esapi/errors/EncryptionException.java    |   61 +
 .../esapi/errors/EncryptionRuntimeException.java   |   63 +
 .../esapi/errors/EnterpriseSecurityException.java  |  124 ++
 .../errors/EnterpriseSecurityRuntimeException.java |  126 ++
 .../org/owasp/esapi/errors/ExecutorException.java  |   62 +
 .../org/owasp/esapi/errors/IntegrityException.java |   62 +
 .../org/owasp/esapi/errors/IntrusionException.java |   92 +
 .../errors/ValidationAvailabilityException.java    |   57 +
 .../owasp/esapi/errors/ValidationException.java    |  115 ++
 .../esapi/errors/ValidationUploadException.java    |   59 +
 src/main/java/org/owasp/esapi/errors/package.html  |   17 +
 .../java/org/owasp/esapi/filters/.svn/all-wcprops  |   47 +
 src/main/java/org/owasp/esapi/filters/.svn/entries |  266 +++
 .../.svn/text-base/ClickjackFilter.java.svn-base   |  108 ++
 .../.svn/text-base/ESAPIFilter.java.svn-base       |  141 ++
 .../RequestRateThrottleFilter.java.svn-base        |  114 ++
 .../.svn/text-base/SecurityWrapper.java.svn-base   |  136 ++
 .../text-base/SecurityWrapperRequest.java.svn-base |  836 +++++++++
 .../SecurityWrapperResponse.java.svn-base          |  513 ++++++
 .../filters/.svn/text-base/package.html.svn-base   |   13 +
 .../org/owasp/esapi/filters/ClickjackFilter.java   |  108 ++
 .../java/org/owasp/esapi/filters/ESAPIFilter.java  |  141 ++
 .../esapi/filters/RequestRateThrottleFilter.java   |  114 ++
 .../org/owasp/esapi/filters/SecurityWrapper.java   |  136 ++
 .../esapi/filters/SecurityWrapperRequest.java      |  836 +++++++++
 .../esapi/filters/SecurityWrapperResponse.java     |  513 ++++++
 src/main/java/org/owasp/esapi/filters/package.html |   13 +
 src/main/java/org/owasp/esapi/package.html         |   85 +
 .../org/owasp/esapi/reference/.svn/all-wcprops     |  119 ++
 .../java/org/owasp/esapi/reference/.svn/entries    |  683 +++++++
 .../AbstractAccessReferenceMap.java.svn-base       |  204 +++
 .../text-base/AbstractAuthenticator.java.svn-base  |  297 ++++
 .../DefaultAccessController.java.svn-base          |  146 ++
 .../.svn/text-base/DefaultEncoder.java.svn-base    |  448 +++++
 .../.svn/text-base/DefaultExecutor.java.svn-base   |  234 +++
 .../text-base/DefaultHTTPUtilities.java.svn-base   | 1022 +++++++++++
 .../DefaultIntrusionDetector.java.svn-base         |  193 ++
 .../.svn/text-base/DefaultRandomizer.java.svn-base |  134 ++
 .../DefaultSecurityConfiguration.java.svn-base     | 1224 +++++++++++++
 .../.svn/text-base/DefaultUser.java.svn-base       |  614 +++++++
 .../.svn/text-base/DefaultValidator.java.svn-base  | 1194 +++++++++++++
 .../text-base/FileBasedAuthenticator.java.svn-base |  725 ++++++++
 .../IntegerAccessReferenceMap.java.svn-base        |   80 +
 .../.svn/text-base/JavaLogFactory.java.svn-base    |  409 +++++
 .../.svn/text-base/Log4JLogFactory.java.svn-base   |   91 +
 .../.svn/text-base/Log4JLogger.java.svn-base       |  524 ++++++
 .../text-base/Log4JLoggerFactory.java.svn-base     |   47 +
 .../RandomAccessReferenceMap.java.svn-base         |   85 +
 .../reference/.svn/text-base/package.html.svn-base |   16 +
 .../reference/AbstractAccessReferenceMap.java      |  204 +++
 .../esapi/reference/AbstractAuthenticator.java     |  297 ++++
 .../esapi/reference/DefaultAccessController.java   |  146 ++
 .../org/owasp/esapi/reference/DefaultEncoder.java  |  448 +++++
 .../org/owasp/esapi/reference/DefaultExecutor.java |  234 +++
 .../esapi/reference/DefaultHTTPUtilities.java      | 1022 +++++++++++
 .../esapi/reference/DefaultIntrusionDetector.java  |  193 ++
 .../owasp/esapi/reference/DefaultRandomizer.java   |  134 ++
 .../reference/DefaultSecurityConfiguration.java    | 1224 +++++++++++++
 .../org/owasp/esapi/reference/DefaultUser.java     |  614 +++++++
 .../owasp/esapi/reference/DefaultValidator.java    | 1194 +++++++++++++
 .../esapi/reference/FileBasedAuthenticator.java    |  725 ++++++++
 .../esapi/reference/IntegerAccessReferenceMap.java |   80 +
 .../org/owasp/esapi/reference/JavaLogFactory.java  |  409 +++++
 .../org/owasp/esapi/reference/Log4JLogFactory.java |   91 +
 .../org/owasp/esapi/reference/Log4JLogger.java     |  524 ++++++
 .../owasp/esapi/reference/Log4JLoggerFactory.java  |   47 +
 .../esapi/reference/RandomAccessReferenceMap.java  |   85 +
 .../esapi/reference/accesscontrol/.svn/all-wcprops |   53 +
 .../esapi/reference/accesscontrol/.svn/entries     |  303 ++++
 .../.svn/text-base/AlwaysFalseACR.java.svn-base    |    9 +
 .../.svn/text-base/AlwaysTrueACR.java.svn-base     |    9 +
 .../.svn/text-base/BaseACR.java.svn-base           |   18 +
 .../.svn/text-base/DelegatingACR.java.svn-base     |   99 ++
 .../text-base/DynaBeanACRParameter.java.svn-base   |  186 ++
 .../EchoRuntimeParameterACR.java.svn-base          |   13 +
 .../ExperimentalAccessController.java.svn-base     |  184 ++
 .../.svn/text-base/FileBasedACRs.java.svn-base     |  559 ++++++
 .../reference/accesscontrol/AlwaysFalseACR.java    |    9 +
 .../reference/accesscontrol/AlwaysTrueACR.java     |    9 +
 .../esapi/reference/accesscontrol/BaseACR.java     |   18 +
 .../reference/accesscontrol/DelegatingACR.java     |   99 ++
 .../accesscontrol/DynaBeanACRParameter.java        |  186 ++
 .../accesscontrol/EchoRuntimeParameterACR.java     |   13 +
 .../ExperimentalAccessController.java              |  184 ++
 .../reference/accesscontrol/FileBasedACRs.java     |  559 ++++++
 .../accesscontrol/policyloader/.svn/all-wcprops    |   47 +
 .../accesscontrol/policyloader/.svn/entries        |  266 +++
 .../text-base/ACRParameterLoader.java.svn-base     |    9 +
 .../ACRParameterLoaderHelper.java.svn-base         |   46 +
 .../text-base/ACRPolicyFileLoader.java.svn-base    |   99 ++
 .../DynaBeanACRParameterLoader.java.svn-base       |   30 +
 .../EchoDynaBeanPolicyParameterACR.java.svn-base   |   15 +
 .../.svn/text-base/PolicyDTO.java.svn-base         |   55 +
 .../.svn/text-base/PolicyParameters.java.svn-base  |   42 +
 .../policyloader/ACRParameterLoader.java           |    9 +
 .../policyloader/ACRParameterLoaderHelper.java     |   46 +
 .../policyloader/ACRPolicyFileLoader.java          |   99 ++
 .../policyloader/DynaBeanACRParameterLoader.java   |   30 +
 .../EchoDynaBeanPolicyParameterACR.java            |   15 +
 .../accesscontrol/policyloader/PolicyDTO.java      |   55 +
 .../policyloader/PolicyParameters.java             |   42 +
 .../owasp/esapi/reference/crypto/.svn/all-wcprops  |   35 +
 .../org/owasp/esapi/reference/crypto/.svn/entries  |  198 +++
 .../crypto/.svn/prop-base/package.html.svn-base    |    5 +
 .../DefaultEncryptedProperties.java.svn-base       |  214 +++
 .../EncryptedPropertiesUtils.java.svn-base         |  212 +++
 .../.svn/text-base/JavaEncryptor.java.svn-base     | 1050 +++++++++++
 .../ReferenceEncryptedProperties.java.svn-base     |  293 ++++
 .../crypto/.svn/text-base/package.html.svn-base    |   10 +
 .../crypto/DefaultEncryptedProperties.java         |  214 +++
 .../reference/crypto/EncryptedPropertiesUtils.java |  212 +++
 .../esapi/reference/crypto/JavaEncryptor.java      | 1050 +++++++++++
 .../crypto/ReferenceEncryptedProperties.java       |  293 ++++
 .../org/owasp/esapi/reference/crypto/package.html  |   10 +
 .../java/org/owasp/esapi/reference/package.html    |   16 +
 .../esapi/reference/validation/.svn/all-wcprops    |   53 +
 .../owasp/esapi/reference/validation/.svn/entries  |  300 ++++
 .../text-base/BaseValidationRule.java.svn-base     |  198 +++
 .../CreditCardValidationRule.java.svn-base         |  165 ++
 .../text-base/DateValidationRule.java.svn-base     |  106 ++
 .../text-base/HTMLValidationRule.java.svn-base     |  129 ++
 .../text-base/IntegerValidationRule.java.svn-base  |   95 +
 .../text-base/NumberValidationRule.java.svn-base   |  146 ++
 .../text-base/StringValidationRule.java.svn-base   |  324 ++++
 .../.svn/text-base/package.html.svn-base           |   11 +
 .../reference/validation/BaseValidationRule.java   |  198 +++
 .../validation/CreditCardValidationRule.java       |  165 ++
 .../reference/validation/DateValidationRule.java   |  106 ++
 .../reference/validation/HTMLValidationRule.java   |  129 ++
 .../validation/IntegerValidationRule.java          |   95 +
 .../reference/validation/NumberValidationRule.java |  146 ++
 .../reference/validation/StringValidationRule.java |  324 ++++
 .../owasp/esapi/reference/validation/package.html  |   11 +
 .../java/org/owasp/esapi/tags/.svn/all-wcprops     |   83 +
 src/main/java/org/owasp/esapi/tags/.svn/entries    |  470 +++++
 .../.svn/text-base/BaseEncodeTag.java.svn-base     |   70 +
 .../.svn/text-base/ELEncodeFunctions.java.svn-base |  172 ++
 .../text-base/EncodeForBase64Tag.java.svn-base     |   82 +
 .../.svn/text-base/EncodeForCSSTag.java.svn-base   |   23 +
 .../EncodeForHTMLAttributeTag.java.svn-base        |   39 +
 .../.svn/text-base/EncodeForHTMLTag.java.svn-base  |   39 +
 .../text-base/EncodeForJavaScriptTag.java.svn-base |   23 +
 .../.svn/text-base/EncodeForURLTag.java.svn-base   |   34 +
 .../text-base/EncodeForVBScriptTag.java.svn-base   |   39 +
 .../EncodeForXMLAttributeTag.java.svn-base         |   23 +
 .../.svn/text-base/EncodeForXMLTag.java.svn-base   |   23 +
 .../.svn/text-base/EncodeForXPathTag.java.svn-base |   23 +
 .../tags/.svn/text-base/package.html.svn-base      |   12 +
 .../java/org/owasp/esapi/tags/BaseEncodeTag.java   |   70 +
 .../org/owasp/esapi/tags/ELEncodeFunctions.java    |  172 ++
 .../org/owasp/esapi/tags/EncodeForBase64Tag.java   |   82 +
 .../java/org/owasp/esapi/tags/EncodeForCSSTag.java |   23 +
 .../esapi/tags/EncodeForHTMLAttributeTag.java      |   39 +
 .../org/owasp/esapi/tags/EncodeForHTMLTag.java     |   39 +
 .../owasp/esapi/tags/EncodeForJavaScriptTag.java   |   23 +
 .../java/org/owasp/esapi/tags/EncodeForURLTag.java |   34 +
 .../org/owasp/esapi/tags/EncodeForVBScriptTag.java |   39 +
 .../owasp/esapi/tags/EncodeForXMLAttributeTag.java |   23 +
 .../java/org/owasp/esapi/tags/EncodeForXMLTag.java |   23 +
 .../org/owasp/esapi/tags/EncodeForXPathTag.java    |   23 +
 src/main/java/org/owasp/esapi/tags/package.html    |   12 +
 .../java/org/owasp/esapi/util/.svn/all-wcprops     |   41 +
 src/main/java/org/owasp/esapi/util/.svn/entries    |  232 +++
 .../prop-base/ByteConversionUtil.java.svn-base     |    5 +
 .../util/.svn/prop-base/ObjFactory.java.svn-base   |    5 +
 .../util/.svn/prop-base/package.html.svn-base      |    5 +
 .../text-base/ByteConversionUtil.java.svn-base     |  128 ++
 .../.svn/text-base/CollectionsUtil.java.svn-base   |  115 ++
 .../text-base/DefaultMessageUtil.java.svn-base     |   49 +
 .../util/.svn/text-base/NullSafe.java.svn-base     |   52 +
 .../util/.svn/text-base/ObjFactory.java.svn-base   |  138 ++
 .../util/.svn/text-base/package.html.svn-base      |   10 +
 .../org/owasp/esapi/util/ByteConversionUtil.java   |  128 ++
 .../java/org/owasp/esapi/util/CollectionsUtil.java |  115 ++
 .../org/owasp/esapi/util/DefaultMessageUtil.java   |   49 +
 src/main/java/org/owasp/esapi/util/NullSafe.java   |   52 +
 src/main/java/org/owasp/esapi/util/ObjFactory.java |  138 ++
 src/main/java/org/owasp/esapi/util/package.html    |   10 +
 src/main/java/org/owasp/esapi/waf/.svn/all-wcprops |   23 +
 src/main/java/org/owasp/esapi/waf/.svn/entries     |  142 ++
 .../text-base/ConfigurationException.java.svn-base |   40 +
 ...ESAPIWebApplicationFirewallFilter.java.svn-base |  466 +++++
 .../esapi/waf/.svn/text-base/package.html.svn-base |   14 +
 .../owasp/esapi/waf/ConfigurationException.java    |   40 +
 .../waf/ESAPIWebApplicationFirewallFilter.java     |  466 +++++
 .../org/owasp/esapi/waf/actions/.svn/all-wcprops   |   41 +
 .../java/org/owasp/esapi/waf/actions/.svn/entries  |  232 +++
 .../actions/.svn/text-base/Action.java.svn-base    |   45 +
 .../.svn/text-base/BlockAction.java.svn-base       |   35 +
 .../.svn/text-base/DefaultAction.java.svn-base     |   34 +
 .../.svn/text-base/DoNothingAction.java.svn-base   |   34 +
 .../.svn/text-base/RedirectAction.java.svn-base    |   39 +
 .../actions/.svn/text-base/package.html.svn-base   |   11 +
 .../java/org/owasp/esapi/waf/actions/Action.java   |   45 +
 .../org/owasp/esapi/waf/actions/BlockAction.java   |   35 +
 .../org/owasp/esapi/waf/actions/DefaultAction.java |   34 +
 .../owasp/esapi/waf/actions/DoNothingAction.java   |   34 +
 .../owasp/esapi/waf/actions/RedirectAction.java    |   39 +
 .../java/org/owasp/esapi/waf/actions/package.html  |   11 +
 .../owasp/esapi/waf/configuration/.svn/all-wcprops |   23 +
 .../org/owasp/esapi/waf/configuration/.svn/entries |  130 ++
 .../AppGuardianConfiguration.java.svn-base         |  227 +++
 .../text-base/ConfigurationParser.java.svn-base    |  627 +++++++
 .../.svn/text-base/package.html.svn-base           |   12 +
 .../configuration/AppGuardianConfiguration.java    |  227 +++
 .../waf/configuration/ConfigurationParser.java     |  627 +++++++
 .../org/owasp/esapi/waf/configuration/package.html |   12 +
 .../org/owasp/esapi/waf/internal/.svn/all-wcprops  |   41 +
 .../java/org/owasp/esapi/waf/internal/.svn/entries |  232 +++
 .../InterceptingHTTPServletRequest.java.svn-base   |  189 ++
 .../InterceptingHTTPServletResponse.java.svn-base  |  186 ++
 .../InterceptingPrintWriter.java.svn-base          |  182 ++
 .../InterceptingServletOutputStream.java.svn-base  |  161 ++
 .../.svn/text-base/Parameter.java.svn-base         |   49 +
 .../internal/.svn/text-base/package.html.svn-base  |   12 +
 .../internal/InterceptingHTTPServletRequest.java   |  189 ++
 .../internal/InterceptingHTTPServletResponse.java  |  186 ++
 .../waf/internal/InterceptingPrintWriter.java      |  182 ++
 .../internal/InterceptingServletOutputStream.java  |  161 ++
 .../org/owasp/esapi/waf/internal/Parameter.java    |   49 +
 .../java/org/owasp/esapi/waf/internal/package.html |   12 +
 src/main/java/org/owasp/esapi/waf/package.html     |   14 +
 .../org/owasp/esapi/waf/rules/.svn/all-wcprops     |  119 ++
 .../java/org/owasp/esapi/waf/rules/.svn/entries    |  674 +++++++
 .../text-base/AddHTTPOnlyFlagRule.java.svn-base    |   63 +
 .../.svn/text-base/AddHeaderRule.java.svn-base     |   92 +
 .../.svn/text-base/AddSecureFlagRule.java.svn-base |   63 +
 .../.svn/text-base/AuthenticatedRule.java.svn-base |   92 +
 .../.svn/text-base/BeanShellRule.java.svn-base     |  116 ++
 .../DetectOutboundContentRule.java.svn-base        |  116 ++
 .../.svn/text-base/EnforceHTTPSRule.java.svn-base  |   95 +
 .../GeneralAttackSignatureRule.java.svn-base       |   63 +
 .../.svn/text-base/HTTPMethodRule.java.svn-base    |   78 +
 .../waf/rules/.svn/text-base/IPRule.java.svn-base  |   79 +
 .../.svn/text-base/MustMatchRule.java.svn-base     |  336 ++++
 .../.svn/text-base/PathExtensionRule.java.svn-base |   60 +
 .../text-base/ReplaceContentRule.java.svn-base     |  113 ++
 .../RestrictContentTypeRule.java.svn-base          |   70 +
 .../text-base/RestrictUserAgentRule.java.svn-base  |   80 +
 .../waf/rules/.svn/text-base/Rule.java.svn-base    |   55 +
 .../rules/.svn/text-base/RuleUtil.java.svn-base    |  151 ++
 .../text-base/SimpleVirtualPatchRule.java.svn-base |  139 ++
 .../waf/rules/.svn/text-base/package.html.svn-base |   12 +
 .../owasp/esapi/waf/rules/AddHTTPOnlyFlagRule.java |   63 +
 .../org/owasp/esapi/waf/rules/AddHeaderRule.java   |   92 +
 .../owasp/esapi/waf/rules/AddSecureFlagRule.java   |   63 +
 .../owasp/esapi/waf/rules/AuthenticatedRule.java   |   92 +
 .../org/owasp/esapi/waf/rules/BeanShellRule.java   |  116 ++
 .../esapi/waf/rules/DetectOutboundContentRule.java |  116 ++
 .../owasp/esapi/waf/rules/EnforceHTTPSRule.java    |   95 +
 .../waf/rules/GeneralAttackSignatureRule.java      |   63 +
 .../org/owasp/esapi/waf/rules/HTTPMethodRule.java  |   78 +
 .../java/org/owasp/esapi/waf/rules/IPRule.java     |   79 +
 .../org/owasp/esapi/waf/rules/MustMatchRule.java   |  336 ++++
 .../owasp/esapi/waf/rules/PathExtensionRule.java   |   60 +
 .../owasp/esapi/waf/rules/ReplaceContentRule.java  |  113 ++
 .../esapi/waf/rules/RestrictContentTypeRule.java   |   70 +
 .../esapi/waf/rules/RestrictUserAgentRule.java     |   80 +
 src/main/java/org/owasp/esapi/waf/rules/Rule.java  |   55 +
 .../java/org/owasp/esapi/waf/rules/RuleUtil.java   |  151 ++
 .../esapi/waf/rules/SimpleVirtualPatchRule.java    |  139 ++
 .../java/org/owasp/esapi/waf/rules/package.html    |   12 +
 src/test/.svn/all-wcprops                          |    5 +
 src/test/.svn/entries                              |   34 +
 src/test/java/.svn/all-wcprops                     |    5 +
 src/test/java/.svn/entries                         |   31 +
 src/test/java/org/.svn/all-wcprops                 |    5 +
 src/test/java/org/.svn/entries                     |   31 +
 src/test/java/org/owasp/.svn/all-wcprops           |    5 +
 src/test/java/org/owasp/.svn/entries               |   31 +
 src/test/java/org/owasp/esapi/.svn/all-wcprops     |   35 +
 src/test/java/org/owasp/esapi/.svn/entries         |  222 +++
 .../prop-base/StringUtilitiesTest.java.svn-base    |    5 +
 .../text-base/PreparedStringTest.java.svn-base     |   65 +
 .../SecurityConfigurationWrapper.java.svn-base     |  633 +++++++
 .../text-base/StringUtilitiesTest.java.svn-base    |   73 +
 .../esapi/.svn/text-base/UserTest.java.svn-base    |  106 ++
 .../ValidationErrorListTest.java.svn-base          |  140 ++
 .../java/org/owasp/esapi/PreparedStringTest.java   |   65 +
 .../owasp/esapi/SecurityConfigurationWrapper.java  |  633 +++++++
 .../java/org/owasp/esapi/StringUtilitiesTest.java  |   73 +
 src/test/java/org/owasp/esapi/UserTest.java        |  106 ++
 .../org/owasp/esapi/ValidationErrorListTest.java   |  140 ++
 .../java/org/owasp/esapi/codecs/.svn/all-wcprops   |   23 +
 src/test/java/org/owasp/esapi/codecs/.svn/entries  |  130 ++
 .../codecs/.svn/text-base/CodecTest.java.svn-base  |  624 +++++++
 .../.svn/text-base/HashTrieTest.java.svn-base      |  213 +++
 .../text-base/XMLEntityCodecTest.java.svn-base     |  253 +++
 .../java/org/owasp/esapi/codecs/CodecTest.java     |  624 +++++++
 .../java/org/owasp/esapi/codecs/HashTrieTest.java  |  213 +++
 .../org/owasp/esapi/codecs/XMLEntityCodecTest.java |  253 +++
 .../java/org/owasp/esapi/crypto/.svn/all-wcprops   |   53 +
 src/test/java/org/owasp/esapi/crypto/.svn/entries  |  300 ++++
 .../.svn/prop-base/CipherSpecTest.java.svn-base    |    5 +
 .../CipherTextSerializerTest.java.svn-base         |    5 +
 .../.svn/prop-base/CryptoHelperTest.java.svn-base  |    5 +
 .../.svn/prop-base/CryptoTokenTest.java.svn-base   |    5 +
 .../SecurityProviderLoaderTest.java.svn-base       |    5 +
 .../.svn/text-base/CipherSpecTest.java.svn-base    |  273 +++
 .../CipherTextSerializerTest.java.svn-base         |   83 +
 .../.svn/text-base/CipherTextTest.java.svn-base    |  390 ++++
 .../.svn/text-base/CryptoHelperTest.java.svn-base  |  189 ++
 .../.svn/text-base/CryptoTokenTest.java.svn-base   |  401 +++++
 .../ESAPICryptoMACByPassTest.java.svn-base         |  231 +++
 .../.svn/text-base/PlainTextTest.java.svn-base     |  133 ++
 .../SecurityProviderLoaderTest.java.svn-base       |  139 ++
 .../org/owasp/esapi/crypto/CipherSpecTest.java     |  273 +++
 .../esapi/crypto/CipherTextSerializerTest.java     |   83 +
 .../org/owasp/esapi/crypto/CipherTextTest.java     |  390 ++++
 .../org/owasp/esapi/crypto/CryptoHelperTest.java   |  189 ++
 .../org/owasp/esapi/crypto/CryptoTokenTest.java    |  401 +++++
 .../esapi/crypto/ESAPICryptoMACByPassTest.java     |  231 +++
 .../java/org/owasp/esapi/crypto/PlainTextTest.java |  133 ++
 .../esapi/crypto/SecurityProviderLoaderTest.java   |  139 ++
 .../java/org/owasp/esapi/errors/.svn/all-wcprops   |   11 +
 src/test/java/org/owasp/esapi/errors/.svn/entries  |   62 +
 .../EnterpriseSecurityExceptionTest.java.svn-base  |  157 ++
 .../errors/EnterpriseSecurityExceptionTest.java    |  157 ++
 .../java/org/owasp/esapi/filters/.svn/all-wcprops  |   17 +
 src/test/java/org/owasp/esapi/filters/.svn/entries |   96 +
 .../text-base/ClickjackFilterTest.java.svn-base    |  106 ++
 .../.svn/text-base/SafeRequestTest.java.svn-base   |  203 +++
 .../owasp/esapi/filters/ClickjackFilterTest.java   |  106 ++
 .../org/owasp/esapi/filters/SafeRequestTest.java   |  203 +++
 .../java/org/owasp/esapi/http/.svn/all-wcprops     |   59 +
 src/test/java/org/owasp/esapi/http/.svn/entries    |  334 ++++
 .../.svn/text-base/MockFilterChain.java.svn-base   |   46 +
 .../.svn/text-base/MockFilterConfig.java.svn-base  |   66 +
 .../text-base/MockHttpServletRequest.java.svn-base |  782 +++++++++
 .../MockHttpServletResponse.java.svn-base          |  463 +++++
 .../.svn/text-base/MockHttpSession.java.svn-base   |  267 +++
 .../text-base/MockRequestDispatcher.java.svn-base  |   54 +
 .../text-base/MockServletContext.java.svn-base     |  549 ++++++
 .../text-base/MockServletInputStream.java.svn-base |   51 +
 .../http/.svn/text-base/package.html.svn-base      |   15 +
 .../java/org/owasp/esapi/http/MockFilterChain.java |   46 +
 .../org/owasp/esapi/http/MockFilterConfig.java     |   66 +
 .../owasp/esapi/http/MockHttpServletRequest.java   |  782 +++++++++
 .../owasp/esapi/http/MockHttpServletResponse.java  |  463 +++++
 .../java/org/owasp/esapi/http/MockHttpSession.java |  267 +++
 .../owasp/esapi/http/MockRequestDispatcher.java    |   54 +
 .../org/owasp/esapi/http/MockServletContext.java   |  549 ++++++
 .../owasp/esapi/http/MockServletInputStream.java   |   51 +
 src/test/java/org/owasp/esapi/http/package.html    |   15 +
 .../org/owasp/esapi/reference/.svn/all-wcprops     |  143 ++
 .../java/org/owasp/esapi/reference/.svn/entries    |  819 +++++++++
 .../text-base/AccessControllerTest.java.svn-base   |  358 ++++
 .../text-base/AccessReferenceMapTest.java.svn-base |  223 +++
 .../.svn/text-base/AuthenticatorTest.java.svn-base |  617 +++++++
 .../DefaultSecurityConfigurationTest.java.svn-base |  390 ++++
 .../.svn/text-base/EncoderTest.java.svn-base       |  802 +++++++++
 .../.svn/text-base/ExecutorTest.java.svn-base      |  269 +++
 .../.svn/text-base/HTTPUtilitiesTest.java.svn-base |  507 ++++++
 .../IntegerAccessReferenceMapTest.java.svn-base    |  223 +++
 .../text-base/IntrusionDetectorTest.java.svn-base  |  132 ++
 .../.svn/text-base/JavaLoggerTest.java.svn-base    |  282 +++
 .../.svn/text-base/Log4JLoggerTest.java.svn-base   |  463 +++++
 .../.svn/text-base/RandomizerTest.java.svn-base    |  144 ++
 .../.svn/text-base/SafeFileTest.java.svn-base      |  279 +++
 .../.svn/text-base/TestDebug.java.svn-base         |   22 +
 .../.svn/text-base/TestError.java.svn-base         |   22 +
 .../.svn/text-base/TestFatal.java.svn-base         |   22 +
 .../.svn/text-base/TestInfo.java.svn-base          |   22 +
 .../.svn/text-base/TestTrace.java.svn-base         |   22 +
 .../.svn/text-base/TestUnspecified.java.svn-base   |   22 +
 .../.svn/text-base/TestWarning.java.svn-base       |   22 +
 .../UnitTestSecurityConfiguration.java.svn-base    |   87 +
 .../.svn/text-base/UserTest.java.svn-base          |  739 ++++++++
 .../.svn/text-base/ValidatorTest.java.svn-base     | 1150 ++++++++++++
 .../esapi/reference/AccessControllerTest.java      |  358 ++++
 .../esapi/reference/AccessReferenceMapTest.java    |  223 +++
 .../owasp/esapi/reference/AuthenticatorTest.java   |  617 +++++++
 .../DefaultSecurityConfigurationTest.java          |  390 ++++
 .../org/owasp/esapi/reference/EncoderTest.java     |  802 +++++++++
 .../org/owasp/esapi/reference/ExecutorTest.java    |  269 +++
 .../owasp/esapi/reference/HTTPUtilitiesTest.java   |  507 ++++++
 .../reference/IntegerAccessReferenceMapTest.java   |  223 +++
 .../esapi/reference/IntrusionDetectorTest.java     |  132 ++
 .../org/owasp/esapi/reference/JavaLoggerTest.java  |  282 +++
 .../org/owasp/esapi/reference/Log4JLoggerTest.java |  463 +++++
 .../org/owasp/esapi/reference/RandomizerTest.java  |  144 ++
 .../org/owasp/esapi/reference/SafeFileTest.java    |  279 +++
 .../java/org/owasp/esapi/reference/TestDebug.java  |   22 +
 .../java/org/owasp/esapi/reference/TestError.java  |   22 +
 .../java/org/owasp/esapi/reference/TestFatal.java  |   22 +
 .../java/org/owasp/esapi/reference/TestInfo.java   |   22 +
 .../java/org/owasp/esapi/reference/TestTrace.java  |   22 +
 .../org/owasp/esapi/reference/TestUnspecified.java |   22 +
 .../org/owasp/esapi/reference/TestWarning.java     |   22 +
 .../reference/UnitTestSecurityConfiguration.java   |   87 +
 .../java/org/owasp/esapi/reference/UserTest.java   |  739 ++++++++
 .../org/owasp/esapi/reference/ValidatorTest.java   | 1150 ++++++++++++
 .../esapi/reference/accesscontrol/.svn/all-wcprops |   11 +
 .../esapi/reference/accesscontrol/.svn/entries     |   65 +
 .../text-base/AccessControllerTest.java.svn-base   |  128 ++
 .../accesscontrol/AccessControllerTest.java        |  128 ++
 .../accesscontrol/policyloader/.svn/all-wcprops    |   11 +
 .../accesscontrol/policyloader/.svn/entries        |   62 +
 .../ACRPolicyFileLoaderTest.java.svn-base          |   70 +
 .../policyloader/ACRPolicyFileLoaderTest.java      |   70 +
 .../owasp/esapi/reference/crypto/.svn/all-wcprops  |   35 +
 .../org/owasp/esapi/reference/crypto/.svn/entries  |  198 +++
 .../.svn/prop-base/CryptoPolicy.java.svn-base      |    5 +
 .../.svn/text-base/CryptoPolicy.java.svn-base      |  105 ++
 .../EncryptedPropertiesTest.java.svn-base          |  232 +++
 .../EncryptedPropertiesUtilsTest.java.svn-base     |  196 +++
 .../.svn/text-base/EncryptorTest.java.svn-base     |  527 ++++++
 .../ReferenceEncryptedPropertiesTest.java.svn-base |  535 ++++++
 .../owasp/esapi/reference/crypto/CryptoPolicy.java |  105 ++
 .../reference/crypto/EncryptedPropertiesTest.java  |  232 +++
 .../crypto/EncryptedPropertiesUtilsTest.java       |  196 +++
 .../esapi/reference/crypto/EncryptorTest.java      |  527 ++++++
 .../crypto/ReferenceEncryptedPropertiesTest.java   |  535 ++++++
 .../esapi/reference/validation/.svn/all-wcprops    |   11 +
 .../owasp/esapi/reference/validation/.svn/entries  |   62 +
 .../StringValidationRuleTest.java.svn-base         |  158 ++
 .../validation/StringValidationRuleTest.java       |  158 ++
 .../java/org/owasp/esapi/util/.svn/all-wcprops     |   29 +
 src/test/java/org/owasp/esapi/util/.svn/entries    |  164 ++
 .../prop-base/ByteConversionUtilTest.java.svn-base |    5 +
 .../.svn/prop-base/ObjFactoryTest.java.svn-base    |    5 +
 .../.svn/prop-base/ThePrefectClass.java.svn-base   |    5 +
 .../text-base/ByteConversionUtilTest.java.svn-base |   86 +
 .../.svn/text-base/FileTestUtils.java.svn-base     |  216 +++
 .../.svn/text-base/ObjFactoryTest.java.svn-base    |  192 ++
 .../.svn/text-base/ThePrefectClass.java.svn-base   |   12 +
 .../owasp/esapi/util/ByteConversionUtilTest.java   |   86 +
 .../java/org/owasp/esapi/util/FileTestUtils.java   |  216 +++
 .../java/org/owasp/esapi/util/ObjFactoryTest.java  |  192 ++
 .../java/org/owasp/esapi/util/ThePrefectClass.java |   12 +
 src/test/java/org/owasp/esapi/waf/.svn/all-wcprops |  119 ++
 src/test/java/org/owasp/esapi/waf/.svn/entries     |  677 +++++++
 .../waf/.svn/text-base/AddHeaderTest.java.svn-base |   59 +
 .../waf/.svn/text-base/BeanShellTest.java.svn-base |   47 +
 .../text-base/DetectOutboundTest.java.svn-base     |   63 +
 .../text-base/DynamicInsertionTest.java.svn-base   |   70 +
 .../EnforceAuthenticationTest.java.svn-base        |   52 +
 .../.svn/text-base/EnforceHTTPSTest.java.svn-base  |   68 +
 .../.svn/text-base/GoodRequestTest.java.svn-base   |   43 +
 .../waf/.svn/text-base/HttpOnlyTest.java.svn-base  |   84 +
 .../text-base/MockWafFilterConfig.java.svn-base    |   18 +
 .../text-base/MockWafServletContext.java.svn-base  |   14 +
 .../waf/.svn/text-base/MustMatchTest.java.svn-base |   55 +
 .../RestrictContentTypeTest.java.svn-base          |   51 +
 .../text-base/RestrictExtensionTest.java.svn-base  |   57 +
 .../text-base/RestrictMethodTest.java.svn-base     |   56 +
 .../text-base/RestrictUserAgentTest.java.svn-base  |   46 +
 .../.svn/text-base/VirtualPatchTest.java.svn-base  |   60 +
 .../waf/.svn/text-base/WAFFilterTest.java.svn-base |   81 +
 .../waf/.svn/text-base/WAFTestCase.java.svn-base   |   65 +
 .../.svn/text-base/WAFTestUtility.java.svn-base    |   91 +
 .../java/org/owasp/esapi/waf/AddHeaderTest.java    |   59 +
 .../java/org/owasp/esapi/waf/BeanShellTest.java    |   47 +
 .../org/owasp/esapi/waf/DetectOutboundTest.java    |   63 +
 .../org/owasp/esapi/waf/DynamicInsertionTest.java  |   70 +
 .../owasp/esapi/waf/EnforceAuthenticationTest.java |   52 +
 .../java/org/owasp/esapi/waf/EnforceHTTPSTest.java |   68 +
 .../java/org/owasp/esapi/waf/GoodRequestTest.java  |   43 +
 .../java/org/owasp/esapi/waf/HttpOnlyTest.java     |   84 +
 .../org/owasp/esapi/waf/MockWafFilterConfig.java   |   18 +
 .../org/owasp/esapi/waf/MockWafServletContext.java |   14 +
 .../java/org/owasp/esapi/waf/MustMatchTest.java    |   55 +
 .../owasp/esapi/waf/RestrictContentTypeTest.java   |   51 +
 .../org/owasp/esapi/waf/RestrictExtensionTest.java |   57 +
 .../org/owasp/esapi/waf/RestrictMethodTest.java    |   56 +
 .../org/owasp/esapi/waf/RestrictUserAgentTest.java |   46 +
 .../java/org/owasp/esapi/waf/VirtualPatchTest.java |   60 +
 .../java/org/owasp/esapi/waf/WAFFilterTest.java    |   81 +
 src/test/java/org/owasp/esapi/waf/WAFTestCase.java |   65 +
 .../java/org/owasp/esapi/waf/WAFTestUtility.java   |   91 +
 .../org/owasp/esapi/waf/internal/.svn/all-wcprops  |   17 +
 .../java/org/owasp/esapi/waf/internal/.svn/entries |   96 +
 ...nterceptingHttpServletRequestTest.java.svn-base |   76 +
 ...terceptingHttpServletResponseTest.java.svn-base |   80 +
 .../InterceptingHttpServletRequestTest.java        |   76 +
 .../InterceptingHttpServletResponseTest.java       |   80 +
 src/test/resources/.svn/all-wcprops                |   29 +
 src/test/resources/.svn/entries                    |  170 ++
 .../ESAPI2.0-ciphertext-portable.ser.svn-base      |    5 +
 .../ESAPI2.0-ciphertext-portable.ser.svn-base      |  Bin 0 -> 114 bytes
 .../resources/.svn/text-base/Readme.txt.svn-base   |    5 +
 .../resources/.svn/text-base/log4j.dtd.svn-base    |  227 +++
 .../resources/.svn/text-base/log4j.xml.svn-base    |   55 +
 .../resources/ESAPI2.0-ciphertext-portable.ser     |  Bin 0 -> 114 bytes
 src/test/resources/Readme.txt                      |    5 +
 src/test/resources/esapi/.svn/all-wcprops          |   41 +
 src/test/resources/esapi/.svn/entries              |  238 +++
 .../ESAPI-AccessControlPolicy.xml.svn-base         |  127 ++
 .../esapi/.svn/text-base/ESAPI.properties.svn-base |  465 +++++
 .../.svn/text-base/antisamy-esapi.xml.svn-base     |  492 ++++++
 .../esapi/.svn/text-base/users.txt.svn-base        |    0
 .../.svn/text-base/validation.properties.svn-base  |   29 +
 .../esapi/.svn/text-base/waf-policy.xml.svn-base   |  149 ++
 .../resources/esapi/ESAPI-AccessControlPolicy.xml  |  127 ++
 src/test/resources/esapi/ESAPI.properties          |  465 +++++
 src/test/resources/esapi/antisamy-esapi.xml        |  492 ++++++
 .../resources/esapi/fbac-policies/.svn/all-wcprops |   35 +
 .../resources/esapi/fbac-policies/.svn/entries     |  198 +++
 .../.svn/text-base/DataAccessRules.txt.svn-base    |    9 +
 .../.svn/text-base/FileAccessRules.txt.svn-base    |   12 +
 .../text-base/FunctionAccessRules.txt.svn-base     |   13 +
 .../.svn/text-base/ServiceAccessRules.txt.svn-base |   12 +
 .../.svn/text-base/URLAccessRules.txt.svn-base     |   18 +
 .../esapi/fbac-policies/DataAccessRules.txt        |    9 +
 .../esapi/fbac-policies/FileAccessRules.txt        |   12 +
 .../esapi/fbac-policies/FunctionAccessRules.txt    |   13 +
 .../esapi/fbac-policies/ServiceAccessRules.txt     |   12 +
 .../esapi/fbac-policies/URLAccessRules.txt         |   18 +
 src/test/resources/esapi/users.txt                 |    0
 src/test/resources/esapi/validation.properties     |   29 +
 .../resources/esapi/waf-policies/.svn/all-wcprops  |  107 ++
 src/test/resources/esapi/waf-policies/.svn/entries |  606 +++++++
 .../.svn/text-base/add-header-policy.xml.svn-base  |   29 +
 .../text-base/add-httponly-policy.xml.svn-base     |   27 +
 .../.svn/text-base/add-secure-policy.xml.svn-base  |   27 +
 .../text-base/authentication-policy.xml.svn-base   |   30 +
 .../.svn/text-base/bean-shell-policy.xml.svn-base  |   30 +
 .../.svn/text-base/bean-shell-rule.bsh.svn-base    |    5 +
 .../text-base/detect-outbound-policy.xml.svn-base  |   27 +
 .../dynamic-insertion-policy.xml.svn-base          |   27 +
 .../text-base/enforce-https-policy.xml.svn-base    |   28 +
 .../.svn/text-base/must-match-policy.xml.svn-base  |   35 +
 .../text-base/replace-outbound-policy.xml.svn-base |   27 +
 .../restrict-content-type-policy.xml.svn-base      |   27 +
 .../restrict-extension-policy.xml.svn-base         |   25 +
 .../text-base/restrict-method-policy.xml.svn-base  |   25 +
 .../restrict-source-ip-policy.xml.svn-base         |   27 +
 .../restrict-user-agent-policy.xml.svn-base        |   26 +
 .../text-base/virtual-patch-policy.xml.svn-base    |   27 +
 .../esapi/waf-policies/add-header-policy.xml       |   29 +
 .../esapi/waf-policies/add-httponly-policy.xml     |   27 +
 .../esapi/waf-policies/add-secure-policy.xml       |   27 +
 .../esapi/waf-policies/authentication-policy.xml   |   30 +
 .../esapi/waf-policies/bean-shell-policy.xml       |   30 +
 .../esapi/waf-policies/bean-shell-rule.bsh         |    5 +
 .../esapi/waf-policies/detect-outbound-policy.xml  |   27 +
 .../waf-policies/dynamic-insertion-policy.xml      |   27 +
 .../esapi/waf-policies/enforce-https-policy.xml    |   28 +
 .../esapi/waf-policies/must-match-policy.xml       |   35 +
 .../esapi/waf-policies/replace-outbound-policy.xml |   27 +
 .../waf-policies/restrict-content-type-policy.xml  |   27 +
 .../waf-policies/restrict-extension-policy.xml     |   25 +
 .../esapi/waf-policies/restrict-method-policy.xml  |   25 +
 .../waf-policies/restrict-source-ip-policy.xml     |   27 +
 .../waf-policies/restrict-user-agent-policy.xml    |   26 +
 .../esapi/waf-policies/virtual-patch-policy.xml    |   27 +
 src/test/resources/esapi/waf-policy.xml            |  149 ++
 src/test/resources/log4j.dtd                       |  227 +++
 src/test/resources/log4j.xml                       |   55 +
 src/test/resources/properties/.svn/all-wcprops     |   23 +
 src/test/resources/properties/.svn/entries         |  130 ++
 .../.svn/text-base/ESAPI_en_US.properties.svn-base |    9 +
 .../.svn/text-base/ESAPI_fr_FR.properties.svn-base |    9 +
 .../text-base/ESAPI_zhs_CN.properties.svn-base     |    1 +
 .../resources/properties/ESAPI_en_US.properties    |    9 +
 .../resources/properties/ESAPI_fr_FR.properties    |    9 +
 .../resources/properties/ESAPI_zhs_CN.properties   |    1 +
 src/util/.svn/all-wcprops                          |   17 +
 src/util/.svn/entries                              |   96 +
 src/util/.svn/prop-base/README.txt.svn-base        |    5 +
 src/util/.svn/prop-base/esapi-release.sh.svn-base  |    5 +
 src/util/.svn/text-base/README.txt.svn-base        |   22 +
 src/util/.svn/text-base/esapi-release.sh.svn-base  |  215 +++
 src/util/README.txt                                |   22 +
 src/util/esapi-release.sh                          |  215 +++
 target/.svn/all-wcprops                            |    5 +
 target/.svn/entries                                |   28 +
 987 files changed, 139957 insertions(+)

diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..3c57589
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v6.0"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644
index 0000000..b20935a
--- /dev/null
+++ b/.project
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ESAPI_2.0</name>
+	<comment>The Enterprise Security API project is an OWASP project
+		to create simple strong security controls for every web platform.
+		Security controls are not simple to build. You can read about the
+		hundreds of pitfalls for unwary developers on the OWASP website. By
+		providing developers with a set of strong controls, we aim to
+		eliminate some of the complexity of creating secure web applications.
+		This can result in significant cost savings across the SDLC.</comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+	</natures>
+</projectDescription>
diff --git a/.settings/.svn/all-wcprops b/.settings/.svn/all-wcprops
new file mode 100644
index 0000000..998ffb5
--- /dev/null
+++ b/.settings/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 45
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.settings
+END
+org.eclipse.wst.common.component
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.settings/org.eclipse.wst.common.component
+END
+org.eclipse.jdt.core.prefs
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.settings/org.eclipse.jdt.core.prefs
+END
+org.eclipse.core.resources.prefs
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.settings/org.eclipse.core.resources.prefs
+END
+org.eclipse.wst.common.project.facet.core.xml
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.settings/org.eclipse.wst.common.project.facet.core.xml
+END
+org.maven.ide.eclipse.prefs
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.settings/org.maven.ide.eclipse.prefs
+END
diff --git a/.settings/.svn/entries b/.settings/.svn/entries
new file mode 100644
index 0000000..1258f5d
--- /dev/null
+++ b/.settings/.svn/entries
@@ -0,0 +1,198 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/.settings
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-05-11T04:39:05.894671Z
+1784
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+org.eclipse.core.resources.prefs
+file
+
+
+
+
+2014-02-18T16:19:56.950045Z
+18cc1d0fbfeda7fbb48333fd5b31c894
+2009-11-26T11:53:44.384577Z
+838
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+88
+

+org.eclipse.wst.common.project.facet.core.xml
+file
+
+
+
+
+2014-02-18T16:19:56.950045Z
+1bcae7c825933973cbe3a047fc59ffef
+2009-07-10T05:22:23.827015Z
+565
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+195
+

+org.maven.ide.eclipse.prefs
+file
+
+
+
+
+2014-02-18T16:19:56.950045Z
+e27471ba8af583ee973db88614a833f0
+2009-07-10T05:22:23.827015Z
+565
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+271
+

+org.eclipse.wst.common.component
+file
+
+
+
+
+2014-02-18T16:19:56.950045Z
+983081f9672f576dafa45036aba5d0ee
+2011-05-11T04:39:05.894671Z
+1784
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+517
+

+org.eclipse.jdt.core.prefs
+file
+
+
+
+
+2014-02-18T16:19:56.950045Z
+10a1f58b2eae5bbe8a7a65110a2b6577
+2011-05-11T04:39:05.894671Z
+1784
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+274
+

diff --git a/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base b/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base
new file mode 100644
index 0000000..2655b64
--- /dev/null
+++ b/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base
@@ -0,0 +1,3 @@
+#Thu Nov 26 01:50:11 HST 2009
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/.settings/.svn/text-base/org.eclipse.jdt.core.prefs.svn-base b/.settings/.svn/text-base/org.eclipse.jdt.core.prefs.svn-base
new file mode 100644
index 0000000..9e33fcc
--- /dev/null
+++ b/.settings/.svn/text-base/org.eclipse.jdt.core.prefs.svn-base
@@ -0,0 +1,6 @@
+#Tue May 10 22:28:38 MDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/.settings/.svn/text-base/org.eclipse.wst.common.component.svn-base b/.settings/.svn/text-base/org.eclipse.wst.common.component.svn-base
new file mode 100644
index 0000000..b814064
--- /dev/null
+++ b/.settings/.svn/text-base/org.eclipse.wst.common.component.svn-base
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-modules id="moduleCoreId" project-version="1.5.0">
+  <wb-module deploy-name="ESAPI">
+    <wb-resource deploy-path="/" source-path="src/main/java"/>
+    <wb-resource deploy-path="/" source-path="src/main/resources"/>
+        <wb-resource deploy-path="/" source-path="/src/test/java"/>
+        <wb-resource deploy-path="/" source-path="/src/test/resources"/>
+        <wb-resource deploy-path="/" source-path="/src/main/java"/>
+  </wb-module>
+</project-modules>
diff --git a/.settings/.svn/text-base/org.eclipse.wst.common.project.facet.core.xml.svn-base b/.settings/.svn/text-base/org.eclipse.wst.common.project.facet.core.xml.svn-base
new file mode 100644
index 0000000..613f29c
--- /dev/null
+++ b/.settings/.svn/text-base/org.eclipse.wst.common.project.facet.core.xml.svn-base
@@ -0,0 +1,6 @@
+<faceted-project>
+  <fixed facet="jst.java"/>
+  <fixed facet="jst.utility"/>
+  <installed facet="jst.utility" version="1.0"/>
+  <installed facet="jst.java" version="5.0"/>
+</faceted-project>
\ No newline at end of file
diff --git a/.settings/.svn/text-base/org.maven.ide.eclipse.prefs.svn-base b/.settings/.svn/text-base/org.maven.ide.eclipse.prefs.svn-base
new file mode 100644
index 0000000..310a300
--- /dev/null
+++ b/.settings/.svn/text-base/org.maven.ide.eclipse.prefs.svn-base
@@ -0,0 +1,9 @@
+#Fri Jul 10 01:10:33 EDT 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..2655b64
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Thu Nov 26 01:50:11 HST 2009
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..9e33fcc
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,6 @@
+#Tue May 10 22:28:38 MDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component
new file mode 100644
index 0000000..b814064
--- /dev/null
+++ b/.settings/org.eclipse.wst.common.component
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-modules id="moduleCoreId" project-version="1.5.0">
+  <wb-module deploy-name="ESAPI">
+    <wb-resource deploy-path="/" source-path="src/main/java"/>
+    <wb-resource deploy-path="/" source-path="src/main/resources"/>
+        <wb-resource deploy-path="/" source-path="/src/test/java"/>
+        <wb-resource deploy-path="/" source-path="/src/test/resources"/>
+        <wb-resource deploy-path="/" source-path="/src/main/java"/>
+  </wb-module>
+</project-modules>
diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..613f29c
--- /dev/null
+++ b/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,6 @@
+<faceted-project>
+  <fixed facet="jst.java"/>
+  <fixed facet="jst.utility"/>
+  <installed facet="jst.utility" version="1.0"/>
+  <installed facet="jst.java" version="5.0"/>
+</faceted-project>
\ No newline at end of file
diff --git a/.settings/org.maven.ide.eclipse.prefs b/.settings/org.maven.ide.eclipse.prefs
new file mode 100644
index 0000000..310a300
--- /dev/null
+++ b/.settings/org.maven.ide.eclipse.prefs
@@ -0,0 +1,9 @@
+#Fri Jul 10 01:10:33 EDT 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/.svn/all-wcprops b/.svn/all-wcprops
new file mode 100644
index 0000000..550e0e8
--- /dev/null
+++ b/.svn/all-wcprops
@@ -0,0 +1,59 @@
+K 25
+svn:wc:ra_dav:version-url
+V 35
+/svn/!svn/ver/1899/tags/esapi-2.1.0
+END
+LICENSE-CONTENT
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/1899/tags/esapi-2.1.0/LICENSE-CONTENT
+END
+pom.xml
+K 25
+svn:wc:ra_dav:version-url
+V 43
+/svn/!svn/ver/1899/tags/esapi-2.1.0/pom.xml
+END
+LICENSE-README
+K 25
+svn:wc:ra_dav:version-url
+V 50
+/svn/!svn/ver/1899/tags/esapi-2.1.0/LICENSE-README
+END
+.classpath
+K 25
+svn:wc:ra_dav:version-url
+V 46
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.classpath
+END
+esapi.iml
+K 25
+svn:wc:ra_dav:version-url
+V 45
+/svn/!svn/ver/1899/tags/esapi-2.1.0/esapi.iml
+END
+LICENSE
+K 25
+svn:wc:ra_dav:version-url
+V 43
+/svn/!svn/ver/1899/tags/esapi-2.1.0/LICENSE
+END
+.project
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/1899/tags/esapi-2.1.0/.project
+END
+javadoc.xml
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/1899/tags/esapi-2.1.0/javadoc.xml
+END
+ant-javadoc.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/1899/tags/esapi-2.1.0/ant-javadoc.xml
+END
diff --git a/.svn/dir-prop-base b/.svn/dir-prop-base
new file mode 100644
index 0000000..cd6c387
--- /dev/null
+++ b/.svn/dir-prop-base
@@ -0,0 +1,20 @@
+K 10
+snv:ignore
+V 9
+esapi.iml
+K 14
+subclipse:tags
+V 93
+195,1.3,/branches/1.3,branch
+195,1.3.0,/releases/1.3.0,tag
+212,1.3.0,/tags/releases/1.3.0,tag
+K 10
+svn:ignore
+V 10
+.settings
+
+K 13
+svn:mergeinfo
+V 29
+/branches/2.0_quality:570-732
+END
diff --git a/.svn/entries b/.svn/entries
new file mode 100644
index 0000000..60e3df3
--- /dev/null
+++ b/.svn/entries
@@ -0,0 +1,352 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-03T01:16:47.357485Z
+1899
+chris.schmidt at owasp.org
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+.project
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+c12a7b18b409cdab1d73a27e8dce39db
+2010-08-17T20:17:55.307022Z
+1476
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1492
+

+ant-javadoc.xml
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+2a322b0e0ad26322b5b058184066bd1c
+2013-08-31T22:47:29.027385Z
+1895
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11377
+

+target
+dir
+

+src
+dir
+

+pom.xml
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+fa198a53f56821c2888522cea8dc343c
+2013-09-03T01:16:41.822668Z
+1898
+chris.schmidt at owasp.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+20705
+

+LICENSE-CONTENT
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+05fc0349753bec21268e9d45516a3f6c
+2010-04-13T17:50:10.636123Z
+1237
+jonathan.ruckwood
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+21351
+

+.classpath
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+882fffa11d2465e83c62ef58b10a94f0
+2011-05-11T04:39:05.894671Z
+1784
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+752
+

+javadoc.xml
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+58cc3c77666a76432faba6322ce7e6c6
+2008-10-21T16:50:37.170052Z
+263
+kfealz at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3623
+

+configuration
+dir
+

+documentation
+dir
+

+resources
+dir
+

+LICENSE-README
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+01c48342c309735c788ef0242607bb03
+2011-03-21T02:38:55.906372Z
+1728
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+424
+

+.settings
+dir
+

+esapi.iml
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+86188268b97dd0781530c8f1ead62ed6
+2011-03-23T15:59:53.013957Z
+1745
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3955
+

+LICENSE
+file
+
+
+
+
+2014-02-18T16:19:56.954045Z
+d3c16b86eb9072c1e9b9c932e4635e53
+2008-06-04T03:55:27.875275Z
+133
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1511
+

diff --git a/.svn/text-base/.classpath.svn-base b/.svn/text-base/.classpath.svn-base
new file mode 100644
index 0000000..3c57589
--- /dev/null
+++ b/.svn/text-base/.classpath.svn-base
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v6.0"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/.svn/text-base/.project.svn-base b/.svn/text-base/.project.svn-base
new file mode 100644
index 0000000..b20935a
--- /dev/null
+++ b/.svn/text-base/.project.svn-base
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ESAPI_2.0</name>
+	<comment>The Enterprise Security API project is an OWASP project
+		to create simple strong security controls for every web platform.
+		Security controls are not simple to build. You can read about the
+		hundreds of pitfalls for unwary developers on the OWASP website. By
+		providing developers with a set of strong controls, we aim to
+		eliminate some of the complexity of creating secure web applications.
+		This can result in significant cost savings across the SDLC.</comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+	</natures>
+</projectDescription>
diff --git a/.svn/text-base/LICENSE-CONTENT.svn-base b/.svn/text-base/LICENSE-CONTENT.svn-base
new file mode 100644
index 0000000..9d154ce
--- /dev/null
+++ b/.svn/text-base/LICENSE-CONTENT.svn-base
@@ -0,0 +1,78 @@
+Creative Commons
+Creative Commons Legal Code
+Attribution-ShareAlike 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. 
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+
+1. Definitions
+
+   1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be [...]
+   2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works [...]
+   3. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License  [...]
+   4. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
+   5. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
+   6. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+   7. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer bei [...]
+   8. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or withou [...]
+   9. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+  10. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the perform [...]
+  11. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
+
+2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
+
+   1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
+   2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
+   3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
+   4. to Distribute and Publicly Perform Adaptations.
+   5.
+
+      For the avoidance of doubt:
+         1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
+         2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
+         3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
+
+The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
+
+4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
+
+   1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. Y [...]
+   2. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you [...]
+   3. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for att [...]
+   4. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the righ [...]
+
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCL [...]
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+   1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+   2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this Lic [...]
+
+8. Miscellaneous
+
+   1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+   2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
+   3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+   4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
+   5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
+   6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction i [...]
+
+    Creative Commons Notice
+
+    Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all r [...]
+
+    Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request fro [...]
+
+    Creative Commons may be contacted at http://creativecommons.org/.
+
diff --git a/.svn/text-base/LICENSE-README.svn-base b/.svn/text-base/LICENSE-README.svn-base
new file mode 100644
index 0000000..e7295e1
--- /dev/null
+++ b/.svn/text-base/LICENSE-README.svn-base
@@ -0,0 +1,7 @@
+Please note that:
+
+1) The LICENSE file only refers to the licensing of the source and binary code of ESAPI. 
+   For example, the actual ESAPI JAR file is only licensed under "The BSD License".
+   
+2) The LICENSE-CONTENT file only refers to the licensing of the content and documentation of ESAPI. 
+   For example, the documentation directory is only licensed under the Creative Commons/ShareAlike 3.0 Unported license.
\ No newline at end of file
diff --git a/.svn/text-base/LICENSE.svn-base b/.svn/text-base/LICENSE.svn-base
new file mode 100644
index 0000000..5d0f11c
--- /dev/null
+++ b/.svn/text-base/LICENSE.svn-base
@@ -0,0 +1,12 @@
+The BSD License
+
+Copyright (c) 2007, The OWASP Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
+Neither the name of the OWASP Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFI [...]
+
diff --git a/.svn/text-base/ant-javadoc.xml.svn-base b/.svn/text-base/ant-javadoc.xml.svn-base
new file mode 100644
index 0000000..084d4d1
--- /dev/null
+++ b/.svn/text-base/ant-javadoc.xml.svn-base
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project default="javadoc">
+    <target name="javadoc">
+        <javadoc access="private" additionalparam="-J-Xmx768m " author="true" classpath="C:\Users\kww\.m2\repository\org\beanshell\bsh-core\2.0b4\bsh-core-2.0b4.jar;C:\Users\kww\.m2\repository\xalan\xalan\2.7.0\xalan-2.7.0.jar;C:\Users\kww\.m2\repository\logkit\logkit\1.0.1\logkit-1.0.1.jar;C:\Users\kww\.m2\repository\commons-io\commons-io\1.3\commons-io-1.3.jar;C:\Users\kww\.m2\repository\commons-collections\commons-collections\3.2\commons-collections-3.2.jar;C:\Users\kww\.m2\repository [...]
+            <link href="http://java.sun.com/javase/7/docs/api/"/>
+        </javadoc>
+    </target>
+</project>
diff --git a/.svn/text-base/esapi.iml.svn-base b/.svn/text-base/esapi.iml.svn-base
new file mode 100644
index 0000000..5729ef2
--- /dev/null
+++ b/.svn/text-base/esapi.iml.svn-base
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.5" level="project" />
+    <orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2" level="project" />
+    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.3" level="project" />
+    <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.1" level="project" />
+    <orderEntry type="library" name="Maven: log4j:log4j:1.2.16" level="project" />
+    <orderEntry type="library" name="Maven: logkit:logkit:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: avalon-framework:avalon-framework:4.1.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: javax.servlet:servlet-api:2.4" level="project" />
+    <orderEntry type="library" name="Maven: commons-digester:commons-digester:1.8" level="project" />
+    <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.7.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils-core:1.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.4" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: javax.servlet:jsp-api:2.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.2" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: commons-io:commons-io:1.3" level="project" />
+    <orderEntry type="library" name="Maven: xom:xom:1.1" level="project" />
+    <orderEntry type="library" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />
+    <orderEntry type="library" name="Maven: xerces:xercesImpl:2.6.2" level="project" />
+    <orderEntry type="library" name="Maven: xalan:xalan:2.7.0" level="project" />
+    <orderEntry type="library" name="Maven: xml-apis:xml-apis:1.0.b2" level="project" />
+    <orderEntry type="library" name="Maven: jaxen:jaxen:1.1-beta-8" level="project" />
+    <orderEntry type="library" name="Maven: dom4j:dom4j:1.6.1" level="project" />
+    <orderEntry type="library" name="Maven: jdom:jdom:1.0" level="project" />
+    <orderEntry type="library" name="Maven: org.beanshell:bsh-core:2.0b4" level="project" />
+    <orderEntry type="library" name="Maven: org.owasp.antisamy:antisamy:1.4.3" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-css:1.7" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-ext:1.7" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-util:1.7" level="project" />
+    <orderEntry type="library" name="Maven: xml-apis:xml-apis-ext:1.3.04" level="project" />
+    <orderEntry type="library" name="Maven: net.sourceforge.nekohtml:nekohtml:1.9.12" level="project" />
+    <orderEntry type="library" name="Maven: commons-httpclient:commons-httpclient:3.1" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.2" level="project" />
+  </component>
+</module>
+
diff --git a/.svn/text-base/javadoc.xml.svn-base b/.svn/text-base/javadoc.xml.svn-base
new file mode 100644
index 0000000..daa7ba9
--- /dev/null
+++ b/.svn/text-base/javadoc.xml.svn-base
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project default="javadoc">
+    <target name="javadoc">
+        <javadoc access="public" author="true" classpath="lib/xml-apis-ext.jar;lib/xml-apis.jar;lib/nekohtml.jar;lib/batik-util.jar;C:\eclipse\plugins\org.junit_3.8.2.v20080602-1318\junit.jar;lib/antisamy-bin.1.2.jar;lib/servlet-api.jar;lib/commons-fileupload-1.2.jar;lib/xercesImpl.jar;lib/batik-css.jar;lib/commons-io-1.3.2.jar;lib/jsp-api.jar" destdir="doc" doctitle="OWASP Enterprise Security API (ESAPI)" nodeprecated="false" nodeprecatedlist="false" noindex="false" nonavbar="false" not [...]
+    </target>
+</project>
diff --git a/.svn/text-base/pom.xml.svn-base b/.svn/text-base/pom.xml.svn-base
new file mode 100644
index 0000000..e6ce765
--- /dev/null
+++ b/.svn/text-base/pom.xml.svn-base
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.owasp.esapi</groupId>
+    <artifactId>esapi</artifactId>
+    <version>2.1.0</version>
+    <packaging>jar</packaging>
+
+    <parent>
+        <groupId>org.sonatype.oss</groupId>
+        <artifactId>oss-parent</artifactId>
+        <version>5</version>
+    </parent>
+
+    <licenses>
+        <license>
+            <name>BSD</name>
+            <url>http://www.opensource.org/licenses/bsd-license.php</url>
+            <comments>Code License - New BSD License</comments>
+        </license>
+        <license>
+            <name>Creative Commons 3.0 BY-SA</name>
+            <url>http://creativecommons.org/licenses/by-sa/3.0/</url>
+            <comments>Content License - Create Commons 3.0 BY-SA</comments>
+        </license>
+    </licenses>
+
+    <name>ESAPI</name>
+    <url>http://www.esapi.org/</url>
+    <description>The Enterprise Security API (ESAPI) project is an OWASP project
+        to create simple strong security controls for every web platform.
+        Security controls are not simple to build. You can read about the
+        hundreds of pitfalls for unwary developers on the OWASP web site. By
+        providing developers with a set of strong controls, we aim to
+        eliminate some of the complexity of creating secure web applications.
+        This can result in significant cost savings across the SDLC.
+    </description>
+
+    <organization>
+        <name>The Open Web Application Security Project (OWASP)</name>
+        <url>http://www.owasp.org/index.php</url>
+    </organization>
+
+    <mailingLists>
+        <mailingList>
+            <name>ESAPI-Users</name>
+            <subscribe>https://lists.owasp.org/mailman/listinfo/esapi-user/</subscribe>
+            <unsubscribe>https://lists.owasp.org/mailman/listinfo/esapi-user/</unsubscribe>
+            <post>mailto:esapi-users at lists.owasp.org</post>
+            <archive>https://lists.owasp.org/pipermail/esapi-users/</archive>
+            <!--This is the OWASP ESAPI mailing list for ESAPI users, regardless of programming language. For example, ESAPI users with questions about ESAPI for Java or ESAPI for PHP would both post here.-->
+        </mailingList>
+        <mailingList>
+            <name>ESAPI-Developers</name>
+            <subscribe>https://lists.owasp.org/mailman/listinfo/esapi-dev/</subscribe>
+            <unsubscribe>https://lists.owasp.org/mailman/listinfo/esapi-dev/</unsubscribe>
+            <post>mailto:esapi-dev at lists.owasp.org</post>
+            <archive>https://lists.owasp.org/pipermail/esapi-dev/</archive>
+            <!--This is the OWASP ESAPI mailing list for ESAPI for Java developers. While the list is not closed, the topics of discussion are likely to be less relevant to those only using ESAPI. Note that this is the list for ESAPI for Java. Most other language implementations, such ESAPI for PHP, have their own mailing lists.-->
+        </mailingList>
+        <mailingList>
+            <name>OWASP-ESAPI (Inactive! Archive only!)</name>
+            <archive>https://lists.owasp.org/pipermail/owasp-esapi/</archive>
+            <!--The name of the obsolete mailing list that previously was a combination of an ESAPI users lists and ESAP development list. While obsolete, it is still sometimes useful for searching through old historical posts. NOTE: NEW POSTS SHOULD NO LONGER BE MADE TO THIS LIST!-->
+        </mailingList>
+    </mailingLists>
+
+    <scm>
+        <connection>scm:svn:http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0</connection>
+        <developerConnection>scm:svn:https://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0</developerConnection>
+        <url>http://code.google.com/p/owasp-esapi-java/source/checkout/tags/esapi-2.1.0</url>
+    </scm>
+
+    <issueManagement>
+        <system>Google Code Issue Tracking</system>
+        <url>http://code.google.com/p/owasp-esapi-java/issues/list</url>
+    </issueManagement>
+
+    <developers>
+        <developer>
+            <name>Jeff Williams</name>
+            <organization>Aspect Security</organization>
+            <roles>
+                <role>Project Owner</role>
+                <role>Architect</role>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Jim Manico</name>
+            <roles>
+                <role>Project Manager</role>
+                <role>BuildMaster</role>
+                <role>Developer</role>
+                <role>Architect</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Chris Schmidt</name>
+            <organization>Aspect Security</organization>
+            <roles>
+                <role>Project Manager</role>
+                <role>Continuous Integration Admin</role>
+                <role>Architect</role>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Kevin W. Wall</name>
+            <organization>Wells Fargo</organization>
+            <roles>
+                <role>Project Manager</role>
+                <role>Architect</role>
+                <role>Developer</role>
+                <role>Crypto Guy</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <contributors>
+        <contributor>
+            <name>Dave Wichers</name>
+            <organization>Aspect Security</organization>
+            <roles>
+                <role>Documentation and Examples</role>
+                <role>Minor Code Contributions</role>
+                <role>Project Guidance</role>
+            </roles>
+        </contributor>
+    </contributors>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>commons-configuration</groupId>
+            <artifactId>commons-configuration</artifactId>
+            <version>1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils-core</artifactId>
+            <version>1.7.0</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>jsp-api</artifactId>
+            <version>2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <!-- unused according to dependancy:analyze
+           this is a optional dependency of commons-fileupload which
+           is at least needed for testing...
+        -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.16</version>
+            <scope>compile</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>xom</groupId>
+            <artifactId>xom</artifactId>
+            <version>1.2.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.beanshell</groupId>
+            <artifactId>bsh-core</artifactId>
+            <version>2.0b4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.owasp.antisamy</groupId>
+            <artifactId>antisamy</artifactId>
+            <version>1.4.3</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                    <debug>true</debug>
+                    <showWarnings>true</showWarnings>
+                    <showDeprecation>false</showDeprecation>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-eclipse-plugin</artifactId>
+                <version>2.8</version>
+                <configuration>
+                    <downloadSources>true</downloadSources>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.3.1</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <!-- Check for updates to dependencies and report on them. -->
+<!--
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>versions-maven-plugin</artifactId>
+                <version>1.2</version>
+                <executions>
+                    <execution>
+                        <id>check-for-dependency-updates</id>
+                        <phase>compile</phase> <!– Eclipse q4e plugin needs this –>
+                        <goals>
+                            <goal>display-dependency-updates</goal>
+                            <goal>display-plugin-updates</goal>
+                            <goal>display-property-updates</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+-->
+        </plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>2.3.1</version>
+                <configuration>
+                    <findbugsXmlOutput>true</findbugsXmlOutput>
+                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+                    <xmlOutput>true</xmlOutput>
+                    <threshold>Low</threshold>
+                    <effort>Max</effort>
+                    <relaxed>false</relaxed>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <formats>
+                        <format>html</format>
+                        <format>xml</format>
+                    </formats>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jdepend-maven-plugin</artifactId>
+                <version>2.0-beta-2</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>2.5</version>
+                <configuration>
+                    <targetJdk>1.5</targetJdk>
+                    <sourceEncoding>utf-8</sourceEncoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.7</version>
+                <configuration>
+                    <detectJavaApiLink>false</detectJavaApiLink>
+                    <detectLinks>false</detectLinks>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>net.sourceforge.maven-taglib</groupId>
+                <artifactId>maven-taglib-plugin</artifactId>
+                <version>2.4</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-changelog-plugin</artifactId>
+                <version>2.2</version>
+                <configuration>
+                    <issueIDRegexPattern>[Ii]ssue[# ]*(\d)+</issueIDRegexPattern>
+                    <issueLinkUrl>http://code.google.com/p/owasp-esapi-java/issues/detail?id=%ISSUE%</issueLinkUrl>
+                    <type>date</type>
+                    <dates>
+                        <date>2011-05-11 00:00:00</date>
+                    </dates>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>versions-maven-plugin</artifactId>
+                <version>1.2</version>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>dependency-updates-report</report>
+                            <report>plugin-updates-report</report>
+                            <report>property-updates-report</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <profiles>
+        <profile>
+            <!-- Activate to sign jars and build distributable download. -->
+            <id>dist</id>
+
+            <!-- This profile is activated when mvn release:perform is called from the command line
+                 to actually do a release. If you need this profile active for some reason outside
+                 of performing a release, use mvn <command> -Pdist
+            -->
+            <activation>
+                <property>
+                    <name>performRelease</name>
+                    <value>true</value>
+                </property>
+            </activation>
+
+            <distributionManagement>
+                <snapshotRepository>
+                    <id>sonatype-nexus-snapshots</id>
+                    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+                </snapshotRepository>
+                <repository>
+                    <id>sonatype-nexus-staging</id>
+                    <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
+                </repository>
+            </distributionManagement>
+
+            <build>
+                <plugins>
+
+                    <!-- Signs all artifacts prior to deploying to maven central -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>deploy</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                                <configuration>
+                                    <keyname>9F460575</keyname>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+					
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-jar-plugin</artifactId>
+						<version>2.3.1</version>
+						<executions>
+							<execution>
+								<phase>package</phase>
+								<goals>
+									<goal>sign</goal>
+								</goals>
+							</execution>
+						</executions>
+						<configuration>
+							<keystore>codesign.keystore</keystore>
+							<alias>owasp foundation, inc.'s godaddy.com, inc. id</alias>
+							<verify>true</verify>
+							<archive>
+								<manifest>
+									<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+									<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+								</manifest>
+								<manifestEntries>
+									<Sealed>true</Sealed>
+								</manifestEntries>
+							</archive>
+						</configuration>
+					</plugin>
+					
+                    <!-- Baseline for ESAPI 2.0 is JDK1.5 - Enforces that a JDK1.5 compiler is being
+                         used to build this release -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>enforce-jdk-version</id>
+                                <goals>
+                                    <goal>enforce</goal>
+                                </goals>
+                                <configuration>
+                                    <rules>
+                                        <requireJavaVersion>
+                                            <version>1.5</version>
+                                            <message>
+                                                ESAPI 2.0 uses the JDK1.5 for it's baseline. Please make sure that your
+                                                JAVA_HOME environment variable is pointed to a JDK1.5 distribution.
+                                            </message>
+                                        </requireJavaVersion>
+                                    </rules>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Attached JavaDocs are required by Sonatype Nexus Repository -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>attach-javadocs</id>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- For building the distribution zip file. -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <version>2.2</version>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>src/main/assembly/dist.xml</descriptor>
+                            </descriptors>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>make-dist</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>single</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Performs a full release. See release documentation for information on how to
+                         perform an ESAPI release using Maven -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-release-plugin</artifactId>
+                        <version>2.1</version>
+                        <configuration>
+                            <tagBase>https://owasp-esapi-java.googlecode.com/svn/tags/releases/</tagBase>
+                        </configuration>
+                    </plugin>
+
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5d0f11c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,12 @@
+The BSD License
+
+Copyright (c) 2007, The OWASP Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
+Neither the name of the OWASP Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFI [...]
+
diff --git a/LICENSE-CONTENT b/LICENSE-CONTENT
new file mode 100644
index 0000000..9d154ce
--- /dev/null
+++ b/LICENSE-CONTENT
@@ -0,0 +1,78 @@
+Creative Commons
+Creative Commons Legal Code
+Attribution-ShareAlike 3.0 Unported
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. 
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+
+1. Definitions
+
+   1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be [...]
+   2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works [...]
+   3. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License  [...]
+   4. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
+   5. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
+   6. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
+   7. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer bei [...]
+   8. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or withou [...]
+   9. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+  10. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the perform [...]
+  11. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
+
+2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
+
+   1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
+   2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
+   3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
+   4. to Distribute and Publicly Perform Adaptations.
+   5.
+
+      For the avoidance of doubt:
+         1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
+         2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
+         3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
+
+The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
+
+4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
+
+   1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. Y [...]
+   2. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you [...]
+   3. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for att [...]
+   4. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the righ [...]
+
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCL [...]
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+   1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+   2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this Lic [...]
+
+8. Miscellaneous
+
+   1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+   2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
+   3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+   4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
+   5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
+   6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction i [...]
+
+    Creative Commons Notice
+
+    Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all r [...]
+
+    Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request fro [...]
+
+    Creative Commons may be contacted at http://creativecommons.org/.
+
diff --git a/LICENSE-README b/LICENSE-README
new file mode 100644
index 0000000..e7295e1
--- /dev/null
+++ b/LICENSE-README
@@ -0,0 +1,7 @@
+Please note that:
+
+1) The LICENSE file only refers to the licensing of the source and binary code of ESAPI. 
+   For example, the actual ESAPI JAR file is only licensed under "The BSD License".
+   
+2) The LICENSE-CONTENT file only refers to the licensing of the content and documentation of ESAPI. 
+   For example, the documentation directory is only licensed under the Creative Commons/ShareAlike 3.0 Unported license.
\ No newline at end of file
diff --git a/ant-javadoc.xml b/ant-javadoc.xml
new file mode 100644
index 0000000..084d4d1
--- /dev/null
+++ b/ant-javadoc.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project default="javadoc">
+    <target name="javadoc">
+        <javadoc access="private" additionalparam="-J-Xmx768m " author="true" classpath="C:\Users\kww\.m2\repository\org\beanshell\bsh-core\2.0b4\bsh-core-2.0b4.jar;C:\Users\kww\.m2\repository\xalan\xalan\2.7.0\xalan-2.7.0.jar;C:\Users\kww\.m2\repository\logkit\logkit\1.0.1\logkit-1.0.1.jar;C:\Users\kww\.m2\repository\commons-io\commons-io\1.3\commons-io-1.3.jar;C:\Users\kww\.m2\repository\commons-collections\commons-collections\3.2\commons-collections-3.2.jar;C:\Users\kww\.m2\repository [...]
+            <link href="http://java.sun.com/javase/7/docs/api/"/>
+        </javadoc>
+    </target>
+</project>
diff --git a/configuration/.svn/all-wcprops b/configuration/.svn/all-wcprops
new file mode 100644
index 0000000..9e27d2d
--- /dev/null
+++ b/configuration/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration
+END
+log4j.xml
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/log4j.xml
+END
+log4j.dtd
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/log4j.dtd
+END
diff --git a/configuration/.svn/entries b/configuration/.svn/entries
new file mode 100644
index 0000000..0ac1f2e
--- /dev/null
+++ b/configuration/.svn/entries
@@ -0,0 +1,105 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/configuration
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-07-25T05:56:06.650568Z
+1857
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+log4j.dtd
+file
+
+
+
+
+2014-02-18T16:19:53.565978Z
+8ac4c1b0a927e184ef859bb4366dc0a6
+2010-10-07T04:07:35.229541Z
+1550
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6718
+

+properties
+dir
+

+log4j.xml
+file
+
+
+
+
+2014-02-18T16:19:53.565978Z
+d64979178f0d8abe7da173b595adec42
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1323
+

+META-INF
+dir
+

+esapi
+dir
+

diff --git a/configuration/.svn/text-base/log4j.dtd.svn-base b/configuration/.svn/text-base/log4j.dtd.svn-base
new file mode 100644
index 0000000..1aabd96
--- /dev/null
+++ b/configuration/.svn/text-base/log4j.dtd.svn-base
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Authors: Chris Taylor, Ceki Gulcu. -->
+
+<!-- Version: 1.2 -->
+
+<!-- A configuration element consists of optional renderer
+elements,appender elements, categories and an optional root
+element. -->
+
+<!ELEMENT log4j:configuration (renderer*, appender*,plugin*, (category|logger)*,root?,
+                               (categoryFactory|loggerFactory)?)>
+
+<!-- The "threshold" attribute takes a level value below which -->
+<!-- all logging statements are disabled. -->
+
+<!-- Setting the "debug" enable the printing of internal log4j logging   -->
+<!-- statements.                                                         -->
+
+<!-- By default, debug attribute is "null", meaning that we not do touch -->
+<!-- internal log4j logging settings. The "null" value for the threshold -->
+<!-- attribute can be misleading. The threshold field of a repository	 -->
+<!-- cannot be set to null. The "null" value for the threshold attribute -->
+<!-- simply means don't touch the threshold field, the threshold field   --> 
+<!-- keeps its old value.                                                -->
+     
+<!ATTLIST log4j:configuration
+  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  threshold                (all|trace|debug|info|warn|error|fatal|off|null) "null"
+  debug                    (true|false|null)  "null"
+  reset                    (true|false) "false"
+>
+
+<!-- renderer elements allow the user to customize the conversion of  -->
+<!-- message objects to String.                                       -->
+
+<!ELEMENT renderer EMPTY>
+<!ATTLIST renderer
+  renderedClass  CDATA #REQUIRED
+  renderingClass CDATA #REQUIRED
+>
+
+<!-- Appenders must have a name and a class. -->
+<!-- Appenders may contain an error handler, a layout, optional parameters -->
+<!-- and filters. They may also reference (or include) other appenders. -->
+<!ELEMENT appender (errorHandler?, param*,
+      rollingPolicy?, triggeringPolicy?, connectionSource?,
+      layout?, filter*, appender-ref*)>
+<!ATTLIST appender
+  name 		CDATA 	#REQUIRED
+  class 	CDATA	#REQUIRED
+>
+
+<!ELEMENT layout (param*)>
+<!ATTLIST layout
+  class		CDATA	#REQUIRED
+>
+
+<!ELEMENT filter (param*)>
+<!ATTLIST filter
+  class		CDATA	#REQUIRED
+>
+
+<!-- ErrorHandlers can be of any class. They can admit any number of -->
+<!-- parameters. -->
+
+<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)> 
+<!ATTLIST errorHandler
+   class        CDATA   #REQUIRED 
+>
+
+<!ELEMENT root-ref EMPTY>
+
+<!ELEMENT logger-ref EMPTY>
+<!ATTLIST logger-ref
+  ref CDATA #REQUIRED
+>
+
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+  name		CDATA   #REQUIRED
+  value		CDATA	#REQUIRED
+>
+
+
+<!-- The priority class is org.apache.log4j.Level by default -->
+<!ELEMENT priority (param*)>
+<!ATTLIST priority
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+<!-- The level class is org.apache.log4j.Level by default -->
+<!ELEMENT level (param*)>
+<!ATTLIST level
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named category. -->
+<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
+<!ATTLIST category
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named logger. -->
+<!ELEMENT logger (level?,appender-ref*)>
+<!ATTLIST logger
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+
+<!ELEMENT categoryFactory (param*)>
+<!ATTLIST categoryFactory 
+   class        CDATA #REQUIRED>
+
+<!ELEMENT loggerFactory (param*)>
+<!ATTLIST loggerFactory
+   class        CDATA #REQUIRED>
+
+<!ELEMENT appender-ref EMPTY>
+<!ATTLIST appender-ref
+  ref CDATA #REQUIRED
+>
+
+<!-- plugins must have a name and class and can have optional parameters -->
+<!ELEMENT plugin (param*, connectionSource?)>
+<!ATTLIST plugin
+  name 		CDATA 	   #REQUIRED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT connectionSource (dataSource?, param*)>
+<!ATTLIST connectionSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT dataSource (param*)>
+<!ATTLIST dataSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT triggeringPolicy ((param|filter)*)>
+<!ATTLIST triggeringPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT rollingPolicy (param*)>
+<!ATTLIST rollingPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+
+<!-- If no priority element is specified, then the configurator MUST not -->
+<!-- touch the priority of root. -->
+<!-- The root category always exists and cannot be subclassed. -->
+<!ELEMENT root (param*, (priority|level)?, appender-ref*)>
+
+
+<!-- ==================================================================== -->
+<!--                       A logging event                                -->
+<!-- ==================================================================== -->
+<!ELEMENT log4j:eventSet (log4j:event*)>
+<!ATTLIST log4j:eventSet
+  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  version                (1.1|1.2) "1.2" 
+  includesLocationInfo   (true|false) "true"
+>
+
+
+
+<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?, 
+                       log4j:locationInfo?, log4j:properties?) >
+
+<!-- The timestamp format is application dependent. -->
+<!ATTLIST log4j:event
+    logger     CDATA #REQUIRED
+    level      CDATA #REQUIRED
+    thread     CDATA #REQUIRED
+    timestamp  CDATA #REQUIRED
+    time       CDATA #IMPLIED
+>
+
+<!ELEMENT log4j:message (#PCDATA)>
+<!ELEMENT log4j:NDC (#PCDATA)>
+
+<!ELEMENT log4j:throwable (#PCDATA)>
+
+<!ELEMENT log4j:locationInfo EMPTY>
+<!ATTLIST log4j:locationInfo
+  class  CDATA	#REQUIRED
+  method CDATA	#REQUIRED
+  file   CDATA	#REQUIRED
+  line   CDATA	#REQUIRED
+>
+
+<!ELEMENT log4j:properties (log4j:data*)>
+
+<!ELEMENT log4j:data EMPTY>
+<!ATTLIST log4j:data
+  name   CDATA	#REQUIRED
+  value  CDATA	#REQUIRED
+>
diff --git a/configuration/.svn/text-base/log4j.xml.svn-base b/configuration/.svn/text-base/log4j.xml.svn-base
new file mode 100644
index 0000000..6f895d5
--- /dev/null
+++ b/configuration/.svn/text-base/log4j.xml.svn-base
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!-- main resources -->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="console" class="org.apache.log4j.ConsoleAppender"> 
+    <param name="Target" value="System.out"/> 
+    <layout class="org.apache.log4j.PatternLayout"> 
+      <param name="ConversionPattern" value="%-5p %m%n"/> 
+    </layout> 
+  </appender> 
+
+  <logger name="org.owasp.esapi.reference.TestTrace">
+    <level value="trace"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestDebug">
+    <level value="debug"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestInfo">
+    <level value="info"/>
+ </logger>
+
+  <logger name="org.owasp.esapi.reference.TestWarning">
+    <level value="warn"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestError">
+    <level value="error"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestFatal">
+    <level value="fatal"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference">
+    <level value="info"/>
+  </logger>
+
+  <root> 
+    <priority value ="debug" /> 
+    <appender-ref ref="console" /> 
+  </root>
+
+  <loggerFactory class="org.owasp.esapi.reference.Log4JLoggerFactory"/>
+  
+</log4j:configuration>
diff --git a/configuration/META-INF/.svn/all-wcprops b/configuration/META-INF/.svn/all-wcprops
new file mode 100644
index 0000000..9b986b7
--- /dev/null
+++ b/configuration/META-INF/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/META-INF
+END
+esapi.tld
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/META-INF/esapi.tld
+END
diff --git a/configuration/META-INF/.svn/entries b/configuration/META-INF/.svn/entries
new file mode 100644
index 0000000..e4260f1
--- /dev/null
+++ b/configuration/META-INF/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/configuration/META-INF
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-10-07T04:07:35.229541Z
+1550
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+esapi.tld
+file
+
+
+
+
+2014-02-18T16:19:53.513977Z
+dbb942adbf01ea1e352de9da5fbe8486
+2009-11-04T19:32:57.034029Z
+745
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10161
+

diff --git a/configuration/META-INF/.svn/text-base/esapi.tld.svn-base b/configuration/META-INF/.svn/text-base/esapi.tld.svn-base
new file mode 100644
index 0000000..596acb9
--- /dev/null
+++ b/configuration/META-INF/.svn/text-base/esapi.tld.svn-base
@@ -0,0 +1,371 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ OWASP Enterprise Security API (ESAPI)
+  ~
+  ~ This file is part of the Open Web Application Security Project (OWASP)
+  ~ Enterprise Security API (ESAPI) project. For details, please see
+  ~ <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+  ~
+  ~ Copyright (c) 2007 - The OWASP Foundation
+  ~
+  ~ The ESAPI is published by OWASP under the BSD license. You should read and accept the
+  ~ LICENSE before you use, modify, and/or redistribute this software.
+  ~
+  ~ @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+  ~ @created 2007
+  -->
+
+<taglib
+	xmlns="http://java.sun.com/xml/ns/j2ee"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="
+		http://java.sun.com/xml/ns/j2ee
+		http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+	version="2.0">
+	<description>
+		OWASP Enterprise Security API (ESAPI) provides
+		a JSP Tag Library that supplies easy access to
+		encoding functionality in the form of JSP Tags and EL
+		functions. These can be used to properly escape user
+		supplied data at display time so that it cannot be used
+		in injection attacks like Cross Site Scripting (XSS).
+	</description>
+	<display-name>OWASP ESAPI</display-name>
+	<tlib-version>2.0</tlib-version>
+	<short-name>esapi</short-name>
+	<uri>
+		http://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API
+	</uri>
+
+	<tag>
+		<description>
+			Encode tag's content using Base64
+		</description>
+		<display-name>Encode For Base64</display-name>
+		<name>encodeForBase64</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForBase64Tag
+		</tag-class>
+		<body-content>JSP</body-content>
+		<attribute>
+			<description>
+				The encoding used to convert the tag
+				content from a String to byte[]. The
+				default is UTF-8.
+			</description>
+			<name>encoding</name>
+		</attribute>
+		<attribute>
+			<description>
+				Whether lines should be wrapped at 64
+				characters. The default is false.
+			</description>
+			<name>wrap</name>
+			<type>boolean</type>
+		</attribute>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in CSS
+		</description>
+		<display-name>Encode For CSS</display-name>
+		<name>encodeForCSS</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForCSSTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in HTML
+		</description>
+		<display-name>Encode For HTML</display-name>
+		<name>encodeForHTML</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForHTMLTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in HTML Attributes
+		</description>
+		<display-name>Encode For HTML Attribute</display-name>
+		<name>encodeForHTMLAttribute</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForHTMLAttributeTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+	
+	<tag>
+		<description>
+			Encode tag's content for usage in JavaScript
+		</description>
+		<display-name>Encode For JavaScript</display-name>
+		<name>encodeForJavaScript</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForJavaScriptTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in URLs
+		</description>
+		<display-name>Encode For URL</display-name>
+		<name>encodeForURL</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForURLTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+	
+	<tag>
+		<description>
+			Encode tag's content for usage in VBScript
+		</description>
+		<display-name>Encode For VBScript</display-name>
+		<name>encodeForVBScript</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForVBScriptTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in XML Attributes
+		</description>
+		<display-name>Encode For XML Attribute</display-name>
+		<name>encodeForXMLAttribute</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForXMLAttributeTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in XML
+		</description>
+		<display-name>Encode For XML</display-name>
+		<name>encodeForXML</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForXMLTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in XPath
+		</description>
+		<display-name>Encode For XPath</display-name>
+		<name>encodeForXPath</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForXPathTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<function>
+		<description>
+			Encodes argument in Base64. UTF-8 is used to
+			convert the argument from a String to byte[]
+			before encoding. Lines are not wrapped.
+		</description>
+		<display-name>Encode For Base64</display-name>
+		<name>encodeForBase64</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64(java.lang.String)
+		</function-signature>
+	</function>
+	
+	<function>
+		<description>
+			Encodes argument in Base64. UTF-8 is used to
+			convert the argument from a String to byte[]
+			before encoding. Lines are wrapped at 64 characters.
+		</description>
+		<display-name>
+			Encode For Base64 with Line Wrapping
+		</display-name>
+		<name>encodeForBase64Wrap</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64Wrap(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the second argument in Base64. The first
+			argument is used as the character set used to
+			convert the argument from a String to byte[]
+			before encoding. Lines are not wrapped.
+		</description>
+		<display-name>
+			Encode For Base64 Using Charset
+		</display-name>
+		<name>encodeForBase64Charset</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64Charset(
+				java.lang.String,
+				java.lang.String)
+		</function-signature>
+	</function>
+	
+	<function>
+		<description>
+			Encodes the second argument in Base64. The
+			first argument is used as the character set
+			used to convert the argument from a String to
+			byte[] before encoding. Lines are wrapped at
+			64 characters.
+		</description>
+		<display-name>
+			Encode For Base64 Using Charset
+		</display-name>
+		<name>encodeForBase64CharsetWrap</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64CharsetWrap(
+				java.lang.String,
+				java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in CSS.
+		</description>
+		<display-name>Encode For Use in CSS</display-name>
+		<name>encodeForCSS</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForCSS(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in HTML.
+		</description>
+		<display-name>Encode For Use in HTML</display-name>
+		<name>encodeForHTML</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForHTML(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in HTML Attributes.
+		</description>
+		<display-name>Encode For Use in HTML Attributes</display-name>
+		<name>encodeForHTMLAttribute</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForHTMLAttribute(
+				java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in JavaScript.
+		</description>
+		<display-name>Encode For Use in JavaScript</display-name>
+		<name>encodeForJavaScript</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForJavaScript(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in URLs.
+		</description>
+		<display-name>Encode For Use in URLs</display-name>
+		<name>encodeForURL</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForURL(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in VBScript.
+		</description>
+		<display-name>Encode For Use in VBScript</display-name>
+		<name>encodeForVBScript</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForVBScript(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in XML.
+		</description>
+		<display-name>Encode For Use in XML</display-name>
+		<name>encodeForXML</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForXML(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in XML Attributes.
+		</description>
+		<display-name>Encode For Use in XML Attributes</display-name>
+		<name>encodeForXMLAttribute</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForXMLAttribute(
+				java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in XPath.
+		</description>
+		<display-name>Encode For Use in XPath</display-name>
+		<name>encodeForXPath</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForXPath(java.lang.String)
+		</function-signature>
+	</function>
+</taglib>
diff --git a/configuration/META-INF/esapi.tld b/configuration/META-INF/esapi.tld
new file mode 100644
index 0000000..596acb9
--- /dev/null
+++ b/configuration/META-INF/esapi.tld
@@ -0,0 +1,371 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ OWASP Enterprise Security API (ESAPI)
+  ~
+  ~ This file is part of the Open Web Application Security Project (OWASP)
+  ~ Enterprise Security API (ESAPI) project. For details, please see
+  ~ <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+  ~
+  ~ Copyright (c) 2007 - The OWASP Foundation
+  ~
+  ~ The ESAPI is published by OWASP under the BSD license. You should read and accept the
+  ~ LICENSE before you use, modify, and/or redistribute this software.
+  ~
+  ~ @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+  ~ @created 2007
+  -->
+
+<taglib
+	xmlns="http://java.sun.com/xml/ns/j2ee"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="
+		http://java.sun.com/xml/ns/j2ee
+		http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+	version="2.0">
+	<description>
+		OWASP Enterprise Security API (ESAPI) provides
+		a JSP Tag Library that supplies easy access to
+		encoding functionality in the form of JSP Tags and EL
+		functions. These can be used to properly escape user
+		supplied data at display time so that it cannot be used
+		in injection attacks like Cross Site Scripting (XSS).
+	</description>
+	<display-name>OWASP ESAPI</display-name>
+	<tlib-version>2.0</tlib-version>
+	<short-name>esapi</short-name>
+	<uri>
+		http://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API
+	</uri>
+
+	<tag>
+		<description>
+			Encode tag's content using Base64
+		</description>
+		<display-name>Encode For Base64</display-name>
+		<name>encodeForBase64</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForBase64Tag
+		</tag-class>
+		<body-content>JSP</body-content>
+		<attribute>
+			<description>
+				The encoding used to convert the tag
+				content from a String to byte[]. The
+				default is UTF-8.
+			</description>
+			<name>encoding</name>
+		</attribute>
+		<attribute>
+			<description>
+				Whether lines should be wrapped at 64
+				characters. The default is false.
+			</description>
+			<name>wrap</name>
+			<type>boolean</type>
+		</attribute>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in CSS
+		</description>
+		<display-name>Encode For CSS</display-name>
+		<name>encodeForCSS</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForCSSTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in HTML
+		</description>
+		<display-name>Encode For HTML</display-name>
+		<name>encodeForHTML</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForHTMLTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in HTML Attributes
+		</description>
+		<display-name>Encode For HTML Attribute</display-name>
+		<name>encodeForHTMLAttribute</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForHTMLAttributeTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+	
+	<tag>
+		<description>
+			Encode tag's content for usage in JavaScript
+		</description>
+		<display-name>Encode For JavaScript</display-name>
+		<name>encodeForJavaScript</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForJavaScriptTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in URLs
+		</description>
+		<display-name>Encode For URL</display-name>
+		<name>encodeForURL</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForURLTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+	
+	<tag>
+		<description>
+			Encode tag's content for usage in VBScript
+		</description>
+		<display-name>Encode For VBScript</display-name>
+		<name>encodeForVBScript</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForVBScriptTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in XML Attributes
+		</description>
+		<display-name>Encode For XML Attribute</display-name>
+		<name>encodeForXMLAttribute</name>
+		<tag-class>
+			org.owasp.esapi.tags.EncodeForXMLAttributeTag
+		</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in XML
+		</description>
+		<display-name>Encode For XML</display-name>
+		<name>encodeForXML</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForXMLTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<tag>
+		<description>
+			Encode tag's content for usage in XPath
+		</description>
+		<display-name>Encode For XPath</display-name>
+		<name>encodeForXPath</name>
+		<tag-class>org.owasp.esapi.tags.EncodeForXPathTag</tag-class>
+		<body-content>JSP</body-content>
+	</tag>
+
+	<function>
+		<description>
+			Encodes argument in Base64. UTF-8 is used to
+			convert the argument from a String to byte[]
+			before encoding. Lines are not wrapped.
+		</description>
+		<display-name>Encode For Base64</display-name>
+		<name>encodeForBase64</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64(java.lang.String)
+		</function-signature>
+	</function>
+	
+	<function>
+		<description>
+			Encodes argument in Base64. UTF-8 is used to
+			convert the argument from a String to byte[]
+			before encoding. Lines are wrapped at 64 characters.
+		</description>
+		<display-name>
+			Encode For Base64 with Line Wrapping
+		</display-name>
+		<name>encodeForBase64Wrap</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64Wrap(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the second argument in Base64. The first
+			argument is used as the character set used to
+			convert the argument from a String to byte[]
+			before encoding. Lines are not wrapped.
+		</description>
+		<display-name>
+			Encode For Base64 Using Charset
+		</display-name>
+		<name>encodeForBase64Charset</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64Charset(
+				java.lang.String,
+				java.lang.String)
+		</function-signature>
+	</function>
+	
+	<function>
+		<description>
+			Encodes the second argument in Base64. The
+			first argument is used as the character set
+			used to convert the argument from a String to
+			byte[] before encoding. Lines are wrapped at
+			64 characters.
+		</description>
+		<display-name>
+			Encode For Base64 Using Charset
+		</display-name>
+		<name>encodeForBase64CharsetWrap</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForBase64CharsetWrap(
+				java.lang.String,
+				java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in CSS.
+		</description>
+		<display-name>Encode For Use in CSS</display-name>
+		<name>encodeForCSS</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForCSS(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in HTML.
+		</description>
+		<display-name>Encode For Use in HTML</display-name>
+		<name>encodeForHTML</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForHTML(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in HTML Attributes.
+		</description>
+		<display-name>Encode For Use in HTML Attributes</display-name>
+		<name>encodeForHTMLAttribute</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForHTMLAttribute(
+				java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in JavaScript.
+		</description>
+		<display-name>Encode For Use in JavaScript</display-name>
+		<name>encodeForJavaScript</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForJavaScript(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in URLs.
+		</description>
+		<display-name>Encode For Use in URLs</display-name>
+		<name>encodeForURL</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForURL(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in VBScript.
+		</description>
+		<display-name>Encode For Use in VBScript</display-name>
+		<name>encodeForVBScript</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForVBScript(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in XML.
+		</description>
+		<display-name>Encode For Use in XML</display-name>
+		<name>encodeForXML</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForXML(java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in XML Attributes.
+		</description>
+		<display-name>Encode For Use in XML Attributes</display-name>
+		<name>encodeForXMLAttribute</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForXMLAttribute(
+				java.lang.String)
+		</function-signature>
+	</function>
+
+	<function>
+		<description>
+			Encodes the argument for use in XPath.
+		</description>
+		<display-name>Encode For Use in XPath</display-name>
+		<name>encodeForXPath</name>
+		<function-class>
+			org.owasp.esapi.tags.ELEncodeFunctions
+		</function-class>
+		<function-signature>
+			java.lang.String encodeForXPath(java.lang.String)
+		</function-signature>
+	</function>
+</taglib>
diff --git a/configuration/esapi/.svn/all-wcprops b/configuration/esapi/.svn/all-wcprops
new file mode 100644
index 0000000..a95d234
--- /dev/null
+++ b/configuration/esapi/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi
+END
+users.txt
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/users.txt
+END
+ESAPI-AccessControlPolicy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/ESAPI-AccessControlPolicy.xml
+END
+antisamy-esapi.xml
+K 25
+svn:wc:ra_dav:version-url
+V 74
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/antisamy-esapi.xml
+END
+validation.properties
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/validation.properties
+END
+waf-policy.xsd
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policy.xsd
+END
+ESAPI.properties
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/ESAPI.properties
+END
diff --git a/configuration/esapi/.svn/entries b/configuration/esapi/.svn/entries
new file mode 100644
index 0000000..74e7d4e
--- /dev/null
+++ b/configuration/esapi/.svn/entries
@@ -0,0 +1,235 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/configuration/esapi
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-07-25T05:56:06.650568Z
+1857
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+waf-policies
+dir
+

+antisamy-esapi.xml
+file
+
+
+
+
+2014-02-18T16:19:53.557978Z
+1ae3bef5c1f2cf729dbbbe00585a6446
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+31816
+

+validation.properties
+file
+
+
+
+
+2014-02-18T16:19:53.557978Z
+88f9ba326a0102788da15201535d3271
+2010-11-07T05:06:49.492325Z
+1652
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1646
+

+waf-policy.xsd
+file
+
+
+
+
+2014-02-18T16:19:53.557978Z
+b958b1b47d55477a419eeab31f4bf4b2
+2009-11-07T17:48:23.955569Z
+756
+arshan.dabirsiaghi
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+20582
+

+ESAPI.properties
+file
+
+
+
+
+2014-02-18T16:19:53.557978Z
+1128f765993a4464b6df531c55b8d3d2
+2011-07-25T05:56:06.650568Z
+1857
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+24448
+

+users.txt
+file
+
+
+
+
+2014-02-18T16:19:53.557978Z
+d41d8cd98f00b204e9800998ecf8427e
+2009-06-06T15:14:24.195945Z
+531
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+0
+

+ESAPI-AccessControlPolicy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.557978Z
+05b199b9241e5fc2fdf41f8b85698ead
+2009-06-06T15:14:24.195945Z
+531
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5357
+

diff --git a/configuration/esapi/.svn/prop-base/waf-policy.xsd.svn-base b/configuration/esapi/.svn/prop-base/waf-policy.xsd.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/configuration/esapi/.svn/prop-base/waf-policy.xsd.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/configuration/esapi/.svn/text-base/ESAPI-AccessControlPolicy.xml.svn-base b/configuration/esapi/.svn/text-base/ESAPI-AccessControlPolicy.xml.svn-base
new file mode 100644
index 0000000..2ed0732
--- /dev/null
+++ b/configuration/esapi/.svn/text-base/ESAPI-AccessControlPolicy.xml.svn-base
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<AccessControlPolicy>
+	<AccessControlRules>
+		<AccessControlRule
+			name="AlwaysTrue"
+			description="Access is always granted"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="AlwaysFalse"
+			description="Access is always denied"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameter"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoRuntimeParameterACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoPolicyParameter"
+			description="Access depends on the value of the policy parameter: isTrue"
+			class="org.owasp.esapi.reference.accesscontrol.policyloader.EchoDynaBeanPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="true"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- 
+			The following Rules are for backwards compatibility with
+			the deprecated AcessController 1.0 reference implementation
+			specification
+		-->
+		<AccessControlRule
+			name="AC 1.0 Data"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/> 
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForData"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String, java.lang.Object"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 File"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFile"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Function"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFunction"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Service"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForService"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 URL"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForURL"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- End Rules for backwards compatibility with Access Controller 1.0 -->
+	</AccessControlRules>
+</AccessControlPolicy>
+
+
+
+<!-- 
+We have these as runtime tests, but not as policy file load tests yet.
+		<AccessControlRule
+			name="EchoRuntimeParameterClassCastException"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="This is not a boolean"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueNull"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="null"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueEmpty"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value=""/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueMissing"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean"/>
+			</Parameters>
+		</AccessControlRule>
+-->
+
+<!-- we should add tests for name and type errors too -->
diff --git a/configuration/esapi/.svn/text-base/ESAPI.properties.svn-base b/configuration/esapi/.svn/text-base/ESAPI.properties.svn-base
new file mode 100644
index 0000000..49e63c6
--- /dev/null
+++ b/configuration/esapi/.svn/text-base/ESAPI.properties.svn-base
@@ -0,0 +1,452 @@
+#
+# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version
+# 
+# This file is part of the Open Web Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2008,2009 - The OWASP Foundation
+#
+# DISCUSS: This may cause a major backwards compatibility issue, etc. but
+#		   from a name space perspective, we probably should have prefaced
+#		   all the property names with ESAPI or at least OWASP. Otherwise
+#		   there could be problems is someone loads this properties file into
+#		   the System properties.  We could also put this file into the
+#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
+#		   ESAPI properties be defined that would overwrite these defaults.
+#		   That keeps the application's properties relatively simple as usually
+#		   they will only want to override a few properties. If looks like we
+#		   already support multiple override levels of this in the
+#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
+#		   defaults in the esapi.jar itself. That way, if the jar is signed,
+#		   we could detect if those properties had been tampered with. (The
+#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
+#		   but off course there is an execution penalty (similar to the way
+#		   that the separate sunjce.jar used to be when a class from it was
+#		   first loaded). Thoughts?
+###############################################################################
+#
+# WARNING: Operating system protection should be used to lock down the .esapi
+# resources directory and all the files inside and all the directories all the
+# way up to the root directory of the file system.  Note that if you are using
+# file-based implementations, that some files may need to be read-write as they
+# get updated dynamically.
+#
+# Before using, be sure to update the MasterKey and MasterSalt as described below.
+# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
+#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
+#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
+#		able to decrypt your data with ESAPI 2.0.
+#
+#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
+#
+#===========================================================================
+# ESAPI Configuration
+#
+# If true, then print all the ESAPI properties set here when they are loaded.
+# If false, they are not printed. Useful to reduce output when running JUnit tests.
+# If you need to troubleshoot a properties related problem, turning this on may help.
+# This is 'false' in the src/test/resources/.esapi version. It is 'true' by
+# default for reasons of backward compatibility with earlier ESAPI versions.
+ESAPI.printProperties=true
+
+# ESAPI is designed to be easily extensible. You can use the reference implementation
+# or implement your own providers to take advantage of your enterprise's security
+# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
+#
+#    String ciphertext =
+#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
+#    CipherText cipherText =
+#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
+#
+# Below you can specify the classname for the provider that you wish to use in your
+# application. The only requirement is that it implement the appropriate ESAPI interface.
+# This allows you to switch security implementations in the future without rewriting the
+# entire application.
+#
+# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
+ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
+# FileBasedAuthenticator requires users.txt file in .esapi directory
+ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
+ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
+ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
+
+ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
+ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
+ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
+# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
+ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
+#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
+ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
+ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
+
+#===========================================================================
+# ESAPI Authenticator
+#
+Authenticator.AllowedLoginAttempts=3
+Authenticator.MaxOldPasswordHashes=13
+Authenticator.UsernameParameterName=username
+Authenticator.PasswordParameterName=password
+# RememberTokenDuration (in days)
+Authenticator.RememberTokenDuration=14
+# Session Timeouts (in minutes)
+Authenticator.IdleTimeoutDuration=20
+Authenticator.AbsoluteTimeoutDuration=120
+
+#===========================================================================
+# ESAPI Encoder
+#
+# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
+# Failure to canonicalize input is a very common mistake when implementing validation schemes.
+# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
+# following code to canonicalize data.
+#
+#      ESAPI.Encoder().canonicalize( "%22hello world&#x22;" );
+#  
+# Multiple encoding is when a single encoding format is applied multiple times. Allowing
+# multiple encoding is strongly discouraged.
+Encoder.AllowMultipleEncoding=false
+
+# Mixed encoding is when multiple different encoding formats are applied, or when 
+# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
+Encoder.AllowMixedEncoding=false
+
+# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
+# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
+# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
+Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
+
+
+#===========================================================================
+# ESAPI Encryption
+#
+# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
+# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+# There is not currently any support for key rotation, so be careful when changing your key and salt as it
+# will invalidate all signed, encrypted, and hashed data.
+#
+# WARNING: Not all combinations of algorithms and key lengths are supported.
+# If you choose to use a key length greater than 128, you MUST download the
+# unlimited strength policy files and install in the lib directory of your JRE/JDK.
+# See http://java.sun.com/javase/downloads/index.jsp for more information.
+#
+# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
+# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
+# possible, these methods should be avoided as they use ECB cipher mode, which in almost
+# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
+# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
+# should only use this compatibility setting if you have persistent data encrypted with
+# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
+# you have decrypted all of your old encrypted data and then re-encrypted it with
+# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
+# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
+# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
+# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
+# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
+# that requires downloading the special jurisdiction policy files mentioned above.)
+#
+#		***** IMPORTANT: Do NOT forget to replace these with your own values! *****
+# To calculate these values, you can run:
+#		java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+#
+#Encryptor.MasterKey=
+#Encryptor.MasterSalt=
+
+# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
+# encryption and hashing. (That is it will look to this provider first, but it
+# will defer to other providers if the requested algorithm is not implemented
+# by this provider.) If left unset, ESAPI will just use your Java VM's current
+# preferred JCE provider, which is generally set in the file
+# "$JAVA_HOME/jre/lib/security/java.security".
+#
+# The main intent of this is to allow ESAPI symmetric encryption to be
+# used with a FIPS 140-2 compliant crypto-module. For details, see the section
+# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
+# the ESAPI 2.0 Symmetric Encryption User Guide, at:
+# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
+# However, this property also allows you to easily use an alternate JCE provider
+# such as "Bouncy Castle" without having to make changes to "java.security".
+# See Javadoc for SecurityProviderLoader for further details. If you wish to use
+# a provider that is not known to SecurityProviderLoader, you may specify the
+# fully-qualified class name of the JCE provider class that implements
+# java.security.Provider. If the name contains a '.', this is interpreted as
+# a fully-qualified class name that implements java.security.Provider.
+#
+# NOTE: Setting this property has the side-effect of changing it in your application
+#       as well, so if you are using JCE in your application directly rather than
+#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
+#       preferred JCE provider there as well.
+#
+# Default: Keeps the JCE provider set to whatever JVM sets it to.
+Encryptor.PreferredJCEProvider=
+
+# AES is the most widely used and strongest encryption algorithm. This
+# should agree with your Encryptor.CipherTransformation property.
+# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
+# very weak. It is essentially a password-based encryption key, hashed
+# with MD5 around 1K times and then encrypted with the weak DES algorithm
+# (56-bits) using ECB mode and an unspecified padding (it is
+# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
+# "AES/CBC/PKCSPadding". If you want to change these, change them here.
+# Warning: This property does not control the default reference implementation for
+#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
+#		   in the future.
+# @deprecated
+Encryptor.EncryptionAlgorithm=AES
+#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
+Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
+
+# Applies to ESAPI 2.0 and later only!
+# Comma-separated list of cipher modes that provide *BOTH*
+# confidentiality *AND* message authenticity. (NIST refers to such cipher
+# modes as "combined modes" so that's what we shall call them.) If any of these
+# cipher modes are used then no MAC is calculated and stored
+# in the CipherText upon encryption. Likewise, if one of these
+# cipher modes is used with decryption, no attempt will be made
+# to validate the MAC contained in the CipherText object regardless
+# of whether it contains one or not. Since the expectation is that
+# these cipher modes support support message authenticity already,
+# injecting a MAC in the CipherText object would be at best redundant.
+#
+# Note that as of JDK 1.5, the SunJCE provider does not support *any*
+# of these cipher modes. Of these listed, only GCM and CCM are currently
+# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
+# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
+# padding modes.
+Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
+
+# Applies to ESAPI 2.0 and later only!
+# Additional cipher modes allowed for ESAPI 2.0 encryption. These
+# cipher modes are in _addition_ to those specified by the property
+# 'Encryptor.cipher_modes.combined_modes'.
+# Note: We will add support for streaming modes like CFB & OFB once
+# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
+# (probably in ESAPI 2.1).
+# DISCUSS: Better name?
+Encryptor.cipher_modes.additional_allowed=CBC
+
+# 128-bit is almost always sufficient and appears to be more resistant to
+# related key attacks than is 256-bit AES. Use '_' to use default key size
+# for cipher algorithms (where it makes sense because the algorithm supports
+# a variable key size). Key length must agree to what's provided as the
+# cipher transformation, otherwise this will be ignored after logging a
+# warning.
+#
+# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
+Encryptor.EncryptionKeyLength=128
+
+# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
+# (All cipher modes except ECB require an IV.) There are two choices: we can either
+# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
+# the IV does not need to be hidden from adversaries, it is important that the
+# adversary not be allowed to choose it. Also, random IVs are generally much more
+# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
+# such as CFB and OFB use a different IV for each encryption with a given key so
+# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
+# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
+# uncomment the Encryptor.fixedIV.
+#
+# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
+Encryptor.ChooseIVMethod=random
+# If you choose to use a fixed IV, then you must place a fixed IV here that
+# is known to all others who are sharing your secret key. The format should
+# be a hex string that is the same length as the cipher block size for the
+# cipher algorithm that you are using. The following is an *example* for AES
+# from an AES test vector for AES-128/CBC as described in:
+# NIST Special Publication 800-38A (2001 Edition)
+# "Recommendation for Block Cipher Modes of Operation".
+# (Note that the block size for AES is 16 bytes == 128 bits.)
+#
+Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
+
+# Whether or not CipherText should use a message authentication code (MAC) with it.
+# This prevents an adversary from altering the IV as well as allowing a more
+# fool-proof way of determining the decryption failed because of an incorrect
+# key being supplied. This refers to the "separate" MAC calculated and stored
+# in CipherText, not part of any MAC that is calculated as a result of a
+# "combined mode" cipher mode.
+#
+# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
+# set this property to false.
+Encryptor.CipherText.useMAC=true
+
+# Whether or not the PlainText object may be overwritten and then marked
+# eligible for garbage collection. If not set, this is still treated as 'true'.
+Encryptor.PlainText.overwrite=true
+
+# Do not use DES except in a legacy situations. 56-bit is way too small key size.
+#Encryptor.EncryptionKeyLength=56
+#Encryptor.EncryptionAlgorithm=DES
+
+# TripleDES is considered strong enough for most purposes.
+#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
+#			requires downloading the special jurisdiction policy from Sun.
+#Encryptor.EncryptionKeyLength=168
+#Encryptor.EncryptionAlgorithm=DESede
+
+Encryptor.HashAlgorithm=SHA-512
+Encryptor.HashIterations=1024
+Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
+Encryptor.DigitalSignatureKeyLength=1024
+Encryptor.RandomAlgorithm=SHA1PRNG
+Encryptor.CharacterEncoding=UTF-8
+
+# This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function
+# (KDF) normally uses. Note this is *only* the PRF used for ESAPI's KDF and
+# *not* what is used for ESAPI's MAC. (Currently, HmacSHA1 is always used for
+# the MAC, mostly to keep the overall size at a minimum.)
+#
+# Currently supported choices for JDK 1.5 and 1.6 are:
+#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
+#	HmacSHA512 (512 bits).
+# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
+# the JDKs support it.  See the ESAPI 2.0 Symmetric Encryption User Guide
+# further details.
+Encryptor.KDF.PRF=HmacSHA256
+#===========================================================================
+# ESAPI HttpUtilties
+#
+# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
+# protect against malicious data from attackers, such as unprintable characters, escaped characters,
+# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
+# headers, and CSRF tokens.
+#
+# Default file upload location (remember to escape backslashes with \\)
+HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
+HttpUtilities.UploadTempDir=C:\\temp
+# Force flags on cookies, if you use HttpUtilities to set cookies
+HttpUtilities.ForceHttpOnlySession=false
+HttpUtilities.ForceSecureSession=false
+HttpUtilities.ForceHttpOnlyCookies=true
+HttpUtilities.ForceSecureCookies=true
+# Maximum size of HTTP headers
+HttpUtilities.MaxHeaderSize=4096
+# File upload configuration
+HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
+HttpUtilities.MaxUploadFileBytes=500000000
+# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
+# container, and any other technologies you may be using. Failure to do this may expose you
+# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
+HttpUtilities.ResponseContentType=text/html; charset=UTF-8
+# This is the name of the cookie used to represent the HTTP session
+# Typically this will be the default "JSESSIONID" 
+HttpUtilities.HttpSessionIdName=JSESSIONID
+
+
+
+#===========================================================================
+# ESAPI Executor
+# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
+Executor.WorkingDirectory=C:\\Windows\\Temp
+Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
+
+
+#===========================================================================
+# ESAPI Logging
+# Set the application name if these logs are combined with other applications
+Logger.ApplicationName=ExampleApplication
+# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
+Logger.LogEncodingRequired=false
+# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
+Logger.LogApplicationName=true
+# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
+Logger.LogServerIP=true
+# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
+# want to place it in a specific directory.
+Logger.LogFileName=ESAPI_logging_file
+# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
+Logger.MaxLogFileSize=10000000
+
+
+#===========================================================================
+# ESAPI Intrusion Detection
+#
+# Each event has a base to which .count, .interval, and .action are added
+# The IntrusionException will fire if we receive "count" events within "interval" seconds
+# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
+#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
+#
+# Custom Events
+# Names must start with "event." as the base
+# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
+# You can also disable intrusion detection completely by changing
+# the following parameter to true
+#
+IntrusionDetector.Disable=false
+#
+IntrusionDetector.event.test.count=2
+IntrusionDetector.event.test.interval=10
+IntrusionDetector.event.test.actions=disable,log
+
+# Exception Events
+# All EnterpriseSecurityExceptions are registered automatically
+# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
+# Use the fully qualified classname of the exception as the base
+
+# any intrusion is an attack
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
+
+# for test purposes
+# CHECKME: Shouldn't there be something in the property name itself that designates
+#		   that these are for testing???
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
+
+# rapid validation errors indicate scans or attacks in progress
+# org.owasp.esapi.errors.ValidationException.count=10
+# org.owasp.esapi.errors.ValidationException.interval=10
+# org.owasp.esapi.errors.ValidationException.actions=log,logout
+
+# sessions jumping between hosts indicates session hijacking
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
+
+
+#===========================================================================
+# ESAPI Validation
+#
+# The ESAPI Validator works on regular expressions with defined names. You can define names
+# either here, or you may define application specific patterns in a separate file defined below.
+# This allows enterprises to specify both organizational standards as well as application specific
+# validation rules.
+#
+Validator.ConfigurationFile=validation.properties
+
+# Validators used by ESAPI
+Validator.AccountName=^[a-zA-Z0-9]{3,20}$
+Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
+Validator.RoleName=^[a-z]{1,20}$
+
+#the word TEST below should be changed to your application 
+#name - only relative URL's are supported
+Validator.Redirect=^\\/test.*$
+
+# Global HTTP Validation Rules
+# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
+Validator.HTTPScheme=^(http|https)$
+Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
+Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$
+Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$
+Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
+Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
+Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$
+Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPURL=^.*$
+Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
+
+# Validation of file related input
+Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+
+# Validation of dates. Controls whether or not 'lenient' dates are accepted.
+# See DataFormat.setLenient(boolean flag) for further details.
+Validator.AcceptLenientDates=false
\ No newline at end of file
diff --git a/configuration/esapi/.svn/text-base/antisamy-esapi.xml.svn-base b/configuration/esapi/.svn/text-base/antisamy-esapi.xml.svn-base
new file mode 100644
index 0000000..500ab59
--- /dev/null
+++ b/configuration/esapi/.svn/text-base/antisamy-esapi.xml.svn-base
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+	
+<!-- 
+W3C rules retrieved from:
+http://www.w3.org/TR/html401/struct/global.html
+-->
+	
+<!--
+Slashdot allowed tags taken from "Reply" page:
+<b> <i> <p> <br> <a> <ol> <ul> <li> <dl> <dt> <dd> <em> <strong> <tt> <blockquote> <div> <ecode> <quote>
+-->
+
+<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xsi:noNamespaceSchemaLocation="antisamy.xsd">
+	
+	<directives>
+		<directive name="omitXmlDeclaration" value="true"/>
+		<directive name="omitDoctypeDeclaration" value="true"/>
+		<directive name="maxInputSize" value="500000"/>
+		<directive name="embedStyleSheets" value="false"/>
+	</directives>
+	
+	
+	<common-regexps>
+		
+		<!-- 
+		From W3C:
+		This attribute assigns a class name or set of class names to an
+		element. Any number of elements may be assigned the same class
+		name or names. Multiple class names must be separated by white 
+		space characters.
+		-->
+		
+		<regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' -->
+		<regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/>
+		<regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/>
+	
+	</common-regexps>
+	
+	<!-- 
+	
+	Tag.name = a, b, div, body, etc.
+	Tag.action = filter: remove tags, but keep content, validate: keep content as long as it passes rules, remove: remove tag and contents
+	Attribute.name = id, class, href, align, width, etc.
+	Attribute.onInvalid = what to do when the attribute is invalid, e.g., remove the tag (removeTag), remove the attribute (removeAttribute), filter the tag (filterTag)
+	Attribute.description = What rules in English you want to tell the users they can have for this attribute. Include helpful things so they'll be able to tune their HTML
+	 
+	 -->
+
+	<!-- 
+	Some attributes are common to all (or most) HTML tags. There aren't many that qualify for this. You have to make sure there's no
+	collisions between any of these attribute names with attribute names of other tags that are for different purposes.
+	-->
+
+	<common-attributes>
+		
+
+		<attribute name="lang" description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
+		 	<regexp-list>
+		 		<regexp value="[a-zA-Z]{2,20}"/>
+		 	</regexp-list>
+		 </attribute>
+		 
+		 <attribute name="title" description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
+		 	<regexp-list>
+		 		<regexp name="htmlTitle"/>
+		 	</regexp-list>
+		 </attribute>
+
+		<attribute name="href" onInvalid="filterTag">
+			<regexp-list>
+				<regexp name="onsiteURL"/>
+				<regexp name="offsiteURL"/>
+			</regexp-list>
+		</attribute>
+	
+		<attribute name="align" description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
+			<literal-list>
+				<literal value="center"/>
+				<literal value="left"/>
+				<literal value="right"/>
+				<literal value="justify"/>
+				<literal value="char"/>
+			</literal-list>
+		</attribute>
+
+	</common-attributes>
+
+
+	<!--
+	This requires normal updates as browsers continue to diverge from the W3C and each other. As long as the browser wars continue
+	this is going to continue. I'm not sure war is the right word for what's going on. Doesn't somebody have to win a war after 
+	a while?
+	 -->
+	
+	<global-tag-attributes>
+		<attribute name="title"/>
+		<attribute name="lang"/>
+	</global-tag-attributes>
+
+
+	<tag-rules>
+
+		<!-- Tags related to JavaScript -->
+
+		<tag name="script" action="remove"/>
+		<tag name="noscript" action="remove"/>
+		
+		<!-- Frame & related tags -->
+		
+		<tag name="iframe" action="remove"/>
+		<tag name="frameset" action="remove"/>
+		<tag name="frame" action="remove"/>
+		<tag name="noframes" action="remove"/>
+		
+
+		<!-- All reasonable formatting tags -->
+		
+		<tag name="p" action="validate">
+			<attribute name="align"/>
+		</tag>
+
+		<tag name="div" action="validate"/>		
+		<tag name="i" action="validate"/>
+		<tag name="b" action="validate"/>
+		<tag name="em" action="validate"/>
+		<tag name="blockquote" action="validate"/>
+		<tag name="tt" action="validate"/>
+		
+		<tag name="br" action="truncate"/>
+
+		<!-- Custom Slashdot tags, though we're trimming the idea of having a possible mismatching end tag with the endtag="" attribute -->
+		
+		<tag name="quote" action="validate"/>
+		<tag name="ecode" action="validate"/> 
+		
+						
+		<!-- Anchor and anchor related tags -->
+		
+		<tag name="a" action="validate">
+
+			<attribute name="href" onInvalid="filterTag"/>
+			<attribute name="nohref">
+				<literal-list>
+					<literal value="nohref"/>
+					<literal value=""/>
+				</literal-list>
+			</attribute>
+			<attribute name="rel">
+				<literal-list>
+					<literal value="nofollow"/>
+				</literal-list>
+			</attribute>
+		</tag>
+
+		<!-- List tags -->
+
+		<tag name="ul" action="validate"/>
+		<tag name="ol" action="validate"/>
+		<tag name="li" action="validate"/>
+		
+	</tag-rules>
+
+
+
+	<!--  No CSS on Slashdot posts -->
+
+	<css-rules>
+	</css-rules>
+
+
+	<html-entities>
+		<entity name="amp" cdata="&"/>
+		<entity name="nbsp" cdata="&#160;"/>
+		
+		<entity name="iexcl" cdata="&#161;"/> <!--inverted exclamation mark, U+00A1 ISOnum -->
+		<entity name="cent" cdata="&#162;"/> <!--cent sign, U+00A2 ISOnum -->
+		<entity name="pound" cdata="&#163;"/> <!--pound sign, U+00A3 ISOnum -->
+		<entity name="curren" cdata="&#164;"/> <!--currency sign, U+00A4 ISOnum -->
+		<entity name="yen" cdata="&#165;"/> <!--yen sign = yuan sign, U+00A5 ISOnum -->
+		<entity name="brvbar" cdata="&#166;"/> <!--broken bar = broken vertical bar, U+00A6 ISOnum -->
+		<entity name="sect" cdata="&#167;"/> <!--section sign, U+00A7 ISOnum -->
+		<entity name="uml" cdata="&#168;"/> <!--diaeresis = spacing diaeresis, U+00A8 ISOdia -->
+		<entity name="copy" cdata="&#169;"/> <!--copyright sign, U+00A9 ISOnum -->
+		<entity name="ordf" cdata="&#170;"/> <!--feminine ordinal indicator, U+00AA ISOnum -->
+		<entity name="laquo" cdata="&#171;"/> <!--left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum -->
+		<entity name="not" cdata="&#172;"/> <!--not sign, U+00AC ISOnum -->
+		<entity name="shy" cdata="&#173;"/> <!--soft hyphen = discretionary hyphen,U+00AD ISOnum -->
+		<entity name="reg" cdata="&#174;"/> <!--registered sign = registered trade mark sign, U+00AE ISOnum -->
+		<entity name="macr" cdata="&#175;"/> <!--macron = spacing macron = overline = APL overbar, U+00AF ISOdia -->
+		<entity name="deg" cdata="&#176;"/> <!--degree sign, U+00B0 ISOnum -->
+		<entity name="plusmn" cdata="&#177;"/> <!--plus-minus sign = plus-or-minus sign, U+00B1 ISOnum -->
+		<entity name="sup2" cdata="&#178;"/> <!--superscript two = superscript digit two = squared, U+00B2 ISOnum -->
+		<entity name="sup3" cdata="&#179;"/> <!--superscript three = superscript digit three= cubed, U+00B3 ISOnum -->
+		<entity name="acute" cdata="&#180;"/> <!--acute accent = spacing acute, U+00B4 ISOdia -->
+		<entity name="micro" cdata="&#181;"/> <!--micro sign, U+00B5 ISOnum -->
+		<entity name="para" cdata="&#182;"/> <!--pilcrow sign = paragraph sign, U+00B6 ISOnum -->
+		<entity name="middot" cdata="&#183;"/> <!--middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum -->
+		<entity name="cedil" cdata="&#184;"/> <!--cedilla = spacing cedilla, U+00B8 ISOdia -->
+		<entity name="sup1" cdata="&#185;"/> <!--superscript one = superscript digit one,U+00B9 ISOnum -->
+		<entity name="ordm" cdata="&#186;"/> <!--masculine ordinal indicator, U+00BA ISOnum -->
+		<entity name="raquo" cdata="&#187;"/> <!--right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum -->
+		<entity name="frac14" cdata="&#188;"/> <!--vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum -->
+		<entity name="frac12" cdata="&#189;"/> <!--vulgar fraction one half = fraction one half, U+00BD ISOnum -->
+		<entity name="frac34" cdata="&#190;"/> <!--vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum -->
+		<entity name="iquest" cdata="&#191;"/> <!--inverted question mark = turned question mark, U+00BF ISOnum -->
+		<entity name="Agrave" cdata="&#192;"/> <!--latin capital letter A with grave = latin capital letter A grave,U+00C0 ISOlat1 -->
+		<entity name="Aacute" cdata="&#193;"/> <!--latin capital letter A with acute,U+00C1 ISOlat1 -->
+		<entity name="Acirc" cdata="&#194;"/> <!--latin capital letter A with circumflex,U+00C2 ISOlat1 -->
+		<entity name="Atilde" cdata="&#195;"/> <!--latin capital letter A with tilde,U+00C3 ISOlat1 -->
+		<entity name="Auml" cdata="&#196;"/> <!--latin capital letter A with diaeresis,U+00C4 ISOlat1 -->
+		<entity name="Aring" cdata="&#197;"/> <!--latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 -->
+		<entity name="AElig" cdata="&#198;"/> <!--latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 -->
+		<entity name="Ccedil" cdata="&#199;"/> <!--latin capital letter C with cedilla, U+00C7 ISOlat1 -->
+		<entity name="Egrave" cdata="&#200;"/> <!--latin capital letter E with grave, U+00C8 ISOlat1 -->
+		<entity name="Eacute" cdata="&#201;"/> <!--latin capital letter E with acute,U+00C9 ISOlat1 -->
+		<entity name="Ecirc" cdata="&#202;"/> <!--latin capital letter E with circumflex,U+00CA ISOlat1 -->
+		<entity name="Euml" cdata="&#203;"/> <!--latin capital letter E with diaeresis, U+00CB ISOlat1 -->
+		<entity name="Igrave" cdata="&#204;"/> <!--latin capital letter I with grave, U+00CC ISOlat1 -->
+		<entity name="Iacute" cdata="&#205;"/> <!--latin capital letter I with acute, U+00CD ISOlat1 -->
+		<entity name="Icirc" cdata="&#206;"/> <!--latin capital letter I with circumflex, U+00CE ISOlat1 -->
+		<entity name="Iuml" cdata="&#207;"/> <!--latin capital letter I with diaeresis, U+00CF ISOlat1 -->
+		<entity name="ETH" cdata="&#208;"/> <!--latin capital letter ETH, U+00D0 ISOlat1 -->
+		<entity name="Ntilde" cdata="&#209;"/> <!--latin capital letter N with tilde, U+00D1 ISOlat1 -->
+		<entity name="Ograve" cdata="&#210;"/> <!--latin capital letter O with grave, U+00D2 ISOlat1 -->
+		<entity name="Oacute" cdata="&#211;"/> <!--latin capital letter O with acute, U+00D3 ISOlat1 -->
+		<entity name="Ocirc" cdata="&#212;"/> <!--latin capital letter O with circumflex, U+00D4 ISOlat1 -->
+		<entity name="Otilde" cdata="&#213;"/> <!--latin capital letter O with tilde, U+00D5 ISOlat1 -->
+		<entity name="Ouml" cdata="&#214;"/> <!--latin capital letter O with diaeresis, U+00D6 ISOlat1 -->
+		<entity name="times" cdata="&#215;"/> <!--multiplication sign, U+00D7 ISOnum -->
+		<entity name="Oslash" cdata="&#216;"/> <!--latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 -->
+		<entity name="Ugrave" cdata="&#217;"/> <!--latin capital letter U with grave, U+00D9 ISOlat1 -->
+		<entity name="Uacute" cdata="&#218;"/> <!--latin capital letter U with acute, U+00DA ISOlat1 -->
+		<entity name="Ucirc" cdata="&#219;"/> <!--latin capital letter U with circumflex, U+00DB ISOlat1 -->
+		<entity name="Uuml" cdata="&#220;"/> <!--latin capital letter U with diaeresis, U+00DC ISOlat1 -->
+		<entity name="Yacute" cdata="&#221;"/> <!--latin capital letter Y with acute, U+00DD ISOlat1 -->
+		<entity name="THORN" cdata="&#222;"/> <!--latin capital letter THORN, U+00DE ISOlat1 -->
+		<entity name="szlig" cdata="&#223;"/> <!--latin small letter sharp s = ess-zed, U+00DF ISOlat1 -->
+		<entity name="agrave" cdata="&#224;"/> <!--latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 -->
+		<entity name="aacute" cdata="&#225;"/> <!--latin small letter a with acute, U+00E1 ISOlat1 -->
+		<entity name="acirc" cdata="&#226;"/> <!--latin small letter a with circumflex, U+00E2 ISOlat1 -->
+		<entity name="atilde" cdata="&#227;"/> <!--latin small letter a with tilde, U+00E3 ISOlat1 -->
+		<entity name="auml" cdata="&#228;"/> <!--latin small letter a with diaeresis, U+00E4 ISOlat1 -->
+		<entity name="aring" cdata="&#229;"/> <!--latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 -->
+		<entity name="aelig" cdata="&#230;"/> <!--latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 -->
+		<entity name="ccedil" cdata="&#231;"/> <!--latin small letter c with cedilla, U+00E7 ISOlat1 -->
+		<entity name="egrave" cdata="&#232;"/> <!--latin small letter e with grave, U+00E8 ISOlat1 -->
+		<entity name="eacute" cdata="&#233;"/> <!--latin small letter e with acute, U+00E9 ISOlat1 -->
+		<entity name="ecirc" cdata="&#234;"/> <!--latin small letter e with circumflex, U+00EA ISOlat1 -->
+		<entity name="euml" cdata="&#235;"/> <!--latin small letter e with diaeresis, U+00EB ISOlat1 -->
+		<entity name="igrave" cdata="&#236;"/> <!--latin small letter i with grave, U+00EC ISOlat1 -->
+		<entity name="iacute" cdata="&#237;"/> <!--latin small letter i with acute, U+00ED ISOlat1 -->
+		<entity name="icirc" cdata="&#238;"/> <!--latin small letter i with circumflex, U+00EE ISOlat1 -->
+		<entity name="iuml" cdata="&#239;"/> <!--latin small letter i with diaeresis, U+00EF ISOlat1 -->
+		<entity name="eth" cdata="&#240;"/> <!--latin small letter eth, U+00F0 ISOlat1 -->
+		<entity name="ntilde" cdata="&#241;"/> <!--latin small letter n with tilde, U+00F1 ISOlat1 -->
+		<entity name="ograve" cdata="&#242;"/> <!--latin small letter o with grave, U+00F2 ISOlat1 -->
+		<entity name="oacute" cdata="&#243;"/> <!--latin small letter o with acute, U+00F3 ISOlat1 -->
+		<entity name="ocirc " cdata="&#244;"/> <!--latin small letter o with circumflex, U+00F4 ISOlat1 -->
+		<entity name="otilde" cdata="&#245;"/> <!--latin small letter o with tilde, U+00F5 ISOlat1 -->
+		<entity name="ouml" cdata="&#246;"/> <!--latin small letter o with diaeresis, U+00F6 ISOlat1 -->
+		<entity name="divide" cdata="&#247;"/> <!--division sign, U+00F7 ISOnum -->
+		<entity name="oslash" cdata="&#248;"/> <!--latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 -->
+		<entity name="ugrave" cdata="&#249;"/> <!--latin small letter u with grave, U+00F9 ISOlat1 -->
+		<entity name="uacute" cdata="&#250;"/> <!--latin small letter u with acute, U+00FA ISOlat1 -->
+		<entity name="ucirc" cdata="&#251;"/> <!--latin small letter u with circumflex, U+00FB ISOlat1 -->
+		<entity name="uuml" cdata="&#252;"/> <!--latin small letter u with diaeresis, U+00FC ISOlat1 -->
+		<entity name="yacute" cdata="&#253;"/> <!--latin small letter y with acute, U+00FD ISOlat1 -->
+		<entity name="thorn" cdata="&#254;"/> <!--latin small letter thorn, U+00FE ISOlat1 -->
+		<entity name="yuml" cdata="&#255;"/> <!--latin small letter y with diaeresis, U+00FF ISOlat1 -->
+		                                  
+		<entity name="fnof" cdata="&#402;"/> <!--latin small f with hook = function = florin, U+0192 ISOtech -->
+		
+		<!-- Greek -->
+		<entity name="Alpha" cdata="&#913;"/> <!--greek capital letter alpha, U+0391 -->
+		<entity name="Beta" cdata="&#914;"/> <!--greek capital letter beta, U+0392 -->
+		<entity name="Gamma" cdata="&#915;"/> <!--greek capital letter gamma, U+0393 ISOgrk3 -->
+		<entity name="Delta" cdata="&#916;"/> <!--greek capital letter delta, U+0394 ISOgrk3 -->
+		<entity name="Epsilon" cdata="&#917;"/> <!--greek capital letter epsilon, U+0395 -->
+		<entity name="Zeta" cdata="&#918;"/> <!--greek capital letter zeta, U+0396 -->
+		<entity name="Eta" cdata="&#919;"/> <!--greek capital letter eta, U+0397 -->
+		<entity name="Theta" cdata="&#920;"/> <!--greek capital letter theta, U+0398 ISOgrk3 -->
+		<entity name="Iota" cdata="&#921;"/> <!--greek capital letter iota, U+0399 -->
+		<entity name="Kappa" cdata="&#922;"/> <!--greek capital letter kappa, U+039A -->
+		<entity name="Lambda" cdata="&#923;"/> <!--greek capital letter lambda, U+039B ISOgrk3 -->
+		<entity name="Mu" cdata="&#924;"/> <!--greek capital letter mu, U+039C -->
+		<entity name="Nu" cdata="&#925;"/> <!--greek capital letter nu, U+039D -->
+		<entity name="Xi" cdata="&#926;"/> <!--greek capital letter xi, U+039E ISOgrk3 -->
+		<entity name="Omicron" cdata="&#927;"/> <!--greek capital letter omicron, U+039F -->
+		<entity name="Pi" cdata="&#928;"/> <!--greek capital letter pi, U+03A0 ISOgrk3 -->
+		<entity name="Rho" cdata="&#929;"/> <!--greek capital letter rho, U+03A1 -->
+		<!-- there is no Sigmaf, and no U+03A2 character either -->
+		<entity name="Sigma" cdata="&#931;"/> <!--greek capital letter sigma, U+03A3 ISOgrk3 -->
+		<entity name="Tau" cdata="&#932;"/> <!--greek capital letter tau, U+03A4 -->
+		<entity name="Upsilon" cdata="&#933;"/> <!--greek capital letter upsilon,U+03A5 ISOgrk3 -->
+		<entity name="Phi" cdata="&#934;"/> <!--greek capital letter phi,U+03A6 ISOgrk3 -->
+		<entity name="Chi" cdata="&#935;"/> <!--greek capital letter chi, U+03A7 -->
+		<entity name="Psi" cdata="&#936;"/> <!--greek capital letter psi,U+03A8 ISOgrk3 -->
+		<entity name="Omega" cdata="&#937;"/> <!--greek capital letter omega,U+03A9 ISOgrk3 -->
+		
+		<entity name="alpha" cdata="&#945;"/> <!--greek small letter alpha,U+03B1 ISOgrk3 -->
+		<entity name="beta" cdata="&#946;"/> <!--greek small letter beta, U+03B2 ISOgrk3 -->
+		<entity name="gamma" cdata="&#947;"/> <!--greek small letter gamma,U+03B3 ISOgrk3 -->
+		<entity name="delta" cdata="&#948;"/> <!--greek small letter delta,U+03B4 ISOgrk3 -->
+		<entity name="epsilon" cdata="&#949;"/> <!--greek small letter epsilon,U+03B5 ISOgrk3 -->
+		<entity name="zeta" cdata="&#950;"/> <!--greek small letter zeta, U+03B6 ISOgrk3 -->
+		<entity name="eta" cdata="&#951;"/> <!--greek small letter eta, U+03B7 ISOgrk3 -->
+		<entity name="theta" cdata="&#952;"/> <!--greek small letter theta, U+03B8 ISOgrk3 -->
+		<entity name="iota" cdata="&#953;"/> <!--greek small letter iota, U+03B9 ISOgrk3 -->
+		<entity name="kappa" cdata="&#954;"/> <!--greek small letter kappa,U+03BA ISOgrk3 -->
+		<entity name="lambda" cdata="&#955;"/> <!--greek small letter lambda, U+03BB ISOgrk3 -->
+		<entity name="mu" cdata="&#956;"/> <!--greek small letter mu, U+03BC ISOgrk3 -->
+		<entity name="nu" cdata="&#957;"/> <!--greek small letter nu, U+03BD ISOgrk3 -->
+		<entity name="xi" cdata="&#958;"/> <!--greek small letter xi, U+03BE ISOgrk3 -->
+		<entity name="omicron" cdata="&#959;"/> <!--greek small letter omicron, U+03BF NEW -->
+		<entity name="pi" cdata="&#960;"/> <!--greek small letter pi, U+03C0 ISOgrk3 -->
+		<entity name="rho" cdata="&#961;"/> <!--greek small letter rho, U+03C1 ISOgrk3 -->
+		<entity name="sigmaf" cdata="&#962;"/> <!--greek small letter final sigma, U+03C2 ISOgrk3 -->
+		<entity name="sigma" cdata="&#963;"/> <!--greek small letter sigma, U+03C3 ISOgrk3 -->
+		<entity name="tau" cdata="&#964;"/> <!--greek small letter tau, U+03C4 ISOgrk3 -->
+		<entity name="upsilon" cdata="&#965;"/> <!--greek small letter upsilon, U+03C5 ISOgrk3 -->
+		<entity name="phi" cdata="&#966;"/> <!--greek small letter phi, U+03C6 ISOgrk3 -->
+		<entity name="chi" cdata="&#967;"/> <!--greek small letter chi, U+03C7 ISOgrk3 -->
+		<entity name="psi" cdata="&#968;"/> <!--greek small letter psi, U+03C8 ISOgrk3 -->
+		<entity name="omega" cdata="&#969;"/> <!--greek small letter omega, U+03C9 ISOgrk3 -->
+		<entity name="thetasym" cdata="&#977;"/> <!--greek small letter theta symbol, U+03D1 NEW -->
+		<entity name="upsih" cdata="&#978;"/> <!--greek upsilon with hook symbol, U+03D2 NEW -->
+		<entity name="piv" cdata="&#982;"/> <!--greek pi symbol, U+03D6 ISOgrk3 -->
+		
+		<!-- General Punctuation -->
+		<entity name="bull" cdata="&#8226;"/> <!--bullet = black small circle, U+2022 ISOpub  -->
+		<!-- bullet is NOT the same as bullet operator, U+2219 -->
+		<entity name="hellip" cdata="&#8230;"/> <!--horizontal ellipsis = three dot leader, U+2026 ISOpub  -->
+		<entity name="prime" cdata="&#8242;"/> <!--prime = minutes = feet, U+2032 ISOtech -->
+		<entity name="Prime" cdata="&#8243;"/> <!--double prime = seconds = inches, U+2033 ISOtech -->
+		<entity name="oline" cdata="&#8254;"/> <!--overline = spacing overscore, U+203E NEW -->
+		<entity name="frasl" cdata="&#8260;"/> <!--fraction slash, U+2044 NEW -->
+		
+		<!-- Letterlike Symbols -->
+		<entity name="weierp" cdata="&#8472;"/> <!--script capital P = power set = Weierstrass p, U+2118 ISOamso -->
+		<entity name="image" cdata="&#8465;"/> <!--blackletter capital I = imaginary part, U+2111 ISOamso -->
+		<entity name="real" cdata="&#8476;"/> <!--blackletter capital R = real part symbol, U+211C ISOamso -->
+		<entity name="trade" cdata="&#8482;"/> <!--trade mark sign, U+2122 ISOnum -->
+		<entity name="alefsym" cdata="&#8501;"/> <!--alef symbol = first transfinite cardinal, U+2135 NEW -->
+		<!-- alef symbol is NOT the same as hebrew letter alef,
+		     U+05D0 although the same glyph could be used to depict both characters -->
+		
+		<!-- Arrows -->
+		<entity name="larr" cdata="&#8592;"/> <!--leftwards arrow, U+2190 ISOnum -->
+		<entity name="uarr" cdata="&#8593;"/> <!--upwards arrow, U+2191 ISOnum-->
+		<entity name="rarr" cdata="&#8594;"/> <!--rightwards arrow, U+2192 ISOnum -->
+		<entity name="darr" cdata="&#8595;"/> <!--downwards arrow, U+2193 ISOnum -->
+		<entity name="harr" cdata="&#8596;"/> <!--left right arrow, U+2194 ISOamsa -->
+		<entity name="crarr" cdata="&#8629;"/> <!--downwards arrow with corner leftwards
+		                                     = carriage return, U+21B5 NEW -->
+		<entity name="lArr" cdata="&#8656;"/> <!--leftwards double arrow, U+21D0 ISOtech -->
+		
+		<!-- ISO 10646 does not say that lArr is the same as the 'is implied by' arrow
+		    but also does not have any other character for that function. So ? lArr can
+		    be used for 'is implied by' as ISOtech suggests -->
+		    
+		<entity name="uArr" cdata="&#8657;"/> <!--upwards double arrow, U+21D1 ISOamsa -->
+		<entity name="rArr" cdata="&#8658;"/> <!--rightwards double arrow, U+21D2 ISOtech -->
+		
+		<!-- ISO 10646 does not say this is the 'implies' character but does not have 
+		     another character with this function so ?
+		     rArr can be used for 'implies' as ISOtech suggests -->
+		     
+		<entity name="dArr" cdata="&#8659;"/> <!--downwards double arrow, U+21D3 ISOamsa -->
+		<entity name="hArr" cdata="&#8660;"/> <!--left right double arrow, U+21D4 ISOamsa -->
+		
+		<!-- Mathematical Operators -->
+		<entity name="forall" cdata="&#8704;"/> <!--for all, U+2200 ISOtech -->
+		<entity name="part" cdata="&#8706;"/> <!--partial differential, U+2202 ISOtech  -->
+		<entity name="exist" cdata="&#8707;"/> <!--there exists, U+2203 ISOtech -->
+		<entity name="empty" cdata="&#8709;"/> <!--empty set = null set = diameter,U+2205 ISOamso -->
+		<entity name="nabla" cdata="&#8711;"/> <!--nabla = backward difference, U+2207 ISOtech -->
+		<entity name="isin" cdata="&#8712;"/> <!--element of, U+2208 ISOtech -->
+		<entity name="notin" cdata="&#8713;"/> <!--not an element of, U+2209 ISOtech -->
+		<entity name="ni" cdata="&#8715;"/> <!--contains as member, U+220B ISOtech -->
+	
+		<!-- should there be a more memorable name than 'ni'? -->
+		<entity name="prod" cdata="&#8719;"/> <!--n-ary product = product sign, U+220F ISOamsb -->
+		
+		<!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though
+		     the same glyph might be used for both -->
+		     
+		<entity name="sum" cdata="&#8721;"/> <!--n-ary sumation, U+2211 ISOamsb -->
+		
+		<!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+		     though the same glyph might be used for both -->
+		
+		<entity name="minus" cdata="&#8722;"/> <!--minus sign, U+2212 ISOtech -->
+		<entity name="lowast" cdata="&#8727;"/> <!--asterisk operator, U+2217 ISOtech -->
+		<entity name="radic" cdata="&#8730;"/> <!--square root = radical sign, U+221A ISOtech -->
+		<entity name="prop" cdata="&#8733;"/> <!--proportional to, U+221D ISOtech -->
+		<entity name="infin" cdata="&#8734;"/> <!--infinity, U+221E ISOtech -->
+		<entity name="ang" cdata="&#8736;"/> <!--angle, U+2220 ISOamso -->
+		<entity name="and" cdata="&#8743;"/> <!--logical and = wedge, U+2227 ISOtech -->
+		<entity name="or" cdata="&#8744;"/> <!--logical or = vee, U+2228 ISOtech -->
+		<entity name="cap" cdata="&#8745;"/> <!--intersection = cap, U+2229 ISOtech -->
+		<entity name="cup" cdata="&#8746;"/> <!--union = cup, U+222A ISOtech -->
+		<entity name="int" cdata="&#8747;"/> <!--integral, U+222B ISOtech -->
+		<entity name="there4" cdata="&#8756;"/> <!--therefore, U+2234 ISOtech -->
+		<entity name="sim" cdata="&#8764;"/> <!--tilde operator = varies with = similar to, U+223C ISOtech -->
+		
+		<!-- tilde operator is NOT the same character as the tilde, U+007E,
+		     although the same glyph might be used to represent both  -->
+		     
+		<entity name="cong" cdata="&#8773;"/> <!--approximately equal to, U+2245 ISOtech -->
+		<entity name="asymp" cdata="&#8776;"/> <!--almost equal to = asymptotic to, U+2248 ISOamsr -->
+		<entity name="ne" cdata="&#8800;"/> <!--not equal to, U+2260 ISOtech -->
+		<entity name="equiv" cdata="&#8801;"/> <!--identical to, U+2261 ISOtech -->
+		<entity name="le" cdata="&#8804;"/> <!--less-than or equal to, U+2264 ISOtech -->
+		<entity name="ge" cdata="&#8805;"/> <!--greater-than or equal to, U+2265 ISOtech -->
+		<entity name="sub" cdata="&#8834;"/> <!--subset of, U+2282 ISOtech -->
+		<entity name="sup" cdata="&#8835;"/> <!--superset of, U+2283 ISOtech -->
+		
+		<!-- note that nsup, 'not a superset of, U+2283' is not covered by the Symbol 
+		     font encoding and is not included. Should it be, for symmetry?
+		     It is in ISOamsn  --> 
+		
+		<entity name="nsub" cdata="&#8836;"/> <!--not a subset of, U+2284 ISOamsn -->
+		<entity name="sube" cdata="&#8838;"/> <!--subset of or equal to, U+2286 ISOtech -->
+		<entity name="supe" cdata="&#8839;"/> <!--superset of or equal to, U+2287 ISOtech -->
+		<entity name="oplus" cdata="&#8853;"/> <!--circled plus = direct sum, U+2295 ISOamsb -->
+		<entity name="otimes" cdata="&#8855;"/> <!--circled times = vector product, U+2297 ISOamsb -->
+		<entity name="perp" cdata="&#8869;"/> <!--up tack = orthogonal to = perpendicular, U+22A5 ISOtech -->
+		<entity name="sdot" cdata="&#8901;"/> <!--dot operator, U+22C5 ISOamsb -->
+		<!-- dot operator is NOT the same character as U+00B7 middle dot -->
+		
+		<!-- Miscellaneous Technical -->
+		<entity name="lceil" cdata="&#8968;"/> <!--left ceiling = apl upstile, U+2308 ISOamsc  -->
+		<entity name="rceil" cdata="&#8969;"/> <!--right ceiling, U+2309 ISOamsc  -->
+		<entity name="lfloor" cdata="&#8970;"/> <!--left floor = apl downstile, U+230A ISOamsc  -->
+		<entity name="rfloor" cdata="&#8971;"/> <!--right floor, U+230B ISOamsc  -->
+		<entity name="lang" cdata="&#9001;"/> <!--left-pointing angle bracket = bra, U+2329 ISOtech -->
+		<!-- lang is NOT the same character as U+003C 'less than' 
+		     or U+2039 'single left-pointing angle quotation mark' -->
+		<entity name="rang" cdata="&#9002;"/> <!--right-pointing angle bracket = ket, U+232A ISOtech -->
+		<!-- rang is NOT the same character as U+003E 'greater than' or U+203A 'single right-pointing angle quotation mark' -->
+		
+		<!-- Geometric Shapes -->
+		<entity name="loz" cdata="&#9674;"/> <!--lozenge, U+25CA ISOpub -->
+		
+		<!-- Miscellaneous Symbols -->
+		<entity name="spades" cdata="&#9824;"/> <!--black spade suit, U+2660 ISOpub -->
+		<!-- black here seems to mean filled as opposed to hollow -->
+		<entity name="clubs" cdata="&#9827;"/> <!--black club suit = shamrock, U+2663 ISOpub -->
+		<entity name="hearts" cdata="&#9829;"/> <!--black heart suit = valentine, U+2665 ISOpub -->
+		<entity name="diams" cdata="&#9830;"/> <!--black diamond suit, U+2666 ISOpub -->
+		
+		<entity name="quot" cdata="&#34;"  /> <!--quotation mark = APL quote, U+0022 ISOnum -->
+		<!-- Latin Extended-A -->
+		<entity name="OElig" cdata="&#338;" /> <!--latin capital ligature OE, U+0152 ISOlat2 -->
+		<entity name="oelig" cdata="&#339;" /> <!--latin small ligature oe, U+0153 ISOlat2 -->
+		<!-- ligature is a misnomer, this is a separate character in some languages -->
+		<entity name="Scaron" cdata="&#352;" /> <!--latin capital letter S with caron, U+0160 ISOlat2 -->
+		<entity name="scaron" cdata="&#353;" /> <!--latin small letter s with caron, U+0161 ISOlat2 -->
+		<entity name="Yuml" cdata="&#376;" /> <!--latin capital letter Y with diaeresis, U+0178 ISOlat2 -->
+		
+		<!-- Spacing Modifier Letters -->
+		<entity name="circ" cdata="&#710;" /> <!--modifier letter circumflex accent, U+02C6 ISOpub -->
+		<entity name="tilde" cdata="&#732;" /> <!--small tilde, U+02DC ISOdia -->
+		
+		<!-- General Punctuation -->
+		<entity name="ensp" cdata="&#8194;"/> <!--en space, U+2002 ISOpub -->
+		<entity name="emsp" cdata="&#8195;"/> <!--em space, U+2003 ISOpub -->
+		<entity name="thinsp" cdata="&#8201;"/> <!--thin space, U+2009 ISOpub -->
+		<entity name="zwnj" cdata="&#8204;"/> <!--zero width non-joiner, U+200C NEW RFC 2070 -->
+		<entity name="zwj" cdata="&#8205;"/> <!--zero width joiner, U+200D NEW RFC 2070 -->
+		<entity name="lrm" cdata="&#8206;"/> <!--left-to-right mark, U+200E NEW RFC 2070 -->
+		<entity name="rlm" cdata="&#8207;"/> <!--right-to-left mark, U+200F NEW RFC 2070 -->
+		<entity name="ndash" cdata="&#8211;"/> <!--en dash, U+2013 ISOpub -->
+		<entity name="mdash" cdata="&#8212;"/> <!--em dash, U+2014 ISOpub -->
+		<entity name="lsquo" cdata="&#8216;"/> <!--left single quotation mark, U+2018 ISOnum -->
+		<entity name="rsquo" cdata="&#8217;"/> <!--right single quotation mark, U+2019 ISOnum -->
+		<entity name="sbquo" cdata="&#8218;"/> <!--single low-9 quotation mark, U+201A NEW -->
+		<entity name="ldquo" cdata="&#8220;"/> <!--left double quotation mark, U+201C ISOnum -->
+		<entity name="rdquo" cdata="&#8221;"/> <!--right double quotation mark, U+201D ISOnum -->
+		<entity name="bdquo" cdata="&#8222;"/> <!--double low-9 quotation mark, U+201E NEW -->
+		<entity name="dagger" cdata="&#8224;"/> <!--dagger, U+2020 ISOpub -->
+		<entity name="Dagger" cdata="&#8225;"/> <!--double dagger, U+2021 ISOpub -->
+		<entity name="permil" cdata="&#8240;"/> <!--per mille sign, U+2030 ISOtech -->
+		<entity name="lsaquo" cdata="&#8249;"/> <!--single left-pointing angle quotation mark, U+2039 ISO proposed -->
+		<!-- lsaquo is proposed but not yet ISO standardized -->
+		<entity name="rsaquo" cdata="&#8250;"/> <!--single right-pointing angle quotation mark, U+203A ISO proposed -->
+		<!-- rsaquo is proposed but not yet ISO standardized -->
+		<entity name="euro" cdata="&#8364;" /> <!--euro sign, U+20AC NEW -->
+	</html-entities>
+
+</anti-samy-rules>
diff --git a/configuration/esapi/.svn/text-base/users.txt.svn-base b/configuration/esapi/.svn/text-base/users.txt.svn-base
new file mode 100644
index 0000000..e69de29
diff --git a/configuration/esapi/.svn/text-base/validation.properties.svn-base b/configuration/esapi/.svn/text-base/validation.properties.svn-base
new file mode 100644
index 0000000..18e037f
--- /dev/null
+++ b/configuration/esapi/.svn/text-base/validation.properties.svn-base
@@ -0,0 +1,29 @@
+# The ESAPI validator does many security checks on input, such as canonicalization
+# and whitelist validation. Note that all of these validation rules are applied *after*
+# canonicalization. Double-encoded characters (even with different encodings involved,
+# are never allowed.
+#
+# To use:
+#
+# First set up a pattern below. You can choose any name you want, prefixed by the word
+# "Validation." For example:
+#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+# 
+# Then you can validate in your code against the pattern like this:
+#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
+# Where maxLength and allowNull are set for you needs, respectively.
+#
+# But note, when you use boolean variants of validation functions, you lose critical 
+# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
+# and use the returned user input which is in canonical form. Consider the following:
+#  
+# try {
+#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
+#
+Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
+Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
+Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
+Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
+Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
+
diff --git a/configuration/esapi/.svn/text-base/waf-policy.xsd.svn-base b/configuration/esapi/.svn/text-base/waf-policy.xsd.svn-base
new file mode 100644
index 0000000..4287e79
Binary files /dev/null and b/configuration/esapi/.svn/text-base/waf-policy.xsd.svn-base differ
diff --git a/configuration/esapi/ESAPI-AccessControlPolicy.xml b/configuration/esapi/ESAPI-AccessControlPolicy.xml
new file mode 100644
index 0000000..2ed0732
--- /dev/null
+++ b/configuration/esapi/ESAPI-AccessControlPolicy.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<AccessControlPolicy>
+	<AccessControlRules>
+		<AccessControlRule
+			name="AlwaysTrue"
+			description="Access is always granted"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="AlwaysFalse"
+			description="Access is always denied"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameter"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoRuntimeParameterACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoPolicyParameter"
+			description="Access depends on the value of the policy parameter: isTrue"
+			class="org.owasp.esapi.reference.accesscontrol.policyloader.EchoDynaBeanPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="true"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- 
+			The following Rules are for backwards compatibility with
+			the deprecated AcessController 1.0 reference implementation
+			specification
+		-->
+		<AccessControlRule
+			name="AC 1.0 Data"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/> 
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForData"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String, java.lang.Object"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 File"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFile"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Function"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFunction"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Service"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForService"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 URL"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForURL"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- End Rules for backwards compatibility with Access Controller 1.0 -->
+	</AccessControlRules>
+</AccessControlPolicy>
+
+
+
+<!-- 
+We have these as runtime tests, but not as policy file load tests yet.
+		<AccessControlRule
+			name="EchoRuntimeParameterClassCastException"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="This is not a boolean"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueNull"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="null"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueEmpty"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value=""/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueMissing"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean"/>
+			</Parameters>
+		</AccessControlRule>
+-->
+
+<!-- we should add tests for name and type errors too -->
diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties
new file mode 100644
index 0000000..49e63c6
--- /dev/null
+++ b/configuration/esapi/ESAPI.properties
@@ -0,0 +1,452 @@
+#
+# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version
+# 
+# This file is part of the Open Web Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2008,2009 - The OWASP Foundation
+#
+# DISCUSS: This may cause a major backwards compatibility issue, etc. but
+#		   from a name space perspective, we probably should have prefaced
+#		   all the property names with ESAPI or at least OWASP. Otherwise
+#		   there could be problems is someone loads this properties file into
+#		   the System properties.  We could also put this file into the
+#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
+#		   ESAPI properties be defined that would overwrite these defaults.
+#		   That keeps the application's properties relatively simple as usually
+#		   they will only want to override a few properties. If looks like we
+#		   already support multiple override levels of this in the
+#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
+#		   defaults in the esapi.jar itself. That way, if the jar is signed,
+#		   we could detect if those properties had been tampered with. (The
+#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
+#		   but off course there is an execution penalty (similar to the way
+#		   that the separate sunjce.jar used to be when a class from it was
+#		   first loaded). Thoughts?
+###############################################################################
+#
+# WARNING: Operating system protection should be used to lock down the .esapi
+# resources directory and all the files inside and all the directories all the
+# way up to the root directory of the file system.  Note that if you are using
+# file-based implementations, that some files may need to be read-write as they
+# get updated dynamically.
+#
+# Before using, be sure to update the MasterKey and MasterSalt as described below.
+# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
+#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
+#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
+#		able to decrypt your data with ESAPI 2.0.
+#
+#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
+#
+#===========================================================================
+# ESAPI Configuration
+#
+# If true, then print all the ESAPI properties set here when they are loaded.
+# If false, they are not printed. Useful to reduce output when running JUnit tests.
+# If you need to troubleshoot a properties related problem, turning this on may help.
+# This is 'false' in the src/test/resources/.esapi version. It is 'true' by
+# default for reasons of backward compatibility with earlier ESAPI versions.
+ESAPI.printProperties=true
+
+# ESAPI is designed to be easily extensible. You can use the reference implementation
+# or implement your own providers to take advantage of your enterprise's security
+# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
+#
+#    String ciphertext =
+#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
+#    CipherText cipherText =
+#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
+#
+# Below you can specify the classname for the provider that you wish to use in your
+# application. The only requirement is that it implement the appropriate ESAPI interface.
+# This allows you to switch security implementations in the future without rewriting the
+# entire application.
+#
+# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
+ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
+# FileBasedAuthenticator requires users.txt file in .esapi directory
+ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
+ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
+ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
+
+ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
+ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
+ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
+# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
+ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
+#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
+ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
+ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
+
+#===========================================================================
+# ESAPI Authenticator
+#
+Authenticator.AllowedLoginAttempts=3
+Authenticator.MaxOldPasswordHashes=13
+Authenticator.UsernameParameterName=username
+Authenticator.PasswordParameterName=password
+# RememberTokenDuration (in days)
+Authenticator.RememberTokenDuration=14
+# Session Timeouts (in minutes)
+Authenticator.IdleTimeoutDuration=20
+Authenticator.AbsoluteTimeoutDuration=120
+
+#===========================================================================
+# ESAPI Encoder
+#
+# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
+# Failure to canonicalize input is a very common mistake when implementing validation schemes.
+# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
+# following code to canonicalize data.
+#
+#      ESAPI.Encoder().canonicalize( "%22hello world&#x22;" );
+#  
+# Multiple encoding is when a single encoding format is applied multiple times. Allowing
+# multiple encoding is strongly discouraged.
+Encoder.AllowMultipleEncoding=false
+
+# Mixed encoding is when multiple different encoding formats are applied, or when 
+# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
+Encoder.AllowMixedEncoding=false
+
+# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
+# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
+# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
+Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
+
+
+#===========================================================================
+# ESAPI Encryption
+#
+# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
+# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+# There is not currently any support for key rotation, so be careful when changing your key and salt as it
+# will invalidate all signed, encrypted, and hashed data.
+#
+# WARNING: Not all combinations of algorithms and key lengths are supported.
+# If you choose to use a key length greater than 128, you MUST download the
+# unlimited strength policy files and install in the lib directory of your JRE/JDK.
+# See http://java.sun.com/javase/downloads/index.jsp for more information.
+#
+# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
+# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
+# possible, these methods should be avoided as they use ECB cipher mode, which in almost
+# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
+# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
+# should only use this compatibility setting if you have persistent data encrypted with
+# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
+# you have decrypted all of your old encrypted data and then re-encrypted it with
+# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
+# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
+# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
+# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
+# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
+# that requires downloading the special jurisdiction policy files mentioned above.)
+#
+#		***** IMPORTANT: Do NOT forget to replace these with your own values! *****
+# To calculate these values, you can run:
+#		java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+#
+#Encryptor.MasterKey=
+#Encryptor.MasterSalt=
+
+# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
+# encryption and hashing. (That is it will look to this provider first, but it
+# will defer to other providers if the requested algorithm is not implemented
+# by this provider.) If left unset, ESAPI will just use your Java VM's current
+# preferred JCE provider, which is generally set in the file
+# "$JAVA_HOME/jre/lib/security/java.security".
+#
+# The main intent of this is to allow ESAPI symmetric encryption to be
+# used with a FIPS 140-2 compliant crypto-module. For details, see the section
+# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
+# the ESAPI 2.0 Symmetric Encryption User Guide, at:
+# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
+# However, this property also allows you to easily use an alternate JCE provider
+# such as "Bouncy Castle" without having to make changes to "java.security".
+# See Javadoc for SecurityProviderLoader for further details. If you wish to use
+# a provider that is not known to SecurityProviderLoader, you may specify the
+# fully-qualified class name of the JCE provider class that implements
+# java.security.Provider. If the name contains a '.', this is interpreted as
+# a fully-qualified class name that implements java.security.Provider.
+#
+# NOTE: Setting this property has the side-effect of changing it in your application
+#       as well, so if you are using JCE in your application directly rather than
+#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
+#       preferred JCE provider there as well.
+#
+# Default: Keeps the JCE provider set to whatever JVM sets it to.
+Encryptor.PreferredJCEProvider=
+
+# AES is the most widely used and strongest encryption algorithm. This
+# should agree with your Encryptor.CipherTransformation property.
+# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
+# very weak. It is essentially a password-based encryption key, hashed
+# with MD5 around 1K times and then encrypted with the weak DES algorithm
+# (56-bits) using ECB mode and an unspecified padding (it is
+# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
+# "AES/CBC/PKCSPadding". If you want to change these, change them here.
+# Warning: This property does not control the default reference implementation for
+#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
+#		   in the future.
+# @deprecated
+Encryptor.EncryptionAlgorithm=AES
+#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
+Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
+
+# Applies to ESAPI 2.0 and later only!
+# Comma-separated list of cipher modes that provide *BOTH*
+# confidentiality *AND* message authenticity. (NIST refers to such cipher
+# modes as "combined modes" so that's what we shall call them.) If any of these
+# cipher modes are used then no MAC is calculated and stored
+# in the CipherText upon encryption. Likewise, if one of these
+# cipher modes is used with decryption, no attempt will be made
+# to validate the MAC contained in the CipherText object regardless
+# of whether it contains one or not. Since the expectation is that
+# these cipher modes support support message authenticity already,
+# injecting a MAC in the CipherText object would be at best redundant.
+#
+# Note that as of JDK 1.5, the SunJCE provider does not support *any*
+# of these cipher modes. Of these listed, only GCM and CCM are currently
+# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
+# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
+# padding modes.
+Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
+
+# Applies to ESAPI 2.0 and later only!
+# Additional cipher modes allowed for ESAPI 2.0 encryption. These
+# cipher modes are in _addition_ to those specified by the property
+# 'Encryptor.cipher_modes.combined_modes'.
+# Note: We will add support for streaming modes like CFB & OFB once
+# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
+# (probably in ESAPI 2.1).
+# DISCUSS: Better name?
+Encryptor.cipher_modes.additional_allowed=CBC
+
+# 128-bit is almost always sufficient and appears to be more resistant to
+# related key attacks than is 256-bit AES. Use '_' to use default key size
+# for cipher algorithms (where it makes sense because the algorithm supports
+# a variable key size). Key length must agree to what's provided as the
+# cipher transformation, otherwise this will be ignored after logging a
+# warning.
+#
+# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
+Encryptor.EncryptionKeyLength=128
+
+# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
+# (All cipher modes except ECB require an IV.) There are two choices: we can either
+# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
+# the IV does not need to be hidden from adversaries, it is important that the
+# adversary not be allowed to choose it. Also, random IVs are generally much more
+# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
+# such as CFB and OFB use a different IV for each encryption with a given key so
+# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
+# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
+# uncomment the Encryptor.fixedIV.
+#
+# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
+Encryptor.ChooseIVMethod=random
+# If you choose to use a fixed IV, then you must place a fixed IV here that
+# is known to all others who are sharing your secret key. The format should
+# be a hex string that is the same length as the cipher block size for the
+# cipher algorithm that you are using. The following is an *example* for AES
+# from an AES test vector for AES-128/CBC as described in:
+# NIST Special Publication 800-38A (2001 Edition)
+# "Recommendation for Block Cipher Modes of Operation".
+# (Note that the block size for AES is 16 bytes == 128 bits.)
+#
+Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
+
+# Whether or not CipherText should use a message authentication code (MAC) with it.
+# This prevents an adversary from altering the IV as well as allowing a more
+# fool-proof way of determining the decryption failed because of an incorrect
+# key being supplied. This refers to the "separate" MAC calculated and stored
+# in CipherText, not part of any MAC that is calculated as a result of a
+# "combined mode" cipher mode.
+#
+# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
+# set this property to false.
+Encryptor.CipherText.useMAC=true
+
+# Whether or not the PlainText object may be overwritten and then marked
+# eligible for garbage collection. If not set, this is still treated as 'true'.
+Encryptor.PlainText.overwrite=true
+
+# Do not use DES except in a legacy situations. 56-bit is way too small key size.
+#Encryptor.EncryptionKeyLength=56
+#Encryptor.EncryptionAlgorithm=DES
+
+# TripleDES is considered strong enough for most purposes.
+#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
+#			requires downloading the special jurisdiction policy from Sun.
+#Encryptor.EncryptionKeyLength=168
+#Encryptor.EncryptionAlgorithm=DESede
+
+Encryptor.HashAlgorithm=SHA-512
+Encryptor.HashIterations=1024
+Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
+Encryptor.DigitalSignatureKeyLength=1024
+Encryptor.RandomAlgorithm=SHA1PRNG
+Encryptor.CharacterEncoding=UTF-8
+
+# This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function
+# (KDF) normally uses. Note this is *only* the PRF used for ESAPI's KDF and
+# *not* what is used for ESAPI's MAC. (Currently, HmacSHA1 is always used for
+# the MAC, mostly to keep the overall size at a minimum.)
+#
+# Currently supported choices for JDK 1.5 and 1.6 are:
+#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
+#	HmacSHA512 (512 bits).
+# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
+# the JDKs support it.  See the ESAPI 2.0 Symmetric Encryption User Guide
+# further details.
+Encryptor.KDF.PRF=HmacSHA256
+#===========================================================================
+# ESAPI HttpUtilties
+#
+# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
+# protect against malicious data from attackers, such as unprintable characters, escaped characters,
+# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
+# headers, and CSRF tokens.
+#
+# Default file upload location (remember to escape backslashes with \\)
+HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
+HttpUtilities.UploadTempDir=C:\\temp
+# Force flags on cookies, if you use HttpUtilities to set cookies
+HttpUtilities.ForceHttpOnlySession=false
+HttpUtilities.ForceSecureSession=false
+HttpUtilities.ForceHttpOnlyCookies=true
+HttpUtilities.ForceSecureCookies=true
+# Maximum size of HTTP headers
+HttpUtilities.MaxHeaderSize=4096
+# File upload configuration
+HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
+HttpUtilities.MaxUploadFileBytes=500000000
+# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
+# container, and any other technologies you may be using. Failure to do this may expose you
+# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
+HttpUtilities.ResponseContentType=text/html; charset=UTF-8
+# This is the name of the cookie used to represent the HTTP session
+# Typically this will be the default "JSESSIONID" 
+HttpUtilities.HttpSessionIdName=JSESSIONID
+
+
+
+#===========================================================================
+# ESAPI Executor
+# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
+Executor.WorkingDirectory=C:\\Windows\\Temp
+Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
+
+
+#===========================================================================
+# ESAPI Logging
+# Set the application name if these logs are combined with other applications
+Logger.ApplicationName=ExampleApplication
+# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
+Logger.LogEncodingRequired=false
+# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
+Logger.LogApplicationName=true
+# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
+Logger.LogServerIP=true
+# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
+# want to place it in a specific directory.
+Logger.LogFileName=ESAPI_logging_file
+# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
+Logger.MaxLogFileSize=10000000
+
+
+#===========================================================================
+# ESAPI Intrusion Detection
+#
+# Each event has a base to which .count, .interval, and .action are added
+# The IntrusionException will fire if we receive "count" events within "interval" seconds
+# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
+#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
+#
+# Custom Events
+# Names must start with "event." as the base
+# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
+# You can also disable intrusion detection completely by changing
+# the following parameter to true
+#
+IntrusionDetector.Disable=false
+#
+IntrusionDetector.event.test.count=2
+IntrusionDetector.event.test.interval=10
+IntrusionDetector.event.test.actions=disable,log
+
+# Exception Events
+# All EnterpriseSecurityExceptions are registered automatically
+# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
+# Use the fully qualified classname of the exception as the base
+
+# any intrusion is an attack
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
+
+# for test purposes
+# CHECKME: Shouldn't there be something in the property name itself that designates
+#		   that these are for testing???
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
+
+# rapid validation errors indicate scans or attacks in progress
+# org.owasp.esapi.errors.ValidationException.count=10
+# org.owasp.esapi.errors.ValidationException.interval=10
+# org.owasp.esapi.errors.ValidationException.actions=log,logout
+
+# sessions jumping between hosts indicates session hijacking
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
+
+
+#===========================================================================
+# ESAPI Validation
+#
+# The ESAPI Validator works on regular expressions with defined names. You can define names
+# either here, or you may define application specific patterns in a separate file defined below.
+# This allows enterprises to specify both organizational standards as well as application specific
+# validation rules.
+#
+Validator.ConfigurationFile=validation.properties
+
+# Validators used by ESAPI
+Validator.AccountName=^[a-zA-Z0-9]{3,20}$
+Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
+Validator.RoleName=^[a-z]{1,20}$
+
+#the word TEST below should be changed to your application 
+#name - only relative URL's are supported
+Validator.Redirect=^\\/test.*$
+
+# Global HTTP Validation Rules
+# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
+Validator.HTTPScheme=^(http|https)$
+Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
+Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$
+Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$
+Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
+Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
+Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$
+Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPURL=^.*$
+Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
+
+# Validation of file related input
+Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+
+# Validation of dates. Controls whether or not 'lenient' dates are accepted.
+# See DataFormat.setLenient(boolean flag) for further details.
+Validator.AcceptLenientDates=false
\ No newline at end of file
diff --git a/configuration/esapi/antisamy-esapi.xml b/configuration/esapi/antisamy-esapi.xml
new file mode 100644
index 0000000..500ab59
--- /dev/null
+++ b/configuration/esapi/antisamy-esapi.xml
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+	
+<!-- 
+W3C rules retrieved from:
+http://www.w3.org/TR/html401/struct/global.html
+-->
+	
+<!--
+Slashdot allowed tags taken from "Reply" page:
+<b> <i> <p> <br> <a> <ol> <ul> <li> <dl> <dt> <dd> <em> <strong> <tt> <blockquote> <div> <ecode> <quote>
+-->
+
+<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xsi:noNamespaceSchemaLocation="antisamy.xsd">
+	
+	<directives>
+		<directive name="omitXmlDeclaration" value="true"/>
+		<directive name="omitDoctypeDeclaration" value="true"/>
+		<directive name="maxInputSize" value="500000"/>
+		<directive name="embedStyleSheets" value="false"/>
+	</directives>
+	
+	
+	<common-regexps>
+		
+		<!-- 
+		From W3C:
+		This attribute assigns a class name or set of class names to an
+		element. Any number of elements may be assigned the same class
+		name or names. Multiple class names must be separated by white 
+		space characters.
+		-->
+		
+		<regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' -->
+		<regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/>
+		<regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/>
+	
+	</common-regexps>
+	
+	<!-- 
+	
+	Tag.name = a, b, div, body, etc.
+	Tag.action = filter: remove tags, but keep content, validate: keep content as long as it passes rules, remove: remove tag and contents
+	Attribute.name = id, class, href, align, width, etc.
+	Attribute.onInvalid = what to do when the attribute is invalid, e.g., remove the tag (removeTag), remove the attribute (removeAttribute), filter the tag (filterTag)
+	Attribute.description = What rules in English you want to tell the users they can have for this attribute. Include helpful things so they'll be able to tune their HTML
+	 
+	 -->
+
+	<!-- 
+	Some attributes are common to all (or most) HTML tags. There aren't many that qualify for this. You have to make sure there's no
+	collisions between any of these attribute names with attribute names of other tags that are for different purposes.
+	-->
+
+	<common-attributes>
+		
+
+		<attribute name="lang" description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
+		 	<regexp-list>
+		 		<regexp value="[a-zA-Z]{2,20}"/>
+		 	</regexp-list>
+		 </attribute>
+		 
+		 <attribute name="title" description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
+		 	<regexp-list>
+		 		<regexp name="htmlTitle"/>
+		 	</regexp-list>
+		 </attribute>
+
+		<attribute name="href" onInvalid="filterTag">
+			<regexp-list>
+				<regexp name="onsiteURL"/>
+				<regexp name="offsiteURL"/>
+			</regexp-list>
+		</attribute>
+	
+		<attribute name="align" description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
+			<literal-list>
+				<literal value="center"/>
+				<literal value="left"/>
+				<literal value="right"/>
+				<literal value="justify"/>
+				<literal value="char"/>
+			</literal-list>
+		</attribute>
+
+	</common-attributes>
+
+
+	<!--
+	This requires normal updates as browsers continue to diverge from the W3C and each other. As long as the browser wars continue
+	this is going to continue. I'm not sure war is the right word for what's going on. Doesn't somebody have to win a war after 
+	a while?
+	 -->
+	
+	<global-tag-attributes>
+		<attribute name="title"/>
+		<attribute name="lang"/>
+	</global-tag-attributes>
+
+
+	<tag-rules>
+
+		<!-- Tags related to JavaScript -->
+
+		<tag name="script" action="remove"/>
+		<tag name="noscript" action="remove"/>
+		
+		<!-- Frame & related tags -->
+		
+		<tag name="iframe" action="remove"/>
+		<tag name="frameset" action="remove"/>
+		<tag name="frame" action="remove"/>
+		<tag name="noframes" action="remove"/>
+		
+
+		<!-- All reasonable formatting tags -->
+		
+		<tag name="p" action="validate">
+			<attribute name="align"/>
+		</tag>
+
+		<tag name="div" action="validate"/>		
+		<tag name="i" action="validate"/>
+		<tag name="b" action="validate"/>
+		<tag name="em" action="validate"/>
+		<tag name="blockquote" action="validate"/>
+		<tag name="tt" action="validate"/>
+		
+		<tag name="br" action="truncate"/>
+
+		<!-- Custom Slashdot tags, though we're trimming the idea of having a possible mismatching end tag with the endtag="" attribute -->
+		
+		<tag name="quote" action="validate"/>
+		<tag name="ecode" action="validate"/> 
+		
+						
+		<!-- Anchor and anchor related tags -->
+		
+		<tag name="a" action="validate">
+
+			<attribute name="href" onInvalid="filterTag"/>
+			<attribute name="nohref">
+				<literal-list>
+					<literal value="nohref"/>
+					<literal value=""/>
+				</literal-list>
+			</attribute>
+			<attribute name="rel">
+				<literal-list>
+					<literal value="nofollow"/>
+				</literal-list>
+			</attribute>
+		</tag>
+
+		<!-- List tags -->
+
+		<tag name="ul" action="validate"/>
+		<tag name="ol" action="validate"/>
+		<tag name="li" action="validate"/>
+		
+	</tag-rules>
+
+
+
+	<!--  No CSS on Slashdot posts -->
+
+	<css-rules>
+	</css-rules>
+
+
+	<html-entities>
+		<entity name="amp" cdata="&"/>
+		<entity name="nbsp" cdata="&#160;"/>
+		
+		<entity name="iexcl" cdata="&#161;"/> <!--inverted exclamation mark, U+00A1 ISOnum -->
+		<entity name="cent" cdata="&#162;"/> <!--cent sign, U+00A2 ISOnum -->
+		<entity name="pound" cdata="&#163;"/> <!--pound sign, U+00A3 ISOnum -->
+		<entity name="curren" cdata="&#164;"/> <!--currency sign, U+00A4 ISOnum -->
+		<entity name="yen" cdata="&#165;"/> <!--yen sign = yuan sign, U+00A5 ISOnum -->
+		<entity name="brvbar" cdata="&#166;"/> <!--broken bar = broken vertical bar, U+00A6 ISOnum -->
+		<entity name="sect" cdata="&#167;"/> <!--section sign, U+00A7 ISOnum -->
+		<entity name="uml" cdata="&#168;"/> <!--diaeresis = spacing diaeresis, U+00A8 ISOdia -->
+		<entity name="copy" cdata="&#169;"/> <!--copyright sign, U+00A9 ISOnum -->
+		<entity name="ordf" cdata="&#170;"/> <!--feminine ordinal indicator, U+00AA ISOnum -->
+		<entity name="laquo" cdata="&#171;"/> <!--left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum -->
+		<entity name="not" cdata="&#172;"/> <!--not sign, U+00AC ISOnum -->
+		<entity name="shy" cdata="&#173;"/> <!--soft hyphen = discretionary hyphen,U+00AD ISOnum -->
+		<entity name="reg" cdata="&#174;"/> <!--registered sign = registered trade mark sign, U+00AE ISOnum -->
+		<entity name="macr" cdata="&#175;"/> <!--macron = spacing macron = overline = APL overbar, U+00AF ISOdia -->
+		<entity name="deg" cdata="&#176;"/> <!--degree sign, U+00B0 ISOnum -->
+		<entity name="plusmn" cdata="&#177;"/> <!--plus-minus sign = plus-or-minus sign, U+00B1 ISOnum -->
+		<entity name="sup2" cdata="&#178;"/> <!--superscript two = superscript digit two = squared, U+00B2 ISOnum -->
+		<entity name="sup3" cdata="&#179;"/> <!--superscript three = superscript digit three= cubed, U+00B3 ISOnum -->
+		<entity name="acute" cdata="&#180;"/> <!--acute accent = spacing acute, U+00B4 ISOdia -->
+		<entity name="micro" cdata="&#181;"/> <!--micro sign, U+00B5 ISOnum -->
+		<entity name="para" cdata="&#182;"/> <!--pilcrow sign = paragraph sign, U+00B6 ISOnum -->
+		<entity name="middot" cdata="&#183;"/> <!--middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum -->
+		<entity name="cedil" cdata="&#184;"/> <!--cedilla = spacing cedilla, U+00B8 ISOdia -->
+		<entity name="sup1" cdata="&#185;"/> <!--superscript one = superscript digit one,U+00B9 ISOnum -->
+		<entity name="ordm" cdata="&#186;"/> <!--masculine ordinal indicator, U+00BA ISOnum -->
+		<entity name="raquo" cdata="&#187;"/> <!--right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum -->
+		<entity name="frac14" cdata="&#188;"/> <!--vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum -->
+		<entity name="frac12" cdata="&#189;"/> <!--vulgar fraction one half = fraction one half, U+00BD ISOnum -->
+		<entity name="frac34" cdata="&#190;"/> <!--vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum -->
+		<entity name="iquest" cdata="&#191;"/> <!--inverted question mark = turned question mark, U+00BF ISOnum -->
+		<entity name="Agrave" cdata="&#192;"/> <!--latin capital letter A with grave = latin capital letter A grave,U+00C0 ISOlat1 -->
+		<entity name="Aacute" cdata="&#193;"/> <!--latin capital letter A with acute,U+00C1 ISOlat1 -->
+		<entity name="Acirc" cdata="&#194;"/> <!--latin capital letter A with circumflex,U+00C2 ISOlat1 -->
+		<entity name="Atilde" cdata="&#195;"/> <!--latin capital letter A with tilde,U+00C3 ISOlat1 -->
+		<entity name="Auml" cdata="&#196;"/> <!--latin capital letter A with diaeresis,U+00C4 ISOlat1 -->
+		<entity name="Aring" cdata="&#197;"/> <!--latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 -->
+		<entity name="AElig" cdata="&#198;"/> <!--latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 -->
+		<entity name="Ccedil" cdata="&#199;"/> <!--latin capital letter C with cedilla, U+00C7 ISOlat1 -->
+		<entity name="Egrave" cdata="&#200;"/> <!--latin capital letter E with grave, U+00C8 ISOlat1 -->
+		<entity name="Eacute" cdata="&#201;"/> <!--latin capital letter E with acute,U+00C9 ISOlat1 -->
+		<entity name="Ecirc" cdata="&#202;"/> <!--latin capital letter E with circumflex,U+00CA ISOlat1 -->
+		<entity name="Euml" cdata="&#203;"/> <!--latin capital letter E with diaeresis, U+00CB ISOlat1 -->
+		<entity name="Igrave" cdata="&#204;"/> <!--latin capital letter I with grave, U+00CC ISOlat1 -->
+		<entity name="Iacute" cdata="&#205;"/> <!--latin capital letter I with acute, U+00CD ISOlat1 -->
+		<entity name="Icirc" cdata="&#206;"/> <!--latin capital letter I with circumflex, U+00CE ISOlat1 -->
+		<entity name="Iuml" cdata="&#207;"/> <!--latin capital letter I with diaeresis, U+00CF ISOlat1 -->
+		<entity name="ETH" cdata="&#208;"/> <!--latin capital letter ETH, U+00D0 ISOlat1 -->
+		<entity name="Ntilde" cdata="&#209;"/> <!--latin capital letter N with tilde, U+00D1 ISOlat1 -->
+		<entity name="Ograve" cdata="&#210;"/> <!--latin capital letter O with grave, U+00D2 ISOlat1 -->
+		<entity name="Oacute" cdata="&#211;"/> <!--latin capital letter O with acute, U+00D3 ISOlat1 -->
+		<entity name="Ocirc" cdata="&#212;"/> <!--latin capital letter O with circumflex, U+00D4 ISOlat1 -->
+		<entity name="Otilde" cdata="&#213;"/> <!--latin capital letter O with tilde, U+00D5 ISOlat1 -->
+		<entity name="Ouml" cdata="&#214;"/> <!--latin capital letter O with diaeresis, U+00D6 ISOlat1 -->
+		<entity name="times" cdata="&#215;"/> <!--multiplication sign, U+00D7 ISOnum -->
+		<entity name="Oslash" cdata="&#216;"/> <!--latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 -->
+		<entity name="Ugrave" cdata="&#217;"/> <!--latin capital letter U with grave, U+00D9 ISOlat1 -->
+		<entity name="Uacute" cdata="&#218;"/> <!--latin capital letter U with acute, U+00DA ISOlat1 -->
+		<entity name="Ucirc" cdata="&#219;"/> <!--latin capital letter U with circumflex, U+00DB ISOlat1 -->
+		<entity name="Uuml" cdata="&#220;"/> <!--latin capital letter U with diaeresis, U+00DC ISOlat1 -->
+		<entity name="Yacute" cdata="&#221;"/> <!--latin capital letter Y with acute, U+00DD ISOlat1 -->
+		<entity name="THORN" cdata="&#222;"/> <!--latin capital letter THORN, U+00DE ISOlat1 -->
+		<entity name="szlig" cdata="&#223;"/> <!--latin small letter sharp s = ess-zed, U+00DF ISOlat1 -->
+		<entity name="agrave" cdata="&#224;"/> <!--latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 -->
+		<entity name="aacute" cdata="&#225;"/> <!--latin small letter a with acute, U+00E1 ISOlat1 -->
+		<entity name="acirc" cdata="&#226;"/> <!--latin small letter a with circumflex, U+00E2 ISOlat1 -->
+		<entity name="atilde" cdata="&#227;"/> <!--latin small letter a with tilde, U+00E3 ISOlat1 -->
+		<entity name="auml" cdata="&#228;"/> <!--latin small letter a with diaeresis, U+00E4 ISOlat1 -->
+		<entity name="aring" cdata="&#229;"/> <!--latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 -->
+		<entity name="aelig" cdata="&#230;"/> <!--latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 -->
+		<entity name="ccedil" cdata="&#231;"/> <!--latin small letter c with cedilla, U+00E7 ISOlat1 -->
+		<entity name="egrave" cdata="&#232;"/> <!--latin small letter e with grave, U+00E8 ISOlat1 -->
+		<entity name="eacute" cdata="&#233;"/> <!--latin small letter e with acute, U+00E9 ISOlat1 -->
+		<entity name="ecirc" cdata="&#234;"/> <!--latin small letter e with circumflex, U+00EA ISOlat1 -->
+		<entity name="euml" cdata="&#235;"/> <!--latin small letter e with diaeresis, U+00EB ISOlat1 -->
+		<entity name="igrave" cdata="&#236;"/> <!--latin small letter i with grave, U+00EC ISOlat1 -->
+		<entity name="iacute" cdata="&#237;"/> <!--latin small letter i with acute, U+00ED ISOlat1 -->
+		<entity name="icirc" cdata="&#238;"/> <!--latin small letter i with circumflex, U+00EE ISOlat1 -->
+		<entity name="iuml" cdata="&#239;"/> <!--latin small letter i with diaeresis, U+00EF ISOlat1 -->
+		<entity name="eth" cdata="&#240;"/> <!--latin small letter eth, U+00F0 ISOlat1 -->
+		<entity name="ntilde" cdata="&#241;"/> <!--latin small letter n with tilde, U+00F1 ISOlat1 -->
+		<entity name="ograve" cdata="&#242;"/> <!--latin small letter o with grave, U+00F2 ISOlat1 -->
+		<entity name="oacute" cdata="&#243;"/> <!--latin small letter o with acute, U+00F3 ISOlat1 -->
+		<entity name="ocirc " cdata="&#244;"/> <!--latin small letter o with circumflex, U+00F4 ISOlat1 -->
+		<entity name="otilde" cdata="&#245;"/> <!--latin small letter o with tilde, U+00F5 ISOlat1 -->
+		<entity name="ouml" cdata="&#246;"/> <!--latin small letter o with diaeresis, U+00F6 ISOlat1 -->
+		<entity name="divide" cdata="&#247;"/> <!--division sign, U+00F7 ISOnum -->
+		<entity name="oslash" cdata="&#248;"/> <!--latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 -->
+		<entity name="ugrave" cdata="&#249;"/> <!--latin small letter u with grave, U+00F9 ISOlat1 -->
+		<entity name="uacute" cdata="&#250;"/> <!--latin small letter u with acute, U+00FA ISOlat1 -->
+		<entity name="ucirc" cdata="&#251;"/> <!--latin small letter u with circumflex, U+00FB ISOlat1 -->
+		<entity name="uuml" cdata="&#252;"/> <!--latin small letter u with diaeresis, U+00FC ISOlat1 -->
+		<entity name="yacute" cdata="&#253;"/> <!--latin small letter y with acute, U+00FD ISOlat1 -->
+		<entity name="thorn" cdata="&#254;"/> <!--latin small letter thorn, U+00FE ISOlat1 -->
+		<entity name="yuml" cdata="&#255;"/> <!--latin small letter y with diaeresis, U+00FF ISOlat1 -->
+		                                  
+		<entity name="fnof" cdata="&#402;"/> <!--latin small f with hook = function = florin, U+0192 ISOtech -->
+		
+		<!-- Greek -->
+		<entity name="Alpha" cdata="&#913;"/> <!--greek capital letter alpha, U+0391 -->
+		<entity name="Beta" cdata="&#914;"/> <!--greek capital letter beta, U+0392 -->
+		<entity name="Gamma" cdata="&#915;"/> <!--greek capital letter gamma, U+0393 ISOgrk3 -->
+		<entity name="Delta" cdata="&#916;"/> <!--greek capital letter delta, U+0394 ISOgrk3 -->
+		<entity name="Epsilon" cdata="&#917;"/> <!--greek capital letter epsilon, U+0395 -->
+		<entity name="Zeta" cdata="&#918;"/> <!--greek capital letter zeta, U+0396 -->
+		<entity name="Eta" cdata="&#919;"/> <!--greek capital letter eta, U+0397 -->
+		<entity name="Theta" cdata="&#920;"/> <!--greek capital letter theta, U+0398 ISOgrk3 -->
+		<entity name="Iota" cdata="&#921;"/> <!--greek capital letter iota, U+0399 -->
+		<entity name="Kappa" cdata="&#922;"/> <!--greek capital letter kappa, U+039A -->
+		<entity name="Lambda" cdata="&#923;"/> <!--greek capital letter lambda, U+039B ISOgrk3 -->
+		<entity name="Mu" cdata="&#924;"/> <!--greek capital letter mu, U+039C -->
+		<entity name="Nu" cdata="&#925;"/> <!--greek capital letter nu, U+039D -->
+		<entity name="Xi" cdata="&#926;"/> <!--greek capital letter xi, U+039E ISOgrk3 -->
+		<entity name="Omicron" cdata="&#927;"/> <!--greek capital letter omicron, U+039F -->
+		<entity name="Pi" cdata="&#928;"/> <!--greek capital letter pi, U+03A0 ISOgrk3 -->
+		<entity name="Rho" cdata="&#929;"/> <!--greek capital letter rho, U+03A1 -->
+		<!-- there is no Sigmaf, and no U+03A2 character either -->
+		<entity name="Sigma" cdata="&#931;"/> <!--greek capital letter sigma, U+03A3 ISOgrk3 -->
+		<entity name="Tau" cdata="&#932;"/> <!--greek capital letter tau, U+03A4 -->
+		<entity name="Upsilon" cdata="&#933;"/> <!--greek capital letter upsilon,U+03A5 ISOgrk3 -->
+		<entity name="Phi" cdata="&#934;"/> <!--greek capital letter phi,U+03A6 ISOgrk3 -->
+		<entity name="Chi" cdata="&#935;"/> <!--greek capital letter chi, U+03A7 -->
+		<entity name="Psi" cdata="&#936;"/> <!--greek capital letter psi,U+03A8 ISOgrk3 -->
+		<entity name="Omega" cdata="&#937;"/> <!--greek capital letter omega,U+03A9 ISOgrk3 -->
+		
+		<entity name="alpha" cdata="&#945;"/> <!--greek small letter alpha,U+03B1 ISOgrk3 -->
+		<entity name="beta" cdata="&#946;"/> <!--greek small letter beta, U+03B2 ISOgrk3 -->
+		<entity name="gamma" cdata="&#947;"/> <!--greek small letter gamma,U+03B3 ISOgrk3 -->
+		<entity name="delta" cdata="&#948;"/> <!--greek small letter delta,U+03B4 ISOgrk3 -->
+		<entity name="epsilon" cdata="&#949;"/> <!--greek small letter epsilon,U+03B5 ISOgrk3 -->
+		<entity name="zeta" cdata="&#950;"/> <!--greek small letter zeta, U+03B6 ISOgrk3 -->
+		<entity name="eta" cdata="&#951;"/> <!--greek small letter eta, U+03B7 ISOgrk3 -->
+		<entity name="theta" cdata="&#952;"/> <!--greek small letter theta, U+03B8 ISOgrk3 -->
+		<entity name="iota" cdata="&#953;"/> <!--greek small letter iota, U+03B9 ISOgrk3 -->
+		<entity name="kappa" cdata="&#954;"/> <!--greek small letter kappa,U+03BA ISOgrk3 -->
+		<entity name="lambda" cdata="&#955;"/> <!--greek small letter lambda, U+03BB ISOgrk3 -->
+		<entity name="mu" cdata="&#956;"/> <!--greek small letter mu, U+03BC ISOgrk3 -->
+		<entity name="nu" cdata="&#957;"/> <!--greek small letter nu, U+03BD ISOgrk3 -->
+		<entity name="xi" cdata="&#958;"/> <!--greek small letter xi, U+03BE ISOgrk3 -->
+		<entity name="omicron" cdata="&#959;"/> <!--greek small letter omicron, U+03BF NEW -->
+		<entity name="pi" cdata="&#960;"/> <!--greek small letter pi, U+03C0 ISOgrk3 -->
+		<entity name="rho" cdata="&#961;"/> <!--greek small letter rho, U+03C1 ISOgrk3 -->
+		<entity name="sigmaf" cdata="&#962;"/> <!--greek small letter final sigma, U+03C2 ISOgrk3 -->
+		<entity name="sigma" cdata="&#963;"/> <!--greek small letter sigma, U+03C3 ISOgrk3 -->
+		<entity name="tau" cdata="&#964;"/> <!--greek small letter tau, U+03C4 ISOgrk3 -->
+		<entity name="upsilon" cdata="&#965;"/> <!--greek small letter upsilon, U+03C5 ISOgrk3 -->
+		<entity name="phi" cdata="&#966;"/> <!--greek small letter phi, U+03C6 ISOgrk3 -->
+		<entity name="chi" cdata="&#967;"/> <!--greek small letter chi, U+03C7 ISOgrk3 -->
+		<entity name="psi" cdata="&#968;"/> <!--greek small letter psi, U+03C8 ISOgrk3 -->
+		<entity name="omega" cdata="&#969;"/> <!--greek small letter omega, U+03C9 ISOgrk3 -->
+		<entity name="thetasym" cdata="&#977;"/> <!--greek small letter theta symbol, U+03D1 NEW -->
+		<entity name="upsih" cdata="&#978;"/> <!--greek upsilon with hook symbol, U+03D2 NEW -->
+		<entity name="piv" cdata="&#982;"/> <!--greek pi symbol, U+03D6 ISOgrk3 -->
+		
+		<!-- General Punctuation -->
+		<entity name="bull" cdata="&#8226;"/> <!--bullet = black small circle, U+2022 ISOpub  -->
+		<!-- bullet is NOT the same as bullet operator, U+2219 -->
+		<entity name="hellip" cdata="&#8230;"/> <!--horizontal ellipsis = three dot leader, U+2026 ISOpub  -->
+		<entity name="prime" cdata="&#8242;"/> <!--prime = minutes = feet, U+2032 ISOtech -->
+		<entity name="Prime" cdata="&#8243;"/> <!--double prime = seconds = inches, U+2033 ISOtech -->
+		<entity name="oline" cdata="&#8254;"/> <!--overline = spacing overscore, U+203E NEW -->
+		<entity name="frasl" cdata="&#8260;"/> <!--fraction slash, U+2044 NEW -->
+		
+		<!-- Letterlike Symbols -->
+		<entity name="weierp" cdata="&#8472;"/> <!--script capital P = power set = Weierstrass p, U+2118 ISOamso -->
+		<entity name="image" cdata="&#8465;"/> <!--blackletter capital I = imaginary part, U+2111 ISOamso -->
+		<entity name="real" cdata="&#8476;"/> <!--blackletter capital R = real part symbol, U+211C ISOamso -->
+		<entity name="trade" cdata="&#8482;"/> <!--trade mark sign, U+2122 ISOnum -->
+		<entity name="alefsym" cdata="&#8501;"/> <!--alef symbol = first transfinite cardinal, U+2135 NEW -->
+		<!-- alef symbol is NOT the same as hebrew letter alef,
+		     U+05D0 although the same glyph could be used to depict both characters -->
+		
+		<!-- Arrows -->
+		<entity name="larr" cdata="&#8592;"/> <!--leftwards arrow, U+2190 ISOnum -->
+		<entity name="uarr" cdata="&#8593;"/> <!--upwards arrow, U+2191 ISOnum-->
+		<entity name="rarr" cdata="&#8594;"/> <!--rightwards arrow, U+2192 ISOnum -->
+		<entity name="darr" cdata="&#8595;"/> <!--downwards arrow, U+2193 ISOnum -->
+		<entity name="harr" cdata="&#8596;"/> <!--left right arrow, U+2194 ISOamsa -->
+		<entity name="crarr" cdata="&#8629;"/> <!--downwards arrow with corner leftwards
+		                                     = carriage return, U+21B5 NEW -->
+		<entity name="lArr" cdata="&#8656;"/> <!--leftwards double arrow, U+21D0 ISOtech -->
+		
+		<!-- ISO 10646 does not say that lArr is the same as the 'is implied by' arrow
+		    but also does not have any other character for that function. So ? lArr can
+		    be used for 'is implied by' as ISOtech suggests -->
+		    
+		<entity name="uArr" cdata="&#8657;"/> <!--upwards double arrow, U+21D1 ISOamsa -->
+		<entity name="rArr" cdata="&#8658;"/> <!--rightwards double arrow, U+21D2 ISOtech -->
+		
+		<!-- ISO 10646 does not say this is the 'implies' character but does not have 
+		     another character with this function so ?
+		     rArr can be used for 'implies' as ISOtech suggests -->
+		     
+		<entity name="dArr" cdata="&#8659;"/> <!--downwards double arrow, U+21D3 ISOamsa -->
+		<entity name="hArr" cdata="&#8660;"/> <!--left right double arrow, U+21D4 ISOamsa -->
+		
+		<!-- Mathematical Operators -->
+		<entity name="forall" cdata="&#8704;"/> <!--for all, U+2200 ISOtech -->
+		<entity name="part" cdata="&#8706;"/> <!--partial differential, U+2202 ISOtech  -->
+		<entity name="exist" cdata="&#8707;"/> <!--there exists, U+2203 ISOtech -->
+		<entity name="empty" cdata="&#8709;"/> <!--empty set = null set = diameter,U+2205 ISOamso -->
+		<entity name="nabla" cdata="&#8711;"/> <!--nabla = backward difference, U+2207 ISOtech -->
+		<entity name="isin" cdata="&#8712;"/> <!--element of, U+2208 ISOtech -->
+		<entity name="notin" cdata="&#8713;"/> <!--not an element of, U+2209 ISOtech -->
+		<entity name="ni" cdata="&#8715;"/> <!--contains as member, U+220B ISOtech -->
+	
+		<!-- should there be a more memorable name than 'ni'? -->
+		<entity name="prod" cdata="&#8719;"/> <!--n-ary product = product sign, U+220F ISOamsb -->
+		
+		<!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though
+		     the same glyph might be used for both -->
+		     
+		<entity name="sum" cdata="&#8721;"/> <!--n-ary sumation, U+2211 ISOamsb -->
+		
+		<!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+		     though the same glyph might be used for both -->
+		
+		<entity name="minus" cdata="&#8722;"/> <!--minus sign, U+2212 ISOtech -->
+		<entity name="lowast" cdata="&#8727;"/> <!--asterisk operator, U+2217 ISOtech -->
+		<entity name="radic" cdata="&#8730;"/> <!--square root = radical sign, U+221A ISOtech -->
+		<entity name="prop" cdata="&#8733;"/> <!--proportional to, U+221D ISOtech -->
+		<entity name="infin" cdata="&#8734;"/> <!--infinity, U+221E ISOtech -->
+		<entity name="ang" cdata="&#8736;"/> <!--angle, U+2220 ISOamso -->
+		<entity name="and" cdata="&#8743;"/> <!--logical and = wedge, U+2227 ISOtech -->
+		<entity name="or" cdata="&#8744;"/> <!--logical or = vee, U+2228 ISOtech -->
+		<entity name="cap" cdata="&#8745;"/> <!--intersection = cap, U+2229 ISOtech -->
+		<entity name="cup" cdata="&#8746;"/> <!--union = cup, U+222A ISOtech -->
+		<entity name="int" cdata="&#8747;"/> <!--integral, U+222B ISOtech -->
+		<entity name="there4" cdata="&#8756;"/> <!--therefore, U+2234 ISOtech -->
+		<entity name="sim" cdata="&#8764;"/> <!--tilde operator = varies with = similar to, U+223C ISOtech -->
+		
+		<!-- tilde operator is NOT the same character as the tilde, U+007E,
+		     although the same glyph might be used to represent both  -->
+		     
+		<entity name="cong" cdata="&#8773;"/> <!--approximately equal to, U+2245 ISOtech -->
+		<entity name="asymp" cdata="&#8776;"/> <!--almost equal to = asymptotic to, U+2248 ISOamsr -->
+		<entity name="ne" cdata="&#8800;"/> <!--not equal to, U+2260 ISOtech -->
+		<entity name="equiv" cdata="&#8801;"/> <!--identical to, U+2261 ISOtech -->
+		<entity name="le" cdata="&#8804;"/> <!--less-than or equal to, U+2264 ISOtech -->
+		<entity name="ge" cdata="&#8805;"/> <!--greater-than or equal to, U+2265 ISOtech -->
+		<entity name="sub" cdata="&#8834;"/> <!--subset of, U+2282 ISOtech -->
+		<entity name="sup" cdata="&#8835;"/> <!--superset of, U+2283 ISOtech -->
+		
+		<!-- note that nsup, 'not a superset of, U+2283' is not covered by the Symbol 
+		     font encoding and is not included. Should it be, for symmetry?
+		     It is in ISOamsn  --> 
+		
+		<entity name="nsub" cdata="&#8836;"/> <!--not a subset of, U+2284 ISOamsn -->
+		<entity name="sube" cdata="&#8838;"/> <!--subset of or equal to, U+2286 ISOtech -->
+		<entity name="supe" cdata="&#8839;"/> <!--superset of or equal to, U+2287 ISOtech -->
+		<entity name="oplus" cdata="&#8853;"/> <!--circled plus = direct sum, U+2295 ISOamsb -->
+		<entity name="otimes" cdata="&#8855;"/> <!--circled times = vector product, U+2297 ISOamsb -->
+		<entity name="perp" cdata="&#8869;"/> <!--up tack = orthogonal to = perpendicular, U+22A5 ISOtech -->
+		<entity name="sdot" cdata="&#8901;"/> <!--dot operator, U+22C5 ISOamsb -->
+		<!-- dot operator is NOT the same character as U+00B7 middle dot -->
+		
+		<!-- Miscellaneous Technical -->
+		<entity name="lceil" cdata="&#8968;"/> <!--left ceiling = apl upstile, U+2308 ISOamsc  -->
+		<entity name="rceil" cdata="&#8969;"/> <!--right ceiling, U+2309 ISOamsc  -->
+		<entity name="lfloor" cdata="&#8970;"/> <!--left floor = apl downstile, U+230A ISOamsc  -->
+		<entity name="rfloor" cdata="&#8971;"/> <!--right floor, U+230B ISOamsc  -->
+		<entity name="lang" cdata="&#9001;"/> <!--left-pointing angle bracket = bra, U+2329 ISOtech -->
+		<!-- lang is NOT the same character as U+003C 'less than' 
+		     or U+2039 'single left-pointing angle quotation mark' -->
+		<entity name="rang" cdata="&#9002;"/> <!--right-pointing angle bracket = ket, U+232A ISOtech -->
+		<!-- rang is NOT the same character as U+003E 'greater than' or U+203A 'single right-pointing angle quotation mark' -->
+		
+		<!-- Geometric Shapes -->
+		<entity name="loz" cdata="&#9674;"/> <!--lozenge, U+25CA ISOpub -->
+		
+		<!-- Miscellaneous Symbols -->
+		<entity name="spades" cdata="&#9824;"/> <!--black spade suit, U+2660 ISOpub -->
+		<!-- black here seems to mean filled as opposed to hollow -->
+		<entity name="clubs" cdata="&#9827;"/> <!--black club suit = shamrock, U+2663 ISOpub -->
+		<entity name="hearts" cdata="&#9829;"/> <!--black heart suit = valentine, U+2665 ISOpub -->
+		<entity name="diams" cdata="&#9830;"/> <!--black diamond suit, U+2666 ISOpub -->
+		
+		<entity name="quot" cdata="&#34;"  /> <!--quotation mark = APL quote, U+0022 ISOnum -->
+		<!-- Latin Extended-A -->
+		<entity name="OElig" cdata="&#338;" /> <!--latin capital ligature OE, U+0152 ISOlat2 -->
+		<entity name="oelig" cdata="&#339;" /> <!--latin small ligature oe, U+0153 ISOlat2 -->
+		<!-- ligature is a misnomer, this is a separate character in some languages -->
+		<entity name="Scaron" cdata="&#352;" /> <!--latin capital letter S with caron, U+0160 ISOlat2 -->
+		<entity name="scaron" cdata="&#353;" /> <!--latin small letter s with caron, U+0161 ISOlat2 -->
+		<entity name="Yuml" cdata="&#376;" /> <!--latin capital letter Y with diaeresis, U+0178 ISOlat2 -->
+		
+		<!-- Spacing Modifier Letters -->
+		<entity name="circ" cdata="&#710;" /> <!--modifier letter circumflex accent, U+02C6 ISOpub -->
+		<entity name="tilde" cdata="&#732;" /> <!--small tilde, U+02DC ISOdia -->
+		
+		<!-- General Punctuation -->
+		<entity name="ensp" cdata="&#8194;"/> <!--en space, U+2002 ISOpub -->
+		<entity name="emsp" cdata="&#8195;"/> <!--em space, U+2003 ISOpub -->
+		<entity name="thinsp" cdata="&#8201;"/> <!--thin space, U+2009 ISOpub -->
+		<entity name="zwnj" cdata="&#8204;"/> <!--zero width non-joiner, U+200C NEW RFC 2070 -->
+		<entity name="zwj" cdata="&#8205;"/> <!--zero width joiner, U+200D NEW RFC 2070 -->
+		<entity name="lrm" cdata="&#8206;"/> <!--left-to-right mark, U+200E NEW RFC 2070 -->
+		<entity name="rlm" cdata="&#8207;"/> <!--right-to-left mark, U+200F NEW RFC 2070 -->
+		<entity name="ndash" cdata="&#8211;"/> <!--en dash, U+2013 ISOpub -->
+		<entity name="mdash" cdata="&#8212;"/> <!--em dash, U+2014 ISOpub -->
+		<entity name="lsquo" cdata="&#8216;"/> <!--left single quotation mark, U+2018 ISOnum -->
+		<entity name="rsquo" cdata="&#8217;"/> <!--right single quotation mark, U+2019 ISOnum -->
+		<entity name="sbquo" cdata="&#8218;"/> <!--single low-9 quotation mark, U+201A NEW -->
+		<entity name="ldquo" cdata="&#8220;"/> <!--left double quotation mark, U+201C ISOnum -->
+		<entity name="rdquo" cdata="&#8221;"/> <!--right double quotation mark, U+201D ISOnum -->
+		<entity name="bdquo" cdata="&#8222;"/> <!--double low-9 quotation mark, U+201E NEW -->
+		<entity name="dagger" cdata="&#8224;"/> <!--dagger, U+2020 ISOpub -->
+		<entity name="Dagger" cdata="&#8225;"/> <!--double dagger, U+2021 ISOpub -->
+		<entity name="permil" cdata="&#8240;"/> <!--per mille sign, U+2030 ISOtech -->
+		<entity name="lsaquo" cdata="&#8249;"/> <!--single left-pointing angle quotation mark, U+2039 ISO proposed -->
+		<!-- lsaquo is proposed but not yet ISO standardized -->
+		<entity name="rsaquo" cdata="&#8250;"/> <!--single right-pointing angle quotation mark, U+203A ISO proposed -->
+		<!-- rsaquo is proposed but not yet ISO standardized -->
+		<entity name="euro" cdata="&#8364;" /> <!--euro sign, U+20AC NEW -->
+	</html-entities>
+
+</anti-samy-rules>
diff --git a/configuration/esapi/users.txt b/configuration/esapi/users.txt
new file mode 100644
index 0000000..e69de29
diff --git a/configuration/esapi/validation.properties b/configuration/esapi/validation.properties
new file mode 100644
index 0000000..18e037f
--- /dev/null
+++ b/configuration/esapi/validation.properties
@@ -0,0 +1,29 @@
+# The ESAPI validator does many security checks on input, such as canonicalization
+# and whitelist validation. Note that all of these validation rules are applied *after*
+# canonicalization. Double-encoded characters (even with different encodings involved,
+# are never allowed.
+#
+# To use:
+#
+# First set up a pattern below. You can choose any name you want, prefixed by the word
+# "Validation." For example:
+#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+# 
+# Then you can validate in your code against the pattern like this:
+#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
+# Where maxLength and allowNull are set for you needs, respectively.
+#
+# But note, when you use boolean variants of validation functions, you lose critical 
+# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
+# and use the returned user input which is in canonical form. Consider the following:
+#  
+# try {
+#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
+#
+Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
+Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
+Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
+Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
+Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
+
diff --git a/configuration/esapi/waf-policies/.svn/all-wcprops b/configuration/esapi/waf-policies/.svn/all-wcprops
new file mode 100644
index 0000000..a07ac4d
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/all-wcprops
@@ -0,0 +1,107 @@
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies
+END
+dynamic-insertion-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/dynamic-insertion-policy.xml
+END
+bean-shell-rule.bsh
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/bean-shell-rule.bsh
+END
+restrict-source-ip-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/restrict-source-ip-policy.xml
+END
+detect-outbound-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/detect-outbound-policy.xml
+END
+restrict-user-agent-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/restrict-user-agent-policy.xml
+END
+add-httponly-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/add-httponly-policy.xml
+END
+replace-outbound-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/replace-outbound-policy.xml
+END
+restrict-extension-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/restrict-extension-policy.xml
+END
+bean-shell-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/bean-shell-policy.xml
+END
+add-secure-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/add-secure-policy.xml
+END
+restrict-method-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/restrict-method-policy.xml
+END
+add-header-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/add-header-policy.xml
+END
+authentication-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/authentication-policy.xml
+END
+enforce-https-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/enforce-https-policy.xml
+END
+must-match-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/must-match-policy.xml
+END
+virtual-patch-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/virtual-patch-policy.xml
+END
+restrict-content-type-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/esapi/waf-policies/restrict-content-type-policy.xml
+END
diff --git a/configuration/esapi/waf-policies/.svn/entries b/configuration/esapi/waf-policies/.svn/entries
new file mode 100644
index 0000000..2edcd7f
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/entries
@@ -0,0 +1,606 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/configuration/esapi/waf-policies
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+restrict-source-ip-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+2ff4f060683f25cffc45cee91aec8600
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+598
+

+detect-outbound-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+1e4da014d4f1f4b3f40008797088e8ae
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+662
+

+restrict-user-agent-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+34d64e18254f6cb9af74b668e63c174b
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+695
+

+add-httponly-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+82bc5d19bb17b3343aae63e47f8416a0
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+632
+

+replace-outbound-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+b32463cce0d1be245de1d47201d1b6a1
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+631
+

+restrict-extension-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+6828cc33d0100ce1b8126f54cf174bb4
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+572
+

+bean-shell-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+e7c750f6b58af472d74b945402333c94
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+582
+

+add-secure-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.537978Z
+bd19de30cd91a27d31523079c40db308
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+627
+

+restrict-method-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.537978Z
+351bfc3d8ed41387ccff7f1225e2b718
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+568
+

+add-header-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.537978Z
+4bd7604af016a4a51b22bed3ad0fa5c3
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+721
+

+authentication-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.537978Z
+7769322f7f92994216757d0a7d86a3fb
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+841
+

+enforce-https-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.537978Z
+6f0371a3e8f38aa91fd783c22f8024a6
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+703
+

+must-match-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.537978Z
+bf74ebd18e27431c6955709b9235a61b
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+906
+

+virtual-patch-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+05b950be72e41b9a168b4aeb1376f42a
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+746
+

+restrict-content-type-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+e633612947422491bf67c9a72fc95b40
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+715
+

+dynamic-insertion-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+b32463cce0d1be245de1d47201d1b6a1
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+631
+

+bean-shell-rule.bsh
+file
+
+
+
+
+2014-02-18T16:19:53.533977Z
+c79bade58fc528b189e4cdc5a1a7d385
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+123
+

diff --git a/configuration/esapi/waf-policies/.svn/text-base/add-header-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/add-header-policy.xml.svn-base
new file mode 100644
index 0000000..b2d8efc
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/add-header-policy.xml.svn-base
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-header rule.
+
+	Protection #1: Any response for a resource will contain a header "FOO" with
+	               a value "BAR".
+
+	Exception #1:  Any request for /marketing/* will not have any such header
+	               returned.
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-header name="FOO" value="BAR" path="/.*">
+			<path-exception type="regex">/marketing/.*</path-exception>
+		</add-header>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/add-httponly-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/add-httponly-policy.xml.svn-base
new file mode 100644
index 0000000..1232f59
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/add-httponly-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-http-flag rule.
+
+	Protection #1: Any cookie set by the application will have the HttpOnly flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<add-http-only-flag>
+			<cookie name=".*"/>
+		</add-http-only-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/add-secure-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/add-secure-policy.xml.svn-base
new file mode 100644
index 0000000..12298a4
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/add-secure-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-secure-flag rule.
+
+	Protection #1: Any cookie set by the application will have the secure flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-secure-flag>
+			<cookie name=".*"/>
+		</add-secure-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/authentication-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/authentication-policy.xml.svn-base
new file mode 100644
index 0000000..1c1a485
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/authentication-policy.xml.svn-base
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an authentication rule.
+
+	Authentication applies to: /.*
+	Session variable whose existence implies authentication: sessionUserObjKey
+	Static exception:   <path-exception>/index.html</path-exception>
+	Pattern exception:  <path-exception type="regex">/images/.*</path-exception>
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>>
+
+	<!--
+		Set authentication rules by path.
+	-->
+	<authentication-rules path="/.*"  key="sessionUserObjKey" >
+		<path-exception>/index.html</path-exception>
+		<path-exception type="regex">/images/.*</path-exception>
+	</authentication-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/bean-shell-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/bean-shell-policy.xml.svn-base
new file mode 100644
index 0000000..2e3f7bb
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/bean-shell-policy.xml.svn-base
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies different custom bean shell rules.
+	
+	
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<!--
+		Import the rules.
+	-->
+	<bean-shell-rules>	
+		<bean-shell-script 
+			name="example1" 
+			file="src/test/resources/.esapi/waf-policies/bean-shell-rule.bsh" 
+			stage="before-request-body"/>
+	</bean-shell-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/bean-shell-rule.bsh.svn-base b/configuration/esapi/waf-policies/.svn/text-base/bean-shell-rule.bsh.svn-base
new file mode 100644
index 0000000..2a1f423
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/bean-shell-rule.bsh.svn-base
@@ -0,0 +1,5 @@
+import org.owasp.esapi.waf.actions.*;
+
+session.setAttribute("simple_waf_test", "true");
+
+action = new RedirectAction();
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/detect-outbound-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/detect-outbound-policy.xml.svn-base
new file mode 100644
index 0000000..8312be9
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/detect-outbound-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a detect-content rule.
+
+	Protection #1: The rule should fire whenever the string "2008" appears in
+	               a response body.
+	Exception #1:  The rule should -not- fire when the content type is anything
+	               but text/*.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<detect-content content-type=".*text/.*" pattern=".*2008.*" />
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/dynamic-insertion-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/dynamic-insertion-policy.xml.svn-base
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/dynamic-insertion-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/enforce-https-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/enforce-https-policy.xml.svn-base
new file mode 100644
index 0000000..ab123b7
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/enforce-https-policy.xml.svn-base
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an enforce-https rule.
+
+	Protection #1: a request of any kind for /foo should be redirected to secure request with 302.
+	Exception #1: Static path /index.html
+	Exception #2: Pattern path /images/.*
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<enforce-https path="/.*">
+			<path-exception>/index.html</path-exception>
+			<path-exception type="regex">/images/.*</path-exception>
+		</enforce-https>
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/must-match-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/must-match-policy.xml.svn-base
new file mode 100644
index 0000000..721fa4c
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/must-match-policy.xml.svn-base
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a must-match rule.
+
+	Protection #1:
+	Protection applies to: /admin/.*
+	Header name needed to access: x-roles
+	Header value needs to *contain* the string: admin
+
+	Protection #2:
+	Protection applies to: /superadmin/.*
+	Header name needed to access: x-roles
+	Header value needs to *equal* the string: superadmin
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<must-match path="^/admin/.*" variable="request.header.x-roles"
+			operator="contains" value="admin" />
+		<must-match path="^/superadmin/.*" variable="request.header.x-roles"
+			operator="equals" value="superadmin" />
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/replace-outbound-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/replace-outbound-policy.xml.svn-base
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/replace-outbound-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/restrict-content-type-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/restrict-content-type-policy.xml.svn-base
new file mode 100644
index 0000000..a9b8a4a
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/restrict-content-type-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-content-type rule.
+
+	Protection #1: any request with a content-type containing the word 'multipart' will be rejected
+	Exception  #1: requests for /fileupload.jsp are allowed to have 'multipart' in content-type
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-content-type deny=".*multipart.*">
+			<path-exception type="regex">/fileupload.jsp</path-exception>
+		</restrict-content-type>
+
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/restrict-extension-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/restrict-extension-policy.xml.svn-base
new file mode 100644
index 0000000..fe1dbe1
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/restrict-extension-policy.xml.svn-base
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny=".*\.log$" />
+		<restrict-extension allow=".*\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/restrict-method-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/restrict-method-policy.xml.svn-base
new file mode 100644
index 0000000..5a2337c
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/restrict-method-policy.xml.svn-base
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny="\.log$" />
+		<restrict-extension allow="\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/restrict-source-ip-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/restrict-source-ip-policy.xml.svn-base
new file mode 100644
index 0000000..d23d87a
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/restrict-source-ip-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-source-ip rule.
+
+	The restriction applies to: /admin/.*
+	Pattern exception:  (192\.168\.1\\..*|127.0.0.1)
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<restrict-source-ip
+			type="regex"
+			ip-regex="(192\.168\.1\\..*|127.0.0.1)">/admin/.*</restrict-source-ip>
+
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/restrict-user-agent-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/restrict-user-agent-policy.xml.svn-base
new file mode 100644
index 0000000..7cdebd5
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/restrict-user-agent-policy.xml.svn-base
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-user-agent rule.
+
+	Protection #1: any request with a user agent containing the word 'GoogleBot' will be rejected
+	Exception  #1: requests for /index.html are allowed to have 'GoogleBot' in user agent
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-user-agent deny=".*GoogleBot.*">
+			<path-exception type="regex">/index.html</path-exception>
+		</restrict-user-agent>
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/.svn/text-base/virtual-patch-policy.xml.svn-base b/configuration/esapi/waf-policies/.svn/text-base/virtual-patch-policy.xml.svn-base
new file mode 100644
index 0000000..61c93fa
--- /dev/null
+++ b/configuration/esapi/waf-policies/.svn/text-base/virtual-patch-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a virtual-patch rule.
+
+	Protection #1: Any request whose URI is /foo.jsp (despite content-type) will have
+	               the 'bar' parameter checked to see if its alphanumeric. If a parameter
+				   fails validation, the message "zomg attax" will be logged.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<virtual-patches>
+		<virtual-patch id="1234" path="/foo.jsp" variable="request.parameters.bar"
+			pattern="[0-9a-zA-Z]" message="zomg attax" />
+	</virtual-patches>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/add-header-policy.xml b/configuration/esapi/waf-policies/add-header-policy.xml
new file mode 100644
index 0000000..b2d8efc
--- /dev/null
+++ b/configuration/esapi/waf-policies/add-header-policy.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-header rule.
+
+	Protection #1: Any response for a resource will contain a header "FOO" with
+	               a value "BAR".
+
+	Exception #1:  Any request for /marketing/* will not have any such header
+	               returned.
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-header name="FOO" value="BAR" path="/.*">
+			<path-exception type="regex">/marketing/.*</path-exception>
+		</add-header>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/add-httponly-policy.xml b/configuration/esapi/waf-policies/add-httponly-policy.xml
new file mode 100644
index 0000000..1232f59
--- /dev/null
+++ b/configuration/esapi/waf-policies/add-httponly-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-http-flag rule.
+
+	Protection #1: Any cookie set by the application will have the HttpOnly flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<add-http-only-flag>
+			<cookie name=".*"/>
+		</add-http-only-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/add-secure-policy.xml b/configuration/esapi/waf-policies/add-secure-policy.xml
new file mode 100644
index 0000000..12298a4
--- /dev/null
+++ b/configuration/esapi/waf-policies/add-secure-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-secure-flag rule.
+
+	Protection #1: Any cookie set by the application will have the secure flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-secure-flag>
+			<cookie name=".*"/>
+		</add-secure-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/authentication-policy.xml b/configuration/esapi/waf-policies/authentication-policy.xml
new file mode 100644
index 0000000..1c1a485
--- /dev/null
+++ b/configuration/esapi/waf-policies/authentication-policy.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an authentication rule.
+
+	Authentication applies to: /.*
+	Session variable whose existence implies authentication: sessionUserObjKey
+	Static exception:   <path-exception>/index.html</path-exception>
+	Pattern exception:  <path-exception type="regex">/images/.*</path-exception>
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>>
+
+	<!--
+		Set authentication rules by path.
+	-->
+	<authentication-rules path="/.*"  key="sessionUserObjKey" >
+		<path-exception>/index.html</path-exception>
+		<path-exception type="regex">/images/.*</path-exception>
+	</authentication-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/bean-shell-policy.xml b/configuration/esapi/waf-policies/bean-shell-policy.xml
new file mode 100644
index 0000000..2e3f7bb
--- /dev/null
+++ b/configuration/esapi/waf-policies/bean-shell-policy.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies different custom bean shell rules.
+	
+	
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<!--
+		Import the rules.
+	-->
+	<bean-shell-rules>	
+		<bean-shell-script 
+			name="example1" 
+			file="src/test/resources/.esapi/waf-policies/bean-shell-rule.bsh" 
+			stage="before-request-body"/>
+	</bean-shell-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/bean-shell-rule.bsh b/configuration/esapi/waf-policies/bean-shell-rule.bsh
new file mode 100644
index 0000000..2a1f423
--- /dev/null
+++ b/configuration/esapi/waf-policies/bean-shell-rule.bsh
@@ -0,0 +1,5 @@
+import org.owasp.esapi.waf.actions.*;
+
+session.setAttribute("simple_waf_test", "true");
+
+action = new RedirectAction();
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/detect-outbound-policy.xml b/configuration/esapi/waf-policies/detect-outbound-policy.xml
new file mode 100644
index 0000000..8312be9
--- /dev/null
+++ b/configuration/esapi/waf-policies/detect-outbound-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a detect-content rule.
+
+	Protection #1: The rule should fire whenever the string "2008" appears in
+	               a response body.
+	Exception #1:  The rule should -not- fire when the content type is anything
+	               but text/*.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<detect-content content-type=".*text/.*" pattern=".*2008.*" />
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/dynamic-insertion-policy.xml b/configuration/esapi/waf-policies/dynamic-insertion-policy.xml
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/configuration/esapi/waf-policies/dynamic-insertion-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/enforce-https-policy.xml b/configuration/esapi/waf-policies/enforce-https-policy.xml
new file mode 100644
index 0000000..ab123b7
--- /dev/null
+++ b/configuration/esapi/waf-policies/enforce-https-policy.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an enforce-https rule.
+
+	Protection #1: a request of any kind for /foo should be redirected to secure request with 302.
+	Exception #1: Static path /index.html
+	Exception #2: Pattern path /images/.*
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<enforce-https path="/.*">
+			<path-exception>/index.html</path-exception>
+			<path-exception type="regex">/images/.*</path-exception>
+		</enforce-https>
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/must-match-policy.xml b/configuration/esapi/waf-policies/must-match-policy.xml
new file mode 100644
index 0000000..721fa4c
--- /dev/null
+++ b/configuration/esapi/waf-policies/must-match-policy.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a must-match rule.
+
+	Protection #1:
+	Protection applies to: /admin/.*
+	Header name needed to access: x-roles
+	Header value needs to *contain* the string: admin
+
+	Protection #2:
+	Protection applies to: /superadmin/.*
+	Header name needed to access: x-roles
+	Header value needs to *equal* the string: superadmin
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<must-match path="^/admin/.*" variable="request.header.x-roles"
+			operator="contains" value="admin" />
+		<must-match path="^/superadmin/.*" variable="request.header.x-roles"
+			operator="equals" value="superadmin" />
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/replace-outbound-policy.xml b/configuration/esapi/waf-policies/replace-outbound-policy.xml
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/configuration/esapi/waf-policies/replace-outbound-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/restrict-content-type-policy.xml b/configuration/esapi/waf-policies/restrict-content-type-policy.xml
new file mode 100644
index 0000000..a9b8a4a
--- /dev/null
+++ b/configuration/esapi/waf-policies/restrict-content-type-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-content-type rule.
+
+	Protection #1: any request with a content-type containing the word 'multipart' will be rejected
+	Exception  #1: requests for /fileupload.jsp are allowed to have 'multipart' in content-type
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-content-type deny=".*multipart.*">
+			<path-exception type="regex">/fileupload.jsp</path-exception>
+		</restrict-content-type>
+
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/restrict-extension-policy.xml b/configuration/esapi/waf-policies/restrict-extension-policy.xml
new file mode 100644
index 0000000..fe1dbe1
--- /dev/null
+++ b/configuration/esapi/waf-policies/restrict-extension-policy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny=".*\.log$" />
+		<restrict-extension allow=".*\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/restrict-method-policy.xml b/configuration/esapi/waf-policies/restrict-method-policy.xml
new file mode 100644
index 0000000..5a2337c
--- /dev/null
+++ b/configuration/esapi/waf-policies/restrict-method-policy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny="\.log$" />
+		<restrict-extension allow="\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/restrict-source-ip-policy.xml b/configuration/esapi/waf-policies/restrict-source-ip-policy.xml
new file mode 100644
index 0000000..d23d87a
--- /dev/null
+++ b/configuration/esapi/waf-policies/restrict-source-ip-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-source-ip rule.
+
+	The restriction applies to: /admin/.*
+	Pattern exception:  (192\.168\.1\\..*|127.0.0.1)
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<restrict-source-ip
+			type="regex"
+			ip-regex="(192\.168\.1\\..*|127.0.0.1)">/admin/.*</restrict-source-ip>
+
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/restrict-user-agent-policy.xml b/configuration/esapi/waf-policies/restrict-user-agent-policy.xml
new file mode 100644
index 0000000..7cdebd5
--- /dev/null
+++ b/configuration/esapi/waf-policies/restrict-user-agent-policy.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-user-agent rule.
+
+	Protection #1: any request with a user agent containing the word 'GoogleBot' will be rejected
+	Exception  #1: requests for /index.html are allowed to have 'GoogleBot' in user agent
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-user-agent deny=".*GoogleBot.*">
+			<path-exception type="regex">/index.html</path-exception>
+		</restrict-user-agent>
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policies/virtual-patch-policy.xml b/configuration/esapi/waf-policies/virtual-patch-policy.xml
new file mode 100644
index 0000000..61c93fa
--- /dev/null
+++ b/configuration/esapi/waf-policies/virtual-patch-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a virtual-patch rule.
+
+	Protection #1: Any request whose URI is /foo.jsp (despite content-type) will have
+	               the 'bar' parameter checked to see if its alphanumeric. If a parameter
+				   fails validation, the message "zomg attax" will be logged.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<virtual-patches>
+		<virtual-patch id="1234" path="/foo.jsp" variable="request.parameters.bar"
+			pattern="[0-9a-zA-Z]" message="zomg attax" />
+	</virtual-patches>
+
+</policy>
\ No newline at end of file
diff --git a/configuration/esapi/waf-policy.xsd b/configuration/esapi/waf-policy.xsd
new file mode 100644
index 0000000..4287e79
Binary files /dev/null and b/configuration/esapi/waf-policy.xsd differ
diff --git a/configuration/log4j.dtd b/configuration/log4j.dtd
new file mode 100644
index 0000000..1aabd96
--- /dev/null
+++ b/configuration/log4j.dtd
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Authors: Chris Taylor, Ceki Gulcu. -->
+
+<!-- Version: 1.2 -->
+
+<!-- A configuration element consists of optional renderer
+elements,appender elements, categories and an optional root
+element. -->
+
+<!ELEMENT log4j:configuration (renderer*, appender*,plugin*, (category|logger)*,root?,
+                               (categoryFactory|loggerFactory)?)>
+
+<!-- The "threshold" attribute takes a level value below which -->
+<!-- all logging statements are disabled. -->
+
+<!-- Setting the "debug" enable the printing of internal log4j logging   -->
+<!-- statements.                                                         -->
+
+<!-- By default, debug attribute is "null", meaning that we not do touch -->
+<!-- internal log4j logging settings. The "null" value for the threshold -->
+<!-- attribute can be misleading. The threshold field of a repository	 -->
+<!-- cannot be set to null. The "null" value for the threshold attribute -->
+<!-- simply means don't touch the threshold field, the threshold field   --> 
+<!-- keeps its old value.                                                -->
+     
+<!ATTLIST log4j:configuration
+  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  threshold                (all|trace|debug|info|warn|error|fatal|off|null) "null"
+  debug                    (true|false|null)  "null"
+  reset                    (true|false) "false"
+>
+
+<!-- renderer elements allow the user to customize the conversion of  -->
+<!-- message objects to String.                                       -->
+
+<!ELEMENT renderer EMPTY>
+<!ATTLIST renderer
+  renderedClass  CDATA #REQUIRED
+  renderingClass CDATA #REQUIRED
+>
+
+<!-- Appenders must have a name and a class. -->
+<!-- Appenders may contain an error handler, a layout, optional parameters -->
+<!-- and filters. They may also reference (or include) other appenders. -->
+<!ELEMENT appender (errorHandler?, param*,
+      rollingPolicy?, triggeringPolicy?, connectionSource?,
+      layout?, filter*, appender-ref*)>
+<!ATTLIST appender
+  name 		CDATA 	#REQUIRED
+  class 	CDATA	#REQUIRED
+>
+
+<!ELEMENT layout (param*)>
+<!ATTLIST layout
+  class		CDATA	#REQUIRED
+>
+
+<!ELEMENT filter (param*)>
+<!ATTLIST filter
+  class		CDATA	#REQUIRED
+>
+
+<!-- ErrorHandlers can be of any class. They can admit any number of -->
+<!-- parameters. -->
+
+<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)> 
+<!ATTLIST errorHandler
+   class        CDATA   #REQUIRED 
+>
+
+<!ELEMENT root-ref EMPTY>
+
+<!ELEMENT logger-ref EMPTY>
+<!ATTLIST logger-ref
+  ref CDATA #REQUIRED
+>
+
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+  name		CDATA   #REQUIRED
+  value		CDATA	#REQUIRED
+>
+
+
+<!-- The priority class is org.apache.log4j.Level by default -->
+<!ELEMENT priority (param*)>
+<!ATTLIST priority
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+<!-- The level class is org.apache.log4j.Level by default -->
+<!ELEMENT level (param*)>
+<!ATTLIST level
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named category. -->
+<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
+<!ATTLIST category
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named logger. -->
+<!ELEMENT logger (level?,appender-ref*)>
+<!ATTLIST logger
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+
+<!ELEMENT categoryFactory (param*)>
+<!ATTLIST categoryFactory 
+   class        CDATA #REQUIRED>
+
+<!ELEMENT loggerFactory (param*)>
+<!ATTLIST loggerFactory
+   class        CDATA #REQUIRED>
+
+<!ELEMENT appender-ref EMPTY>
+<!ATTLIST appender-ref
+  ref CDATA #REQUIRED
+>
+
+<!-- plugins must have a name and class and can have optional parameters -->
+<!ELEMENT plugin (param*, connectionSource?)>
+<!ATTLIST plugin
+  name 		CDATA 	   #REQUIRED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT connectionSource (dataSource?, param*)>
+<!ATTLIST connectionSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT dataSource (param*)>
+<!ATTLIST dataSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT triggeringPolicy ((param|filter)*)>
+<!ATTLIST triggeringPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT rollingPolicy (param*)>
+<!ATTLIST rollingPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+
+<!-- If no priority element is specified, then the configurator MUST not -->
+<!-- touch the priority of root. -->
+<!-- The root category always exists and cannot be subclassed. -->
+<!ELEMENT root (param*, (priority|level)?, appender-ref*)>
+
+
+<!-- ==================================================================== -->
+<!--                       A logging event                                -->
+<!-- ==================================================================== -->
+<!ELEMENT log4j:eventSet (log4j:event*)>
+<!ATTLIST log4j:eventSet
+  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  version                (1.1|1.2) "1.2" 
+  includesLocationInfo   (true|false) "true"
+>
+
+
+
+<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?, 
+                       log4j:locationInfo?, log4j:properties?) >
+
+<!-- The timestamp format is application dependent. -->
+<!ATTLIST log4j:event
+    logger     CDATA #REQUIRED
+    level      CDATA #REQUIRED
+    thread     CDATA #REQUIRED
+    timestamp  CDATA #REQUIRED
+    time       CDATA #IMPLIED
+>
+
+<!ELEMENT log4j:message (#PCDATA)>
+<!ELEMENT log4j:NDC (#PCDATA)>
+
+<!ELEMENT log4j:throwable (#PCDATA)>
+
+<!ELEMENT log4j:locationInfo EMPTY>
+<!ATTLIST log4j:locationInfo
+  class  CDATA	#REQUIRED
+  method CDATA	#REQUIRED
+  file   CDATA	#REQUIRED
+  line   CDATA	#REQUIRED
+>
+
+<!ELEMENT log4j:properties (log4j:data*)>
+
+<!ELEMENT log4j:data EMPTY>
+<!ATTLIST log4j:data
+  name   CDATA	#REQUIRED
+  value  CDATA	#REQUIRED
+>
diff --git a/configuration/log4j.xml b/configuration/log4j.xml
new file mode 100644
index 0000000..6f895d5
--- /dev/null
+++ b/configuration/log4j.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!-- main resources -->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="console" class="org.apache.log4j.ConsoleAppender"> 
+    <param name="Target" value="System.out"/> 
+    <layout class="org.apache.log4j.PatternLayout"> 
+      <param name="ConversionPattern" value="%-5p %m%n"/> 
+    </layout> 
+  </appender> 
+
+  <logger name="org.owasp.esapi.reference.TestTrace">
+    <level value="trace"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestDebug">
+    <level value="debug"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestInfo">
+    <level value="info"/>
+ </logger>
+
+  <logger name="org.owasp.esapi.reference.TestWarning">
+    <level value="warn"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestError">
+    <level value="error"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestFatal">
+    <level value="fatal"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference">
+    <level value="info"/>
+  </logger>
+
+  <root> 
+    <priority value ="debug" /> 
+    <appender-ref ref="console" /> 
+  </root>
+
+  <loggerFactory class="org.owasp.esapi.reference.Log4JLoggerFactory"/>
+  
+</log4j:configuration>
diff --git a/configuration/properties/.svn/all-wcprops b/configuration/properties/.svn/all-wcprops
new file mode 100644
index 0000000..0bc16da
--- /dev/null
+++ b/configuration/properties/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/properties
+END
+ESAPI_en_US.properties
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/properties/ESAPI_en_US.properties
+END
+ESAPI_fr_FR.properties
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/properties/ESAPI_fr_FR.properties
+END
+ESAPI_zhs_CN.properties
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/1899/tags/esapi-2.1.0/configuration/properties/ESAPI_zhs_CN.properties
+END
diff --git a/configuration/properties/.svn/entries b/configuration/properties/.svn/entries
new file mode 100644
index 0000000..783c90f
--- /dev/null
+++ b/configuration/properties/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/configuration/properties
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-10-07T04:07:35.229541Z
+1550
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+ESAPI_zhs_CN.properties
+file
+
+
+
+
+2014-02-18T16:19:53.561978Z
+7270334fb9959d18b8d28d296955c1c5
+2009-05-06T03:37:24.782205Z
+494
+singhpawanpreet at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+40
+

+ESAPI_en_US.properties
+file
+
+
+
+
+2014-02-18T16:19:53.565978Z
+ad9bd570860eda9a26787bf0827e5ff7
+2009-05-06T03:37:24.782205Z
+494
+singhpawanpreet at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+174
+

+ESAPI_fr_FR.properties
+file
+
+
+
+
+2014-02-18T16:19:53.565978Z
+9e399d84513a2118654a27916f97f5d6
+2009-04-21T21:02:25.549748Z
+478
+singhpawanpreet at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+192
+

diff --git a/configuration/properties/.svn/text-base/ESAPI_en_US.properties.svn-base b/configuration/properties/.svn/text-base/ESAPI_en_US.properties.svn-base
new file mode 100644
index 0000000..9b43d1a
--- /dev/null
+++ b/configuration/properties/.svn/text-base/ESAPI_en_US.properties.svn-base
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Error creating randomizer
+
+This.is.test.message=This {0} is {1} a test {2} message
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/configuration/properties/.svn/text-base/ESAPI_fr_FR.properties.svn-base b/configuration/properties/.svn/text-base/ESAPI_fr_FR.properties.svn-base
new file mode 100644
index 0000000..89ebcba
--- /dev/null
+++ b/configuration/properties/.svn/text-base/ESAPI_fr_FR.properties.svn-base
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Erreur lors de la cr�ation al�atoire
+
+This.is.test.message=Ceci est un message de test message singh
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/configuration/properties/.svn/text-base/ESAPI_zhs_CN.properties.svn-base b/configuration/properties/.svn/text-base/ESAPI_zhs_CN.properties.svn-base
new file mode 100644
index 0000000..2287405
--- /dev/null
+++ b/configuration/properties/.svn/text-base/ESAPI_zhs_CN.properties.svn-base
@@ -0,0 +1 @@
+Back_error_popup.tpl.13=��������˻�����
\ No newline at end of file
diff --git a/configuration/properties/ESAPI_en_US.properties b/configuration/properties/ESAPI_en_US.properties
new file mode 100644
index 0000000..9b43d1a
--- /dev/null
+++ b/configuration/properties/ESAPI_en_US.properties
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Error creating randomizer
+
+This.is.test.message=This {0} is {1} a test {2} message
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/configuration/properties/ESAPI_fr_FR.properties b/configuration/properties/ESAPI_fr_FR.properties
new file mode 100644
index 0000000..89ebcba
--- /dev/null
+++ b/configuration/properties/ESAPI_fr_FR.properties
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Erreur lors de la cr�ation al�atoire
+
+This.is.test.message=Ceci est un message de test message singh
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/configuration/properties/ESAPI_zhs_CN.properties b/configuration/properties/ESAPI_zhs_CN.properties
new file mode 100644
index 0000000..2287405
--- /dev/null
+++ b/configuration/properties/ESAPI_zhs_CN.properties
@@ -0,0 +1 @@
+Back_error_popup.tpl.13=��������˻�����
\ No newline at end of file
diff --git a/documentation/.svn/all-wcprops b/documentation/.svn/all-wcprops
new file mode 100644
index 0000000..172119f
--- /dev/null
+++ b/documentation/.svn/all-wcprops
@@ -0,0 +1,143 @@
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation
+END
+Analysis-of-ESAPI-2.0-KDF.pdf
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/Analysis-of-ESAPI-2.0-KDF.pdf
+END
+esapi4java-big-duke.JPG
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-big-duke.JPG
+END
+esapi4java-core-2.0-readme-crypto-changes.html
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-readme-crypto-changes.html
+END
+esapi4java-waf-2.0-policy-file-spec.docx
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-waf-2.0-policy-file-spec.docx
+END
+esapi4java-core-2.1-release-notes.txt
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.1-release-notes.txt
+END
+esapi4java-2.0-readme.txt
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-2.0-readme.txt
+END
+cc.JPG
+K 25
+svn:wc:ra_dav:version-url
+V 56
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/cc.JPG
+END
+esapi4java-core-2.0-crypto-design-goals.doc
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-crypto-design-goals.doc
+END
+esapi4java-2.0rc6-override-log4jloggingfactory.txt
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt
+END
+esapi4java-core-2.0-ciphertext-serialization.xls
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-ciphertext-serialization.xls
+END
+Analysis-of-ESAPI-2.0-KDF.odt
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/Analysis-of-ESAPI-2.0-KDF.odt
+END
+esapi4java-waf-2.0-policy-file-spec.pdf
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-waf-2.0-policy-file-spec.pdf
+END
+esapi4java-core-2.0-ciphertext-serialization.pdf
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf
+END
+esapi4java-core-2.0-symmetric-crypto-user-guide.html
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
+END
+esapi4java-google-code.doc
+K 25
+svn:wc:ra_dav:version-url
+V 76
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-google-code.doc
+END
+esapi4java-core-2.0-install-guide.doc
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-install-guide.doc
+END
+esapi4java-2.0-javadoc-pictures.pptx
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-2.0-javadoc-pictures.pptx
+END
+esapi4java-core-2.0-install-guide.pdf
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-install-guide.pdf
+END
+esapi4java-waf.JPG
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-waf.JPG
+END
+esapi4java-core-2.0-release-notes.doc
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-release-notes.doc
+END
+esapi4java-disa.JPG
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-disa.JPG
+END
+esapi4java-core-2.0-release-notes.pdf
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-core-2.0-release-notes.pdf
+END
+esapi4java-google-code.JPG
+K 25
+svn:wc:ra_dav:version-url
+V 76
+/svn/!svn/ver/1899/tags/esapi-2.1.0/documentation/esapi4java-google-code.JPG
+END
diff --git a/documentation/.svn/entries b/documentation/.svn/entries
new file mode 100644
index 0000000..94507df
--- /dev/null
+++ b/documentation/.svn/entries
@@ -0,0 +1,810 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/documentation
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-30T03:33:42.825975Z
+1889
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+Analysis-of-ESAPI-2.0-KDF.pdf
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+b789a03b30425f3297780bfc1c9470a5
+2011-02-04T06:38:30.022799Z
+1714
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+313772
+

+esapi4java-big-duke.JPG
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+93db3467bed228f807c4ea6d622726d9
+2010-04-29T12:58:27.893960Z
+1273
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8350
+

+esapi4java-core-2.0-readme-crypto-changes.html
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+7ad80e5e1208df6527d39971a4a48240
+2010-10-11T14:14:37.204391Z
+1555
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22120
+

+esapi4java-waf-2.0-policy-file-spec.docx
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+185161ad734ec78ea85e10d91a1e1a01
+2009-11-13T21:30:01.452262Z
+827
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+216462
+

+esapi4java-core-2.1-release-notes.txt
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+d0bd933fe53421de814e28f4f69a51eb
+2013-08-30T03:33:42.825975Z
+1889
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3912
+

+esapi4java-2.0-readme.txt
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+d0c29c93fa243baa33f36e24d46b8c63
+2013-08-29T00:54:52.980029Z
+1883
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3744
+

+cc.JPG
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+bd34a41f2b45595458bec62bbc01ba94
+2010-04-29T15:50:40.963907Z
+1331
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4578
+

+esapi4java-2.0rc6-override-log4jloggingfactory.txt
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+948660167f8f4925dba58104efed47ce
+2010-02-24T03:05:23.135018Z
+1188
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3372
+

+esapi4java-core-2.0-crypto-design-goals.doc
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+a6cf9ab3ddbd02d4a163581379f95388
+2010-11-23T21:53:32.926704Z
+1658
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+119808
+

+esapi4java-core-2.0-ciphertext-serialization.xls
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+e947b8bd33a633936a5c246b9e6b2357
+2011-02-04T05:13:24.924022Z
+1683
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+16384
+

+Analysis-of-ESAPI-2.0-KDF.odt
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+37862270d8ae53d0a7258e449fec3dfd
+2011-02-04T06:38:06.966579Z
+1713
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22536
+

+esapi4java-waf-2.0-policy-file-spec.pdf
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+02f5b7e0051ad7b9d7131c3ea7437eb3
+2009-11-13T21:14:04.703710Z
+823
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+758388
+

+esapi4java-core-2.0-ciphertext-serialization.pdf
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+5ce5d9b4f3ae8ea5c839f3fdd1c49b35
+2011-02-04T05:13:11.986300Z
+1682
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+136825
+

+esapi4java-core-2.0-symmetric-crypto-user-guide.html
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+08600394b797469a799e1608440aec40
+2011-02-06T01:19:59.981853Z
+1716
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+38170
+

+esapi4java-google-code.doc
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+632f621d00132206109ebb065a845761
+2009-12-04T22:00:35.780227Z
+845
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+141824
+

+esapi4java-core-2.0-install-guide.doc
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+92657251ebd89d859adf15fea07ea93a
+2010-10-24T21:10:51.076131Z
+1626
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+278016
+

+esapi4java-2.0-javadoc-pictures.pptx
+file
+
+
+
+
+2014-02-18T16:19:56.930045Z
+f2be6e0d27a02d40b9d444b2527f170e
+2009-11-13T21:14:04.703710Z
+823
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+130846
+

+esapi4java-core-2.0-install-guide.pdf
+file
+
+
+
+
+2014-02-18T16:19:56.930045Z
+f2f7e473df3dc2258378f6a3184a8ef3
+2010-10-24T21:11:50.309617Z
+1627
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+582255
+

+esapi4java-waf.JPG
+file
+
+
+
+
+2014-02-18T16:19:56.930045Z
+17029d1223177eabc3a2681ae2cbce03
+2010-04-30T16:44:34.351281Z
+1383
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+31960
+

+esapi4java-core-2.0-release-notes.doc
+file
+
+
+
+
+2014-02-18T16:19:56.930045Z
+3dbe77bf2a357a6ceb0223cf827036fb
+2010-09-28T03:46:04.117523Z
+1542
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+283648
+

+esapi4java-disa.JPG
+file
+
+
+
+
+2014-02-18T16:19:56.930045Z
+9c2f6b5a9f2eade8f18e92953a655d29
+2010-05-13T19:18:20.219832Z
+1404
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+47448
+

+esapi4java-core-2.0-release-notes.pdf
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+71c763f71b2de57e22159d7d6c374b6c
+2009-11-13T21:18:52.067887Z
+824
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+531816
+

+esapi4java-google-code.JPG
+file
+
+
+
+
+2014-02-18T16:19:56.934045Z
+90da66e7f18b4363e9064316dec74f9b
+2009-12-04T22:00:35.780227Z
+845
+mike.boberski
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2004
+

diff --git a/documentation/.svn/prop-base/Analysis-of-ESAPI-2.0-KDF.odt.svn-base b/documentation/.svn/prop-base/Analysis-of-ESAPI-2.0-KDF.odt.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/Analysis-of-ESAPI-2.0-KDF.odt.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/Analysis-of-ESAPI-2.0-KDF.pdf.svn-base b/documentation/.svn/prop-base/Analysis-of-ESAPI-2.0-KDF.pdf.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/Analysis-of-ESAPI-2.0-KDF.pdf.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/cc.JPG.svn-base b/documentation/.svn/prop-base/cc.JPG.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/cc.JPG.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-2.0-javadoc-pictures.pptx.svn-base b/documentation/.svn/prop-base/esapi4java-2.0-javadoc-pictures.pptx.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-2.0-javadoc-pictures.pptx.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-big-duke.JPG.svn-base b/documentation/.svn/prop-base/esapi4java-big-duke.JPG.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-big-duke.JPG.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-ciphertext-serialization.pdf.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-ciphertext-serialization.pdf.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-ciphertext-serialization.pdf.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-ciphertext-serialization.xls.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-ciphertext-serialization.xls.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-ciphertext-serialization.xls.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-crypto-design-goals.doc.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-crypto-design-goals.doc.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-crypto-design-goals.doc.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-install-guide.doc.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-install-guide.doc.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-install-guide.doc.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-readme-crypto-changes.html.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-readme-crypto-changes.html.svn-base
new file mode 100644
index 0000000..d356868
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-readme-crypto-changes.html.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 9
+text/html
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-release-notes.doc.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-release-notes.doc.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-release-notes.doc.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-release-notes.pdf.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-release-notes.pdf.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-release-notes.pdf.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-core-2.0-symmetric-crypto-user-guide.html.svn-base b/documentation/.svn/prop-base/esapi4java-core-2.0-symmetric-crypto-user-guide.html.svn-base
new file mode 100644
index 0000000..d356868
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-core-2.0-symmetric-crypto-user-guide.html.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 9
+text/html
+END
diff --git a/documentation/.svn/prop-base/esapi4java-disa.JPG.svn-base b/documentation/.svn/prop-base/esapi4java-disa.JPG.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-disa.JPG.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-google-code.JPG.svn-base b/documentation/.svn/prop-base/esapi4java-google-code.JPG.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-google-code.JPG.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-google-code.doc.svn-base b/documentation/.svn/prop-base/esapi4java-google-code.doc.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-google-code.doc.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-waf-2.0-policy-file-spec.docx.svn-base b/documentation/.svn/prop-base/esapi4java-waf-2.0-policy-file-spec.docx.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-waf-2.0-policy-file-spec.docx.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-waf-2.0-policy-file-spec.pdf.svn-base b/documentation/.svn/prop-base/esapi4java-waf-2.0-policy-file-spec.pdf.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-waf-2.0-policy-file-spec.pdf.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/prop-base/esapi4java-waf.JPG.svn-base b/documentation/.svn/prop-base/esapi4java-waf.JPG.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/documentation/.svn/prop-base/esapi4java-waf.JPG.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/documentation/.svn/text-base/Analysis-of-ESAPI-2.0-KDF.odt.svn-base b/documentation/.svn/text-base/Analysis-of-ESAPI-2.0-KDF.odt.svn-base
new file mode 100644
index 0000000..7301bc6
Binary files /dev/null and b/documentation/.svn/text-base/Analysis-of-ESAPI-2.0-KDF.odt.svn-base differ
diff --git a/documentation/.svn/text-base/Analysis-of-ESAPI-2.0-KDF.pdf.svn-base b/documentation/.svn/text-base/Analysis-of-ESAPI-2.0-KDF.pdf.svn-base
new file mode 100644
index 0000000..7bc07b1
Binary files /dev/null and b/documentation/.svn/text-base/Analysis-of-ESAPI-2.0-KDF.pdf.svn-base differ
diff --git a/documentation/.svn/text-base/cc.JPG.svn-base b/documentation/.svn/text-base/cc.JPG.svn-base
new file mode 100644
index 0000000..8ca55b7
Binary files /dev/null and b/documentation/.svn/text-base/cc.JPG.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-2.0-javadoc-pictures.pptx.svn-base b/documentation/.svn/text-base/esapi4java-2.0-javadoc-pictures.pptx.svn-base
new file mode 100644
index 0000000..388fbd9
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-2.0-javadoc-pictures.pptx.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-2.0-readme.txt.svn-base b/documentation/.svn/text-base/esapi4java-2.0-readme.txt.svn-base
new file mode 100644
index 0000000..de7c40d
--- /dev/null
+++ b/documentation/.svn/text-base/esapi4java-2.0-readme.txt.svn-base
@@ -0,0 +1,60 @@
+
+                            Welcome to ESAPI for Java!
+
+(This file best viewed full screen.)
+
+Here are the most significant directories and files included the zip file for this release:
+
+File / Directory                                                        Description
+=========================================================================================
+<root>/	
+|
++---configuration/                                                      Directory of ESAPI configuration files
+|     |
+|     |---.esapi/
+|     |     |---waf-policies/                                           Directory containing Web Application Firewall policies
+|     |     |---ESAPI.properties                                        The main ESAPI configuration file
+|     |     `---validation.properties                                   Regular expressions used by the ESAPI validator
+|     |
+|     `---properties/                                                   Examples of how to internationalize error messages???
+|           |---ESAPI_en_US.properties                                      in US/English
+|           |---ESAPI_fr_FR.properties                                      in French
+|           `---ESAPI_zhs_CN.properties                                     in Chinese
+|
+|---documentation/                                                      ESAPI documentation
+|     |
+|     |---esapi4java-2.0-readme.txt                                     The file you are now reading
+|     |---esapi4java-core-2.0-release-notes.pdf                         ESAPI 2.0 release notes (draft)
+|     |---esapi4java-core-2.0-install-guide.doc                         ESAPI 2.0 installation guide (draft)
+|     |---esapi4java-2.0rc6-override-log4jloggingfactory.txt            How to use log4j to override User logging
+|     |---esapi4java-core-2.0-ciphertext-serialization.pdf              Describes serialization layout of ESAPI 2.0 ciphertext representation
+|     |---esapi4java-core-2.0-crypto-design-goals.doc (draft)           Describes ESAPI 2.0 crypto design goals & design decisions
+|     |---esapi4java-core-2.0-readme-crypto-changes.html                Describes why crypto was changed from what was in ESAPI 1.4
+|     |---esapi4java-core-2.0-symmetric-crypto-user-guide.html          User guide for using symmetric encryption in ESAPI 2.0
+|     |---esapi4java-core-2.1-release-notes.txt                         ESAPI 2.1 release notes
+|     `---esapi4java-waf-2.0-policy-file-spec.pdf                       Describes how to configure ESAPI 2.0's Web Application Firewall
+|
+|---libs/                                                               ESAPI dependencies
+|
+|---site/
+|     |---apidocs                                                       ESAPI Javadoc
+|     |---cobertura
+|     `---testapidocs                                                   ESAPI Javadoc for its JUnit test cases
+|
+|---src/                                                                ESAPI source code
+|
+|---esapi-<vers>.jar                                                    The ESAPI jar for version <vers> (e.g., <vers> == 2.0_rc10)
+|
+|---LICENSE.txt                                                         ESAPI license for source code and documentation
+|
+`---pom.xml                                                             Maven's pom.xml for building ESAPI from source via mvn.
+
+===========================================================
+
+Where to go from here -- please see the installation guide and the release
+notes.
+
+Please address comments and questions concerning the API and this document to
+the ESAPI Users mailing list, <esapi-user at lists.owasp.org>.
+
+Copyright (C) 2009-2010 The OWASP Foundation.
diff --git a/documentation/.svn/text-base/esapi4java-2.0rc6-override-log4jloggingfactory.txt.svn-base b/documentation/.svn/text-base/esapi4java-2.0rc6-override-log4jloggingfactory.txt.svn-base
new file mode 100644
index 0000000..891f769
--- /dev/null
+++ b/documentation/.svn/text-base/esapi4java-2.0rc6-override-log4jloggingfactory.txt.svn-base
@@ -0,0 +1,71 @@
+This release includes critical changes to the ESAPI Log4JLogger that will now allow you to over-ride the user specific 
+message using your own User or java.security.Principal implementation.
+
+There are a three critical steps that need to be taken to over-ride the ESAPI Log4JLogger:
+
+1)  Please make a copy of http://owasp-esapi-java.googlecode.com/svn/trunk/src/main/java/org/owasp/esapi/reference/ExampleExtendedLog4JLogFactory.java and change the package and the class name (something like com.yourcompany.logging.ExtendedLog4JFactory). This class (not very big at all) gives you the exact “shell” that you will need to over-ride the user message of the ESAPI Log4JLogger.
+
+2)  In your new class, please change the following function to use your user object:
+
+        public String getUserInfo() {
+            return "-EXTENDEDUSERINFO-";
+    }
+
+3)  Change your copy of ESAPI.properties to use your new logging class
+
+The ESAPI.properties entry looks like this now:
+
+ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
+
+Please change it to the following, based on how you renamed your new logging class
+
+ESAPI.Logger=com.yourcompany.logging.ExtendedLog4JFactory
+
+And you should be all set!
+
+PS: The original ESAPI Log4JLogging class used a secure random number as a replacement to logging the session ID. This allowed 
+us to tie log messages from the same session together, without exposing the actual session id in the log file. The code looks 
+like this, and you may wish to use it in your over-ridden version of getUserInfo.
+
+HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+if ( request != null ) {
+    HttpSession session = request.getSession( false );
+    if ( session != null ) {
+        sid = (String)session.getAttribute("ESAPI_SESSION");
+        // if there is no session ID for the user yet, we create one and store it in the user's session
+        if ( sid == null ) {
+            sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000);
+            session.setAttribute("ESAPI_SESSION", sid);
+        }
+    }
+}
+
+In fact, here is the entire original getUserInfo() implementation (that was tied to the ESAPI request and user object) – 
+you may wish to emulate some of this.
+
+public String getUserInfo() {
+    // create a random session number for the user to represent the user's 'session', if it doesn't exist already
+    String sid = null;
+    HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+    if ( request != null ) {
+        HttpSession session = request.getSession( false );
+        if ( session != null ) {
+            sid = (String)session.getAttribute("ESAPI_SESSION");
+            // if there is no session ID for the user yet, we create one and store it in the user's session
+            if ( sid == null ) {
+                sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000);
+                session.setAttribute("ESAPI_SESSION", sid);
+            }
+        }
+    }
+    
+    // log user information - username:session at ipaddr
+    User user = ESAPI.authenticator().getCurrentUser();            
+    String userInfo = "";
+    //TODO - make type logging configurable
+    if ( user != null) {
+        userInfo += user.getAccountName()+ ":" + sid + "@"+ user.getLastHostAddress();
+    }
+    
+    return userInfo;
+}
diff --git a/documentation/.svn/text-base/esapi4java-big-duke.JPG.svn-base b/documentation/.svn/text-base/esapi4java-big-duke.JPG.svn-base
new file mode 100644
index 0000000..6f70077
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-big-duke.JPG.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-ciphertext-serialization.pdf.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-ciphertext-serialization.pdf.svn-base
new file mode 100644
index 0000000..9d1dc33
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-core-2.0-ciphertext-serialization.pdf.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-ciphertext-serialization.xls.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-ciphertext-serialization.xls.svn-base
new file mode 100644
index 0000000..7f3106e
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-core-2.0-ciphertext-serialization.xls.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-crypto-design-goals.doc.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-crypto-design-goals.doc.svn-base
new file mode 100644
index 0000000..836e8c7
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-core-2.0-crypto-design-goals.doc.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-install-guide.doc.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-install-guide.doc.svn-base
new file mode 100644
index 0000000..d185ecb
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-core-2.0-install-guide.doc.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-install-guide.pdf.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-install-guide.pdf.svn-base
new file mode 100644
index 0000000..012c497
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-core-2.0-install-guide.pdf.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-readme-crypto-changes.html.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-readme-crypto-changes.html.svn-base
new file mode 100644
index 0000000..8687f5c
--- /dev/null
+++ b/documentation/.svn/text-base/esapi4java-core-2.0-readme-crypto-changes.html.svn-base
@@ -0,0 +1,440 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+    <TITLE>Why Is OWASP Changing ESAPI Encryption?</TITLE>
+	<META NAME="GENERATOR" CONTENT="OpenOffice.org 3.0  (Linux)">
+	<META NAME="CREATED" CONTENT="0;0">
+	<META NAME="CHANGED" CONTENT="20100228;21125900">
+	<META NAME="CHANGEDBY" CONTENT="Kevin W. Wall">
+</HEAD>
+<BODY LANG="en-US" DIR="LTR">
+<H1 ALIGN=CENTER>Why Is OWASP Changing ESAPI Encryption?</H1>
+<H2>Reasons for Change</H2>
+<H3>Symmetric Encryption in ESAPI 1.4</H3>
+<P STYLE="margin-bottom: 0in">The as-delivered, default ESAPI 1.4
+encryption mechanism uses "PBEWithMD5AndDES", a "Password
+Based Encryption" algorithm. As implemented in ESAPI 1.4,
+password (or pass phrase), taken from the property MasterPassword, is
+concatenated with the salt as specified by the property MasterSalt,
+and then repeated hashed 20 times using the MD5 message digest
+algorithm. The intent of the salt and repetitive hashing is to slow
+down dictionary attacks on the password. The result after the 20
+iterations of hashing is used as the DES encryption key. The
+encryption uses Cipher Block Chaining (CBC) cipher mode.</P>
+<H3>Problems with Symmetric Encryption in ESAPI 1.4</H3>
+<P>The problems with symmetric encryption in ESAPI 1.4 are many. Here
+are some of the main issues:</P>
+<OL>
+	<LI><P>The implementation only supported a single encryption key.
+	While this may be sufficient for some uses, clearly there are cases
+	where multiple recipients are involved when you would like to use
+	unique encryption keys for each recipient.</P>
+	<LI><P>ESAPI 1.4 came with a default setting for MasterPassword and
+	MasterSalt, thus not requiring that it be changed. How many of you
+	forgot to change these?</P>
+	<LI><P>Password based encryption is likely to be much weaker
+	randomly choosing a secret key. For instance, if you restrict
+	yourself to printable ASCII characters (which most people will), it
+	would require a pass phrase of roughly 45 characters to be the
+	equivalent of a randomly chosen 128-bit secret key.</P>
+	<LI><P>The default algorithm, DES, only has an effective key length
+	of 56-bits. This key size is too short and is in the realm of brute
+	forcing within a short amount of time for most medium to large
+	companies. (Fortunately, this could be addressed in part by changing
+	the EncryptionAlgorithm property from "PBEWithMD5AndDES"
+	to either "PBEWithHmacSHA1AndDESede" [if you are using JDK
+	1.5 or later] or to "PBEWithMD5AndTripleDES" [if you are
+	still using JDK 1.4]. Unfortunately, because of the implementation
+	of <CODE>JavaEncryptor</CODE>, only "password based encryption"
+	algorithms can be used in ESAPI 1.4.)</P>
+	<LI><P>The symmetric encryption does not provide for any means of
+	confirming message authenticity. Cryptographers consider ensuring
+	message authenticity an important feature of modern cryptosystems.</P>
+</OL>
+<H3>Symmetric Encryption in ESAPI 2.0rc1 and 2.0rc2</H3>
+<P STYLE="margin-bottom: 0in">By default, release candidates ESAPI
+2.0-rc1 and 2.0-rc2 used the 256-bit AES cipher with the cipher mode
+Electronic Code Book (ECB) for encryption with <CODE>Encryptor</CODE>
+via the ESAPI reference implementation, <CODE>JavaEncryptor</CODE>.
+ECB cipher mode is the simplest cipher mode to use, but it is also
+cryptographically very weak. If more than one block of plaintext is
+encrypted with the same key, those identical blocks of ciphertext
+always encrypt to the same ciphertext block, thus revealing patterns
+in the plaintext input. For example, these images from Wikipedia's
+<A HREF="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation">Block
+cipher modes of operation</A> illustrate this point well: 
+</P>
+<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0>
+	<TR>
+		<TD>
+			<P ALIGN=CENTER><FONT COLOR="#000080"><A HREF="http://en.wikipedia.org/wiki/File:Tux.jpg"><FONT COLOR="#000080"><IMG SRC="http://upload.wikimedia.org/wikipedia/commons/5/56/Tux.jpg" NAME="graphics1" ALT="Original Tux image" ALIGN=BOTTOM WIDTH=196 HEIGHT=216 BORDER=1></FONT></A></FONT></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><FONT COLOR="#000080"><A HREF="http://en.wikipedia.org/wiki/File:Tux_ecb.jpg"><FONT COLOR="#000080"><IMG SRC="http://upload.wikimedia.org/wikipedia/commons/f/f0/Tux_ecb.jpg" NAME="graphics2" ALT="Tux image encrypted using ECB mode" ALIGN=BOTTOM WIDTH=196 HEIGHT=216 BORDER=1></FONT></A></FONT></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><FONT COLOR="#000080"><A HREF="http://en.wikipedia.org/wiki/File:Tux_secure.jpg"><FONT COLOR="#000080"><IMG SRC="http://upload.wikimedia.org/wikipedia/commons/a/a0/Tux_secure.jpg" NAME="graphics3" ALT="Tux image encrypted with other modessuch as CBC" ALIGN=BOTTOM WIDTH=196 HEIGHT=216 BORDER=1></FONT></A></FONT></P>
+		</TD>
+	</TR>
+	<TR>
+		<TD>
+			<P ALIGN=CENTER><I>Original Tux image</I></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><I>Image encrypted using ECB mode</I></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><I>Image encrypted using modes other than ECB</I></P>
+		</TD>
+	</TR>
+</TABLE>
+<P>Ciphertext encrypted with ECB cipher mode are also subject to
+"block replay attacks". See Bruce Schneier's <A HREF="http://www.google.com/url?sa=t&source=web&ct=res&cd=2&url=http%3A%2F%2Fbooks.google.com%2Fbooks%3Fid%3DA6ZO2D6ayNwC%26pg%3DPT216%26lpg%3DPT216%26dq%3Decb%2B%2522block%2Breplay%2522%26source%3Dbl%26ots%3DiEbAWQpu0e%26sig%3D8xiUva4XKaAOfPJEPsULPAJPk88%26hl%3Den%26ei%3Da6yISoLQPJOuMI-Z_OkE%26sa%3DX%26oi%3Dbook_result%26ct%3Dresult%26resnum%3D2&ei=a6yISoLQPJOuMI-Z_OkE&rct=j&q=ecb+%22block+replay%22&am [...]
+Cryptography: protocols, algorithms, and source code</I> </A>for
+details. 
+</P>
+<P>In both ESAPI 2.0-rc1 and 2.0-rc2, one can choose other block
+ciphers (e.g. Blowfish) or other key sizes (e.g., 512-bit AES), but
+encryption in ESAPI 2.0-rc1 and 2.0-rc2 <I>only</I> support ECB
+cipher mode with no padding; it cannot be used to decrypt data
+encrypted with anything other than an algorithm using ECB cipher mode
+and no padding. Most encryption (outside of ESAPI) uses some other
+cipher mode (e.g., Cipher Block Chaining (CBC) mode) and some sort of
+padding scheme, such as PKCS#5 padding, meaning that they are many
+situations where ESAPI encryption would not be suitable.</P>
+<H3>Problems with Symmetric Encryption in ESAPI 2.0-rc1 and 2.0-rc2</H3>
+<P>The problems with symmetric encryption in ESAPI 2.0-rc1 and 2.0rc2
+are many. Here are some of the main issues:</P>
+<OL>
+	<LI VALUE=1><P>The implementation only supported a single encryption
+	key. While this may be sufficient for some uses, clearly there are
+	cases where multiple recipients are involved when you would like to
+	use unique encryption keys for each recipient.</P>
+	<LI><P>ESAPI 2.0-rc1 and 2.0-rc2 came with a default settings for
+	Encryptor.MasterKey and Encryptor.MasterSalt, thus not requiring
+	that these be changed. How many of you forgot to change these?</P>
+	<LI><P>Because the reference implementation in <CODE>JavaEncryptor</CODE>
+	allows no means to specify an Initialization Vector (IV), only the
+	weak ECB cipher mode can be used.</P>
+	<LI><P>The symmetric encryption does not provide for any means of
+	confirming message authenticity. Cryptographers consider ensuring
+	message authenticity an important feature of modern cryptosystems.</P>
+</OL>
+<H2>The Encryption Changes in ESAPI 2.0-rc3 and Later</H2>
+<P>Briefly speaking, the changes being implemented for ESAPI Java 2.0
+are: 
+</P>
+<OL>
+	<LI><P STYLE="margin-bottom: 0in">Starting in ESAPI Java 2.0-rc3,
+	the <I>default</I> cipher transformation is changing to
+	"AES-128/CBC/PKCS5Padding" (i.e., 128-bit AES in CBC
+	cipher mode and PKCS#5 padding) with a random IV. The main reason
+	that 128-bit AES was chosen over 256-bit AES for the default is that
+	128-bit AES is slightly faster than 256-bit AES, and it does not
+	require you downloading the Unlimited Strength Jurisdiction Policy
+	files from Sun Microsystems. For those believing that 128-bit AES is
+	not sufficiently strong--bruting forcing it (except perhaps with a
+	reasonable quantum computer) is still not feasible--and certain key
+	related attacks (see Biryukov, Dunkelman, Keller, Khovratovich, and
+	Shamir's <A HREF="http://eprint.iacr.org/2009/374">Cryptology ePrint
+	Archive: Report 2009/374 -- Key Recovery Attacks of Practical
+	Complexity on AES Variants With Up To 10 Rounds </A>for further
+	details) show that longer key sizes in AES may not always be the
+	best.</P>
+	<LI><P STYLE="margin-bottom: 0in">Because cipher modes other than
+	ECB require an Initialization Vector (IV), a mechanism to handle
+	both fixed and random IVs has been added. The use of the “fixed”
+	IV should only be considered if there is a need to be compatible
+	with some legacy software or third party software package. Those
+	cases should be rare.</P>
+	<LI><P STYLE="margin-bottom: 0in">The original attempt to preserve
+	backward compatibility with ESAPI Java 1.4 (via
+	<CODE>org.owasp.esapi.reference.crypto.LegacyJavaEncryptor</CODE>)
+	has been removed. This was put to a vote on Jan 31, 2010 before both
+	the ESAPI Users and ESAPI Developers mailing lists and the lack of
+	response was deafening. There literally was but a single response
+	and that was to kill off <CODE>LegacyJavaEncryptor</CODE><CODE><FONT FACE="Thorndale AMT, serif">.</FONT></CODE>
+	(By this time, the two symmetric encryption interfaces in <CODE>Encryptor</CODE>
+	had already been deprecated.) 
+	</P>
+	<LI><P>The byte-encoding has been changed from native byte encoding
+	to UTF-8 byte-encoding throughout ESAPI 2.0 and not just for
+	encryption. This was needed so that one could (say) encrypt on a
+	Sparc running Solaris and then move the encrypted data to an Intel
+	host running Windows and still be able to decrypt the data. Without
+	this change to use uniform encoding throughout, this could not be
+	guaranteed.</P>
+</OL>
+<H2>The Good, the Bad, and the Ugly</H2>
+<P>Or put another way, there are always trade-offs to be made... 
+</P>
+<H3>The Good</H3>
+<P>We get improved security by encouraging the use of stronger cipher
+modes (and ESAPI is all about "enterprise security",
+right?). Furthermore, it is possible to choose a preferred JCE
+provider for ESAPI and ESAPI will attempt to use that provider. For
+example, if you wish to use Bouncy Castle rather than the SunJCE, you
+can do so. One place where this might be a significant advantage is
+if you need to use a FIPS 140-2 compliant JCE implementation. Whereas
+in production environments, you ultimately will want to ensure this
+by editing your <FONT FACE="DejaVu Sans Mono, sans-serif">$JAVA_HOME/jre/lib/security/java.security</FONT>
+file, this is often inconvenient for developers who simply wish to
+test because this file often requires super-user access to alter. In
+such cases, setting the new ESAPI property
+<FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.PreferredJCEProvider</FONT>
+to the fully qualified class name of your FIPS 140-2 JCE provider in
+your <FONT FACE="Thorndale AMT, serif"><B>ESAPI.properties</B></FONT>
+file and you can test without any other changes to your code or you
+environment.</P>
+<P>We also get message authenticity as long as we don't use any of
+the deprecated encryption methods or intentionally disable it. (The
+only <I>sane</I> reason for intentionally disabling the MAC
+calculation is for FIPS 140-2 compliance. This MAC calculation
+requires deriving two new <FONT FACE="DejaVu Sans Mono, sans-serif">SecretKey</FONT>s
+from the original key—one to use for message authenticity and a
+second one to use for message confidentiality. While we believe that
+this mechanism is secure [it was originally suggested by professional
+cryptographers], this key derivation would not be using FIPS 140-2
+approved code. Therefore if you require FIPS 140-2 compliance, you
+should set “<FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.CipherText.useMAC=false</FONT>”
+in your <FONT FACE="Thorndale AMT, serif"><B>ESAPI.properties</B></FONT>
+file.)</P>
+<H3>The Bad</H3>
+<P>With cipher modes that require an IV, the same IV must be used
+both to encrypt and decrypt. While it is not required that the IV be
+kept secret from adversaries, there are some attacks that are
+possible if the adversary is permitted to alter the IV at will and
+observe the results of the ensuing decryption attempt. 
+</P>
+<P>So that leaves two choices for the IV: 
+</P>
+<UL>
+	<LI><P STYLE="margin-bottom: 0in">Using a <I><B>fixed IV</B></I>:
+	The IV can be agreed upon out-of-band by the parties exchanging
+	encrypted messages. In this way, the encrypted data that is stored
+	and/or transmitted can be limited to only the <I>raw</I> ciphertext.
+	(<U><B>WARNING</B></U><SPAN STYLE="text-decoration: none"><SPAN STYLE="font-weight: normal">:
+	This case should be reserved for situations requiring compatibility
+	with legacy or third party software packages.)</SPAN></SPAN></P>
+	<LI><P>Using a <I><B>random IV</B></I>: Most cryptographers prefer
+	to use a random IV, typically a different one with each message to
+	be encrypted. However, doing so on encrypted data that will be
+	persisted (e.g., to a database) or transmitted to the recipient this
+	random IV must be stored / made known. Therefore, the raw ciphertext
+	can no longer suffice; whatever random IV that was chosen must be
+	communicated. 
+	</P>
+</UL>
+<P>Likewise, the use of padding is going to add some overhead to the
+length of the ciphertext.</P>
+<P STYLE="margin-bottom: 0in">For example, a short plaintext of 0-15
+bytes, this overhead can be around 200% of the total plaintext size.
+This overhead diminishes (as a percent of the total plaintext size)
+as the plaintext size increases. Here is a table that illustrates
+this:</P>
+<A NAME="encryption-overhead">
+<P STYLE="margin-bottom: 0in"><BR>
+</P>
+</A>
+<TABLE WIDTH=100% BORDER=1 BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0>
+	<COL WIDTH=64*>
+	<COL WIDTH=64*>
+	<COL WIDTH=64*>
+	<COL WIDTH=64*>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Original input length of plaintext <BR>(#
+			bytes)</B></P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Base64-encoded length of ciphertext (with IV
+			and padding) encrypted with 128-bit AES in CBC mode, but no MAC<SUP>1</SUP></B></P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Length of raw portable serialized CipherText
+			byte array, containing IV, padding, and MAC, and encrypted with
+			128-bit AES in CBC mode</B></P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Length of raw portable serialized CipherText,
+			containing IV, padding, and MAC, and encrypted with 128-bit AES in
+			CBC mode as a Base64-encoded string</B></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>0-15</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>44</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>102</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="137" SDNUM="1033;">
+			<P>137</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>16-31</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>64</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>118</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="162" SDNUM="1033;">
+			<P>162</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>32-47</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>88</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>134</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="182" SDNUM="1033;">
+			<P>182</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>48-63</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>108</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>150</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="202" SDNUM="1033;">
+			<P>202</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>64-79</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>128</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>166</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="222" SDNUM="1033;">
+			<P>222</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>… etc.  Each additional 16 bytes of plaintext...</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>...adds ~20 additional bytes</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>...adds 16 additional bytes</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P>...adds 20 additional bytes</P>
+		</TD>
+	</TR>
+</TABLE>
+<P>__________________________________</P>
+<P>1. That is, the <FONT FACE="DejaVu Sans Mono, sans-serif">String</FONT>
+that is returned by the deprecated <FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.decrypt(String)</FONT>
+method.</P>
+<P>As you can see, since the size of the IV and the amount of padding
+bytes are fixed at a maximum, this overhead goes down as the length
+of the plaintext message increases. The IV is always a fixed length
+for a given cipher; it is always the same as the cipher block size.
+The padding can vary, but for PKCS5 padding, the padding will be
+between 1 to the cipher block size (in bits) / 8 bytes. For AES, the
+cipher block size is 128-bits, but more typically, a cipher's block
+size is 64-bits so the padding would be between 1 to 16 bytes for AES
+and 1 to 8 bytes for a 64-bit block size cipher and the IV would be
+IV would be 16 bytes for AES and 8 bytes for most other ciphers. 
+</P>
+<H3>The Ugly</H3>
+<P>Well, so far, this "bad" news may be bad for you but
+good for your database and/or storage vendor, but you probably can
+live with it, especially if you are willing to do a little bit of
+recoding of your database table sizes.</P>
+<P>But wait Skippy, don't go running off just quite yet. As Robert
+Heinlein wrote in his 1966 novel <I>The Moon is a Harsh Mistress</I>
+"There ain't no such thing as a free lunch". (Some of us
+more hardened cynics know it more commonly as <I>TANSTAAFL</I>.) 
+</P>
+<P>As mentioned earlier, backward compatibility with ESAPI 1.4
+(originally planned via <CODE>LegacyJavaEncryptor</CODE>) has been
+removed because of apparent lack of interest. What this means is if
+you are one of the unlucky stuckeys who are using ESAPI 1.4 symmetric
+encryption to encrypt data and you have persisted this data (and this
+would <I>include </I><SPAN STYLE="font-style: normal">the use of the
+</SPAN><FONT FACE="DejaVu Sans Mono, sans-serif"><SPAN STYLE="font-style: normal">org.owasp.esapi.EncryptedProperties</SPAN></FONT>
+<SPAN STYLE="font-style: normal">class in ESAPI 1.4)</SPAN>, you will
+first have to <I>decrypt </I><SPAN STYLE="font-style: normal">this
+encrypted data using ESAPI 1.4 before using encryption with ESAPI
+2.0. As they say, “we are sorry for the inconvenience”, but there
+was an opportunity to make your voice heard so we refuse to take </SPAN><I>all
+</I><SPAN STYLE="font-style: normal">the blame.</SPAN></P>
+<P>In addition, all this backward compatibility and flexibility comes
+at the extra cost of complexity, and not <I>merely</I> additional
+complexity in the reference implementation. There is also some
+additional complexity in the ESAPI <CODE>Encryptor</CODE> interfaces
+as well.</P>
+<P>In particular, there new classes for plaintext (<FONT FACE="DejaVu Sans Mono, sans-serif">PlainText</FONT>)
+and ciphertext (<CODE>CipherText)</CODE> class to coalesce all this
+complexity of handling the ciphertext result from encryption
+operations. And then there are new encryption and decryption methods
+for the <CODE>Encryptor</CODE> interface. Specifically, the encrypt
+and decrypt methods have been generalized as: 
+</P>
+<PRE STYLE="margin-left: 0.49in"><FONT COLOR="#000000"><FONT FACE="Monospace">CipherText encrypt(SecretKey key, PlainText plaintext)</FONT></FONT>
+<FONT COLOR="#000000">        <FONT FACE="Monospace">throws EncryptionException;</FONT></FONT></PRE><P STYLE="margin-bottom: 0in">
+and 
+</P>
+<PRE STYLE="margin-left: 0.49in"><FONT COLOR="#000000"><FONT FACE="Monospace">PlainText decrypt(SecretKey key, CipherText ciphertext)</FONT></FONT>
+<FONT COLOR="#000000">        <FONT FACE="Monospace">throws EncryptionException</FONT></FONT></PRE><P>
+(There are also two corresponding encrypt / decrypt methods that omit
+the initial <FONT FACE="DejaVu Sans Mono, sans-serif">SecretKey</FONT>
+argument and instead use the <FONT FACE="DejaVu Sans Mono, sans-serif">SecretKey</FONT>
+based on <FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.MasterKey</FONT>.)</P>
+<P>The two existing interfaces from ESAPI 1.4 and earlier:</P>
+<PRE STYLE="margin-left: 0.49in; margin-bottom: 0.2in">String encrypt(String plaintext) throws EncryptionException</PRE><P STYLE="margin-bottom: 0in">
+and 
+</P>
+<PRE STYLE="margin-left: 0.49in; margin-bottom: 0.2in">String decrypt(String ciphertext) throws EncryptionException</PRE><P>
+are still supported but have been <I>deprecated</I>, mainly because
+they do not support integrity / authenticity checks which most
+cryptographers consider essential. Also these older String-based
+encrypt() / decrypt() interfaces have slightly different semantics
+than do their ESAPI 1.4 counterparts in that these methods default to
+CBC cipher mode, PKCS5Padding, and a random IV. This means that at a
+minimum, the IV must be included as the resulting base64-encoded
+“ciphertext”.</P>
+<P>However, this difference in cipher mode means that if you have
+data that was previously encrypted using ESAPI 1.4 or earlier, you
+will not be able to decrypt it using the ESAPI 2.0</P>
+<PRE STYLE="margin-left: 0.49in; margin-bottom: 0.2in">String decrypt(String ciphertext) throws EncryptionException</PRE><P>
+method. If you require such compatibility, you may contact the
+author, <a href="mailto:kevin.w.wall at gmail.com">Kevin W. Wall</a>,
+for assistance.
+</P>
+<P>Note that while the new String-based encrypt() / decrypt() methods
+use the stronger CBC cipher mode with a random IV, they still do not
+ensure authenticity and integrity so the more general encrypt() /
+decrypt() methods should be preferred over the String-based ones.
+(See below for examples of how to use these new methods.)</P>
+<H2>Further Details</H2>
+<P>Further details are provided in the <CODE>Encryptor</CODE> Javadoc
+and the “Symmetric Encryption” section of the ESAPI User Guide.</P>
+</BODY>
+</HTML>
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-release-notes.doc.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-release-notes.doc.svn-base
new file mode 100644
index 0000000..9853e0d
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-core-2.0-release-notes.doc.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-release-notes.pdf.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-release-notes.pdf.svn-base
new file mode 100644
index 0000000..351f055
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-core-2.0-release-notes.pdf.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-core-2.0-symmetric-crypto-user-guide.html.svn-base b/documentation/.svn/text-base/esapi4java-core-2.0-symmetric-crypto-user-guide.html.svn-base
new file mode 100644
index 0000000..2091cad
--- /dev/null
+++ b/documentation/.svn/text-base/esapi4java-core-2.0-symmetric-crypto-user-guide.html.svn-base
@@ -0,0 +1,796 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+	<TITLE>ESAPI 2.0 Symmetric Encryption User Guide</TITLE>
+	<META NAME="GENERATOR" CONTENT="OpenOffice.org 3.0  (Linux)">
+	<META NAME="CREATED" CONTENT="20100214;0">
+	<META NAME="CHANGEDBY" CONTENT="Kevin W. Wall">
+	<META NAME="CHANGED" CONTENT="20100304;00400700">
+</HEAD>
+<BODY LANG="en-US" DIR="LTR">
+<TABLE BORDER="0" BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0 STYLE="page-break-before: auto; page-break-after: auto; page-break-inside: auto">
+<TR>
+<TD>
+<OBJECT TYPE="audio/x-mpeg" data="http://www.catonmat.net/download/crypt-o.mp3"
+WIDTH="500" HEIGHT="64" AUTOPLAY="false">
+    <PARAM NAME="src" VALUE="http://www.catonmat.net/download/crypt-o.mp3" />
+    <PARAM NAME="controller" VALUE="true" />
+    <PARAM NAME="autoplay" VALUE="false" />
+    <PARAM NAME="autostart" VALUE="0" />
+</OBJECT>
+</TD>
+<TD>
+<FONT COLOR="#00a444" SIZE="+2">
+<I>Crypto song. Take a listen and enjoy! Harry Belafonte never sounded this good. ;-)</I>
+</FONT>
+</TD>
+</TABLE>
+<H1 ALIGN=CENTER>ESAPI 2.0 Symmetric Encryption User Guide</H1>
+<H2>ESAPI.properties Properties Relevant to Symmetric Encryption</H2>
+<P>Those properties that are new since ESAPI 2.0-rc2 are shown in
+<FONT COLOR="#ff0000">red</FONT>. Values shown in <FONT COLOR="#0000ff">blue</FONT>
+are ones that you would replace.</P>
+<TABLE BORDER=1 BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0 STYLE="page-break-before: auto; page-break-after: auto; page-break-inside: auto">
+	<COL WIDTH=249>
+	<COL WIDTH=202>
+	<COL WIDTH=226>
+	<TR VALIGN=TOP>
+		<TH WIDTH=249>
+			<P>Property Name</P>
+		</TH>
+		<TH WIDTH=202>
+			<P>Default Value</P>
+		</TH>
+		<TH WIDTH=226>
+			<P>Comment</P>
+		</TH>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT SIZE=2>ESAPI.Encryptor</FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT SIZE=2>org.owasp.esapi.reference.crypto.JavaEncryptor</FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>The class implementing the Encryptor interface and
+			returned by ESAPI.encryptor().</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT SIZE=2>Encryptor.MasterKey</FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<P><FONT COLOR="#0000ff"><FONT SIZE=2><initially unset></FONT></FONT></P>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>The base64-encoded SecretKey. The key must be
+			appropriate to the specified key size and cipher algorithm.</FONT></P>
+			<P><FONT SIZE=2>Set as per the instructions in the ESAPI
+			Installation Guide.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT SIZE=2>Encryptor.MasterSalt</FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<P><FONT COLOR="#0000ff"><FONT SIZE=2><initially unset></FONT></FONT></P>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>A base64-encoded random “salt”. This should be
+			at least 20-bytes. It is used to generate a random (but
+			consistent) public/private key pair used in asymmetric encryption
+			and digital signatures.</FONT></P>
+			<P><FONT SIZE=2>Set as per the instructions in the ESAPI
+			Installation Guide.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><STRIKE><FONT SIZE=2>Encryptor.EncryptionAlgorithm</FONT></STRIKE></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT SIZE=2>AES</FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>A deprecated property, superseded by
+			Encryptor.CipherTransformation.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.CipherTransformation</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>AES/CBC/PKCS5Padding</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Specifies the cipher transformation to use for
+			symmetric encryption. The format is
+			cipherAlgorithm/cipherMode/paddingScheme.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.EncryptionKeyLength</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>128</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Key size, in bits. Required for cipher algorithms
+			that support multiple key sizes.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.ChooseIVMethod</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>random</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Legal values are “random” or “fixed”.
+			Random is recommended. Set to “fixed” only if required for
+			compatibility with legacy or third party software. If set to
+			“fixed”, then the property Encryptor.fixedIV must also be
+			set to hex-encoded specific IV that you need to use.
+            </FONT></P><P><FONT SIZE=2>
+            <B>CAUTION:</B> While it is not required that the IV be kept
+            secret, encryption relying on fixed IVs can lead to a known
+            plaintext attack called a "Key Collision Attack". While this
+            attack is probably not practical (for those with modest
+            resources) for ciphers with 128-bit key size, this attack
+            makes it possible to capture the ciphertexts from only
+            2<sup>(N/2)</sup> known plaintexts to discover the
+            encryption key. Loughran and Dowling explain a Java
+            implementation of Eli Biham's key collision attack on DES in
+            their easy to understand paper,
+            <a href="http://homepage.eircom.net/~johnloughran/projects/computer/camDesAttackv4.pdf">
+            A Java Implemented Key Collision Attack on the
+            Data Encryption Standard (DES)</a>. Since attacks only get
+            better and the cost of storage is dropping rapidly, you are
+            urged to avoid using "fixed" IVs except when required for
+            backward compatibility. In particular, should never use
+            fixed IVs just to avoid the storage cost of storing a random
+            IV.
+            </FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.fixedIV</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#0000ff"><FONT SIZE=2>0x000102030405060708090a0b0c0d0e0f</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>A hex-encoded value to use as a fixed IV. Only
+			used if the property Encryptor.fixedIV is set to “fixed”. Intended
+            <I>only</I> for compatibility with legacy code. See the above
+            related above caution for <code>Encryptor.ChooseIVMethod</code>.
+            </FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.CipherText.useMAC</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>true</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Whether or not <code>CipherText</code>
+            should use a message authentication code (MAC) with
+            it. This prevents an adversary from altering the IV
+            as well as allowing a more fool-proof way of
+			determining the decryption failed because of an incorrect key
+			being supplied. This refers to the "separate" MAC
+			calculated and stored in <code>CipherText</code>, not
+            part of any MAC that is calculated as a result
+            of a "combined mode" cipher mode.</FONT></P>
+			<P><FONT SIZE=2>Note: If the cipher mode used is one specified in
+			the comma-separated list of cipher modes given in the property
+			<b>Encryptor.cipher_modes.combined_modes</b>, then a separate MAC
+            is <i>not</i> calculated for <code>CipherText</code> regardless of
+            the setting of this property. (Doing so would be
+            superfluous.)</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.PreferredJCEProvider</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2><i><empty string></i></FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Specifies the <i>preferred</i> JCE provider, that
+            is the JCE provider that is first looked at for JCE algorithms.
+            The <code>Encryptor</code> reference implementation,
+            <code>JavaEncryptor</code>, attempts to
+			load this JCE provider at position the first position when the
+            <code>JavaEncryptor</code> class is first loaded.
+            <br><br>
+            The value may either be a provider name (e.g., “BC” for
+            Bouncy Castle) or the fully qualified class name implementing
+            <code>java.security.Provider</code> for
+			the desired JCE provider. If left set to the empty string (the
+            default) or unset, the effect is to not change the preferred JCE
+            provider so that your application ends up using whatever your Java
+            VM is already using, which is generally determined by the settings
+            in your <code>$JAVA_HOME/jre/lib/security/java.security</code>
+            file.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.cipher_modes.additional_allowed</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>CBC</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Additional cipher modes allowed for ESAPI 2.0
+			symmetric encryption. These cipher modes are in <I>addition</I> to
+			those specified by the property
+			<b>Encryptor.cipher_modes.combined_modes</b>.</FONT></P>
+			<P><FONT SIZE=2>Note: We will add support for streaming modes like
+			CFB & OFB once we add support for 'specified' to the property
+			<b>Encryptor.ChooseIVMethod</b> (probably in ESAPI 2.1).</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.cipher_modes.combined_modes</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>GCM,CCM,IAPM,EAX,OCB,CWC</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Comma-separated list of cipher modes that provide
+			<B><I>both</I></B> confidentiality <B><I>and</I></B>
+            message authenticity. (NIST refers to such cipher
+            modes as "combined modes" so that's what we
+			shall call them.) If any of these cipher modes are used then no
+			MAC is calculated and stored in the <code>CipherText</code>
+            upon encryption.  Likewise, if one of these cipher
+            modes is used with decryption, no attempt will be
+            made to validate the MAC contained in the <code>CipherText</code>
+            object regardless of whether it contains one or not.
+			Since the expectation is that these cipher modes support support
+			message authenticity already, injecting a MAC in the
+            <code>CipherText</code> object would be at best redundant.</FONT>
+			</P>
+			<P><FONT SIZE=2>Note that as of JDK 1.5, the SunJCE provider does
+			not support <I>any</I> of these cipher modes. Of these listed,
+			only GCM and CCM are currently NIST approved.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.PlainText.overwrite</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>TRUE</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Whether or not the plaintext bytes for the
+			PlainText object may be overwritten with “*” characters and
+			then marked eligible for garbage collection. If not set, this is
+			still treated as 'true'. If this is set to 'true', you will not be
+			able to use any PlainText object after you have used it with one
+			of the Encryptor encrypt() methods.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.KDF.PDF</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>HmacSHA256</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>
+            This is the Pseudo Random Function (PRF) that ESAPI's Key
+            Derivation Function (KDF) normally uses. NSA wanted us to
+            support something stronger than HmacSHA1 (even though that
+            is considered fine for now--unless perhaps you are guarding
+            nuclear launch codes--and if so, your Java license probably
+            prohibits that ;-).
+			<BR /><BR />
+            (Note this is *only* the PRF used for ESAPI's KDF and *not*
+            what is used for ESAPI's MAC. Currently, HMacSHA1 is always
+            used for the MAC.)
+			<BR /><BR />
+            Currently supported choices for JDK 1.5 and 1.6 are:
+            HMacSHA1 (160 bits), HMacSHA256 (256 bits), HMacSHA384
+            (384 bits), and HMacSHA512 (512 bits).
+			<BR /><BR />
+            Note that HMacMD5 is *not* supported for the PRF
+            used by the KDF even though these JDKs support it.
+            ESAPI 2.0 release candidates prior to 2.0_rc11
+            ALWAYS used HMacSHA1. It is somewhat faster than
+            the SHA2 HMAC variations, but not significantly so.
+            <BR /><BR />
+            The only legitimate reason to tweak this would be to change it to
+            one of the new HMACs based on the future SHA-3 winner once it is
+            announced by NIST and supported in ESAPI and most JDKs. Until then,
+            don't meddle.
+			</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#000000"><FONT SIZE=2>Encryptor.CharacterEncoding</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#000000"><FONT SIZE=2>UTF-8</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>The default encoding used for certain aspects such
+			as signing and sealing.</FONT></P>
+		</TD>
+	</TR>
+</TABLE>
+<H2>How the Old (Deprecated) Methods Were Used</H2>
+<P>To encrypt / decrypt using the String-based, deprecated methods
+carried over from ESAPI 1.4, code similar to the following would be
+used. 
+</P>
+<PRE>    String myplaintext = "My plaintext";
+    try {
+        String ciphertext = ESAPI.encryptor().encrypt(myplaintext);
+        String decrypted  = ESAPI.encryptor().decrypt(ciphertext);
+        assert decrypted.equals(myplaintext);
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate
+    }</PRE><P>
+This code will still work, however if you are using the standard
+(default) reference for <CODE>ESAPI.Encryptor</CODE>, which is
+<CODE>org.owasp.esapi.reference.crypto.JavaEncryptor</CODE>, the
+cipher transformation used with be that specified by the property
+<b>Encryptor.CipherTransformation</b> with a key size (when the
+algorithm supports a variable key size) of that specified by
+<b>Encryptor.EncryptionKeyLength</b> and the IV type specified
+by <b>Encryptor.ChooseIVMethod</b>. What is not provided by
+these methods (and why they are deprecated) is that they provide no
+mechanism to ensure message authenticity unless they are used with a
+so-called “combined” cipher mode such as CCM or GCM. <SPAN STYLE="font-style: normal">(Note
+that as of JDK 1.6, the default JCE provider, “SunJCE”, does not
+support any combined cipher modes.)</SPAN></P>
+<H2>Requirements for using ESAPI Symmetric Encryption</H2>
+<P>
+The parties participating in using ESAPI's symmetric encryption
+capabilities must agree on the following:
+<ol>
+<li>
+All parties must share the same encryption key(s). If multiple keys are
+used, each party must know which key to use for encryption / decryption.
+Parties must ensure that exchanging, storing, and all management
+of these encryption keys is done securely.  How this is done is
+presently outside the scope of ESAPI encryptions, but developers may
+find the advice in
+<a href="http://www.owasp.org/index.php/Cryptographic_Storage_Cheat_Sheet">
+OWASP's Cryptographic Storage Cheat Sheet</a> helpful in this regard.
+</li>
+<li>
+If parties are only exchanging raw ciphertext (plus IV, when appropriate),
+then they must also agree on the cipher transformation to use.
+</li>
+<li>
+If the involved parties participating in encryption are <i>not</i> using a
+"combined" cipher mode that provides both confidentiality and authenticity
+(for example, something like GCM or CCM), then they <i>must</i> whether or
+not there will be an additional MAC sent allow with the raw ciphertext and
+other cipher spec information in order to provide evidence of authenticity /
+data integrity. (See the property <b>Encryptor.CipherText.useMAC</b>
+for further details.) Failure to agree on this may allow an adversary to
+carry out certain chosen ciphertext attacks against their encrypted data
+resulting in (possibly complete) leakage of the corresponding plaintext.
+</li>
+</ol>
+<H2>Encrypting / Decrypting with the New Methods -- The Simple Usage</H2>
+<P>Using the new encryption / decryption methods is somewhat more
+complicated, but this is in part because they are more flexible and
+that flexibility means that more information needs to be communicated
+as to the details of the encryption. 
+</P>
+<P>A code snippet using the new methods that use the master
+encryption key would look something like this: 
+</P>
+<PRE>    String myplaintext = "My plaintext";
+    try {
+        CipherText ciphertext =
+            ESAPI.encryptor().encrypt( new PlainText(myplaintext) );
+        PlainText recoveredPlaintext = ESAPI.encryptor().decrypt(ciphertext) );
+        assert myplaintext.equals( recoveredPlaintext.toString() );
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }</PRE><P>
+Yes, this is a bit more complicated, but it will 1) work across
+different hardware platforms and operating systems whereas the older
+methods may not, and 2) it provides for <I>authenticity</I> <I><B>and</B></I>
+<I>confidentiality</I> of the ciphertext regardless of which cipher
+mode is chosen.</P>
+<P>Also, these new methods allow a general byte array to be
+encrypted, not just a Java String. If one needed to encrypt a byte
+array with the old deprecated method, one would first have to use 
+</P>
+<PRE>    byte[] plaintextByteArray = { /* byte array to be encrypted */ };
+    String plaintext = new String(plaintextByteArray, "UTF-8");</PRE><P>
+all the while catching the required <CODE>UnsupportedEncodingException</CODE>.
+For example, to handle this in ESAPI 1.4, one would have to write
+something like:</P>
+<PRE>
+    try {
+        byte[] plaintextByteArray = { /* byte array to be encrypted */ };
+        String myplaintext = new String(plaintextByteArray, "UTF-8");
+        String ciphertext = ESAPI.encryptor().encrypt(myplaintext);
+        String decrypted  = ESAPI.encryptor().decrypt(ciphertext);
+        byte[] recoveredBytes = decrypted.getBytes(“UFT-8”);
+        assert java.util.Arrays.equals( plaintextByteArray, recoveredBytes );
+    } catch( UnsupportedEncodingException ex) {
+        // Should not happen but need to catch and deal with it anyhow.
+        // Log error then return error designation however appropriate.
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }
+</PRE><P>
+However, dealing with this in ESAPI 2.0 is not any more cumbersome
+than dealing with Strings:</P>
+<PRE>
+    try {
+        byte[] plaintextByteArray = { /* byte array to be encrypted */ };
+        CipherText ciphertext =
+            ESAPI.encryptor().encrypt( new PlainText(plaintextByteArray) );
+        PlainText recoveredPlaintext = ESAPI.encryptor().decrypt(ciphertext) );
+
+        assert java.util.Arrays.equals( plaintextByteArray,
+                                        recoveredPlaintext.asBytes() );
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }
+</PRE><P>
+Ideally when you are encrypting sensitive data you do not want the
+plaintext sensitive data to be left lying around after it is
+encrypted. Instead, you should overwrite them after their value as
+been used. However, when you are using immutable Strings, this is not
+possible using native Java methods. But if you are able to pass in
+byte arrays that are passed directly to <CODE>PlainText</CODE>
+objects (as shown above), the <I>default</I> is to overwrite this
+after they are encrypted.
+<!-- Need to verify this. It was like this, but think I may have changed
+it to return a copy rather than a reference based on FindBugs report. -->
+<SPAN STYLE="background: #ffff00">(Note: Verify!) If the default for
+</SPAN><CODE><SPAN STYLE="background: #ffff00">Encryptor.PlainText.overwrite</SPAN></CODE><SPAN STYLE="background: #ffff00">
+of </SPAN><CODE><B><SPAN STYLE="background: #ffff00">true</SPAN></B></CODE><SPAN STYLE="background: #ffff00">
+had been used, then the array </SPAN><CODE><SPAN STYLE="background: #ffff00">plaintextByteArray</SPAN></CODE><SPAN STYLE="background: #ffff00">
+would have been overwritten with ASCII “*” characters.</SPAN></P>
+<H2>Encrypting / Decrypting with the New Methods – Storing Encrypted Data</H2>
+<P><SPAN STYLE="background: transparent">If you use one of the new
+Encryptor encrypt() / decrypt() methods, how do you persist the
+<code>CipherText</code> object returned by the encrypt() methods and how do
+you restore it to pass to the decrypt() method?</SPAN></P>
+<P><SPAN STYLE="background: transparent">The following example code
+snippet will illustrate this. In the following example we will simply
+write out the serialized <code>CipherText</code> object to a local file, but
+obviously you could hex- or base64-encode the serialized byte array
+and store it in a database or sent it in a SOAP XML message to a web
+service, etc.</SPAN></P>
+<PRE>    public class PersistedEncryptedData
+    {
+        public static int persistEncryptedData(PlainText plaintext,
+                                                String filename)
+            throws EncryptionException, IOException
+        {
+            File serializedFile = new File(filename);
+            serializedFile.delete(); // Delete any old serialized file.
+
+            CipherText ct = ESAPI.encryptor().encrypt(plaintext);
+            byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+
+            FileOutputStream fos = new FileOutputStream(serializedFile);
+            fos.write(serializedCiphertext);
+            fos.close();
+            return serializedCiphertext.length;
+        }
+
+        public static PlainText restorePlaintext(String encryptedDataFilename)
+            throws EncryptionException, IOException
+        {
+            File serializedFile = new File(encryptedDataFilename);
+            FileInputStream fis = new FileInputStream(serializedFile);
+            int avail = fis.available();
+            byte[] bytes = new byte[avail];
+            fis.read(bytes, 0, avail);
+
+            CipherText restoredCipherText =
+                                CipherText.fromPortableSerializedBytes(bytes);
+            fis.close();
+            PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
+            return plaintext;
+        }
+    }
+  </PRE>
+<H2>Advanced Usage</H2>
+<H3>Encrypting / Decrypting with the New Methods</H3>
+<P>ESAPI 1.4 and earlier only allowed you to use the master key
+(<FONT FACE="DejaVu Sans Mono, sans-serif">MasterPassword</FONT> in
+ESAPI 1.4; <CODE>Encryptor.MasterKey</CODE> in ESAPI 2.0) to encrypt
+and decrypt with. But encryption with a single key seldom is
+sufficient. For instance, lets say that your application has a need
+to encrypt both bank account numbers and credit card numbers. The
+encrypted bank account numbers are to be sent to one recipient and
+the encrypted credit card numbers are to be sent to a different
+recipient. Obviously in such cases, you do not want to share the same
+key for both recipients. 
+</P>
+<P>In ESAPI 1.4 there was not much you can do, but in ESAPI 2.0 and
+later, there are new encryption / decryption methods that allow you
+to specify a specific <CODE>SecretKey</CODE>. There is also a static
+helper method in <CODE>CryptoHelper</CODE> to allow you to generate a
+<CODE>SecretKey</CODE> of a specific type. (Distribution of this key
+is out of scope for this particular example, but for the moment, we
+will assume that secret keys are first generated, and then
+distributed to the recipients out-of-band. On you could distribute
+them dynamically via asymmetric encryption assuming that you've
+previously exchanged public keys with the recipients.)</P>
+<P>The following illustrates how these new methods might be used. 
+</P>
+<P>First, we would generate some appropriate secret keys and
+distribute them securely (e.g., perhaps over SSL/TLS) or exchange
+them earlier out-of-band to the intended recipients. (E.g., one could
+put them on two separate thumb drives and use a trusted courier to
+distribute them to the recipients or one could use PGP-mail or S/MIME
+to securely email them, etc.) 
+</P>
+<PRE>    // Generate two random, 128-bit AES keys to be distributed out-of-band.
+    import javax.crypto.SecretKey;
+    import org.owasp.esapi.crypto.CryptoHelper;
+    import org.owasp.esapi.codecs.Hex;
+
+    public class MySecretKeys {
+        public void main(String[] args) {
+          try {
+            SecretKey bankAcctKey = CryptoHelper.generateSecretKey("AES", 128);
+            SecretKey credCardKey = CryptoHelper.generateSecretKey("AES", 128);
+
+            System.out.println("Bank account key: " +
+                Hex.encode( bankAcctKey.getEncoding(), true ) );
+            System.out.println("Credit card key: " +
+                Hex.encode( credCardKey.getEncoding(), true ) );
+          } catch(Exception ex) {
+            ex.printStackTrace(System.err);
+            System.exit(1);
+          }
+          System.exit(0);
+        }
+    }</PRE><P>
+Second, these keys would be printed out and stored somewhere secure
+by our application, perhaps using something like ESAPI's
+<CODE>EncryptedProperties</CODE> class, where they could later be
+retrieved and used. 
+</P>
+<P>In the following code, we assume that the <CODE>SecretKey</CODE>
+values have already been initialized elsewhere. 
+</P>
+<PRE>    SecretKey bankAcctKey = ...;        // These might be read from EncryptedProperties
+    SecretKey credCardKey = ...;        // or from a restricted database, etc.
+    ...
+    String bankAccountNumber = ...;     // Assume obtained elsewhere
+    String creditCardNumber = ...;      // Ditto
+    ...
+    try {
+        // Encrypt each with their appropriate secret key
+        CipherText encryptedBankAcct =
+            ESAPI.encryptor().encrypt( bankAcctKey, new PlainText(bankAccountNumber) );
+        CipherText encryptedCreditCard =
+            ESAPI.encryptor().encrypt( credCardKey, new PlainText(creditCardNumber) );
+        ...
+        // Decrypt using appropriate secret key
+        PlainText recoveredBankAcct = ESAPI.encryptor().decrypt( bankAcctKey, encryptedBankAcct ) );
+        assert bankAccountNumber.equals( recoveredBankAcct );
+        ... etc. ...
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }</PRE>
+<H3>Using ESAPI with Multiple Cipher Transformations</H3>
+For the most part, the architecture of ESAPI assumes that you will
+stick to using the defaults in the <b>ESAPI.properties</b> configuration
+file or implment your own classes--possibly by extending the ESAPI
+reference classes--and use these classes instead of the reference
+classes.
+<p>
+For most things, this works well. Most applications likely can
+standardize on a single cipher transformation such as AES/CBC/PKCS5Padding
+with a 128-bit AES key and use that 100% of the time. However, on
+occassion, an application may need to use two separate cipher
+transformations (or even two different cipher algorithms) to
+handle legacy applications or deal with multiple partners.
+</p><p>
+This section discusses how to do this without implementing your own
+classes or extending the ESAPI reference class, <code>JavaEncryptor</code>.
+Note that it is recognized that this approach is somewhat of a kludge.
+A simpler approach is planned for ESAPI 2.1, but the approach shown
+here is workable even though it's not pretty.
+</p><p>
+If you find yourself in need of encrypting with a different cipher
+transformation, the first thing that you should count on is not
+using the same encryption key for each. While in some cases this
+likely would work (e.g., you are only using a different cipher mode
+or you have 256-bit AES key for "Encryptor.MasterKey" but also have
+a need to do encryption with a 128-bit AES key), it is not guaranteed
+to do so. Instead, you should count on generating a separate encryption
+key and using the encrypt / decrypt methods taking an additional
+<code>SecretKey</code> parameter as show in the previous section.
+</p>
+<p>
+Rather than repeating all the details of how to do this in this
+user guide, we encourage you to investigate how this was done in
+the Junit testing for the <code>JavaEncryptor</code> class. Please
+look at the source code for the private method
+<code>runNewEncryptDecryptTestCase(String, int, byte[])</code> in
+the <code>EncryptorTest</code> JUnit test in the source code
+"src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java".
+This code calls:
+<pre>
+		ESAPI.securityConfiguration().setCipherTransformation(cipherXform);
+</pre>
+which sets ESAPI to use the specified cipher transformation,
+<code>cipherXform</code>. As a convenience (for later restoral),
+it returns the previous cipher transformation.
+<p>
+There are also a few non-obvious key size adjustments that are also
+going on with DES and DESede (aka, triple DES) keys that are made there
+as well. This has to do with the fact that for DES keys (which
+includes DESede), the "true" key size differs from the "effective"
+key size. (E.g., in DES, the "true" key size--from the DES algorithms's
+perspective is 64-bits, however the <i>effective</i> key size for DES
+is only 56-bits because of the 8-bits of parity "imposed" by the NSA in
+the early 1970s.) This inconsistency manifests itself in the JCE by
+the fact that <code>KeyGenerator.init()</code> wants the <i>effective</i>
+key size to be specified (so 56-bits for DES, 112- or 168-bits for
+DESede), but <code>SecretKey.getEncoding().length</code> stores the
+"true" key size (e.g., 64-bits or 192-bits for DES and DESede respectively).
+Other cipher algorithms do not have this discrepancy between true and
+effective key sizes. Since a <code>SecretKey</code> is returned by the
+<code>KeyGenerator</code>, this is only all too confusing. The
+reference <code>JavaEncryptor</code> and its JUnit test,
+<code>EncryptorTest</code> attempt to deal with this discrepency.
+See the adjustments made to key size in
+<code>EncryptorTest.runNewEncryptDecryptTestCase()</code>
+for further details.
+</p>
+<H2>Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules</H2>
+If you wish to use ESAPI with a FIPS 140-2 validated, JCE-compliant
+cryptographic module such as the
+<a
+href="http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/1401val2005.htm#497"
+target="_blank">
+IBM Java JCE FIPS 140-2 Cryptographic Module</a>
+(hereafter referred to as IBMJCEFIPS) or the
+<a
+href="http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/1401val2008.htm#1048"
+target="_blank">
+RSA Security BSafe Crypto-J</a>
+(hereafter referred to as Crypto-J), or other similar products, you
+<i>must</i> follow these instructions. Failure to do so will mean that
+you will fail to meet FISMA compliance. (<b>Note</b>: The mention of these
+two specific vendor products does not constitute an endorsement from
+either OWASP or this author.)
+<br />
+<ol>
+<li>Follow the vendor's regular instructions to configure your application
+to use the vendor product in a FIPS 140-2 compliant manner. Generally this
+will include some special configuration and/or initialization requirements
+that must be followed to restrict the vendor software to use FIPS 140-2
+approved algorithms.
+<br /><br />
+If the vendor's instructions do not already include a recommendation to edit
+the <code>$JAVA_HOME/jre/lib/security/java.security</code> file, the OWASP
+team recommends that you do so to prevent you from accidentally using any
+other cryptographic module (such as SunJCE). To do this, you will need
+to change the property "security.provider.1" (which represents the default)
+provider so that it refers to the fully-qualified classname of
+your vendor's JCE <code>Provider</code> class. (By default, this is set to
+"sun.security.provider.Sun".) In fact, you may wish to go as far as to
+either comment out or remove the other providers to reduce the possibility
+that they will accidentally be used by your application.
+</li>
+<li>Edit the <b>ESAPI.properties</b> that your application is going to
+be using and set the property "Encryptor.PreferredJCEProvider" to
+the fully qualified classname of your vendor's FIPS 140-2 compliant
+JCE provider class. (If you are using a Java <code>SecurityManager</code>
+you may also need to grant
+<code>org.owasp.esapi.reference.crypto.JavaEncryptor</code> permissions
+to change the JCE provider.)
+</li>
+<li>Edit the <b>ESAPI.properties</b> that your application is going to
+be using and set the property <b>Encryptor.CipherText.useMAC</b> to
+<b><code>false</code></b>. This is critical as having this property
+set to <b><code>true</code></b> causes ESAPI to use <i>derived</i> keys
+for the actual encryption and MAC calculation, which is against FIPS 140-2
+compliance. (<b>Note</b>: This
+does not mean that OWASP believes that this key derivation is weak--its
+design has been suggested some cryptographers--but it would be creating and
+using a related key for encryption in a manner not reviewed by NIST
+and thus it is not acceptable to FIPS 140-2 approval.)
+</li>
+<li>Edit the <b>ESAPI.properties</b> that your application is going to
+be using and set the property "Encryptor.CipherTransformation" to
+a cipher transformation that is FIPS 140-2 certified for you vendor's
+software. OWASP recommends using a NIST "combined" cipher mode, that is
+one that provides for both message confidentiality <i>and</i> message
+authenticity if such a cipher mode is available from your vendor
+and FIPS 140-2 certified. (Cipher modes "GCM" and "CCM" are the only
+FIPS 140-2 approved as of this writing.)
+If such a "combined" cipher mode is not available, then use a cipher
+transformation like "AES/CBC/PKCS5Padding" with a random IV.
+This is almost always available as one of the FIPS 140-2 certified cipher
+transformations, however it will not provide you with message authenticity
+because to remain FIPS 140-2 compliant you will have had to disable the
+MAC calculation (via setting <b>Encryptor.CipherText.useMAC</b> to
+<b><code>false</code></b>) causing ESAPI to skip calculating an
+explicit MAC for you and hence providing no assurance of data integrity
+to the party attempting to decrypt your data. Without authenticity
+however, your encryption may still be vulnerable to the "padded oracle"
+chose ciphertext attack. Consult with your local cryptographic expert in
+such cases as this depends greatly on the circumstances of how you are
+using encryption.
+</li>
+<li>
+If your vendor softare requires you to explicitly to initialize their
+software for FIPS-mode (e.g., to cause the required FIPS 140-2 "power-on
+self tests" to run) by calling some software method, then this must be
+done <i>by your application code <b>before</b></i> calling any of the ESAPI
+crypto-related code. Specifically, it must be called <i>before</i>
+your application calls <code>ESAPI.encryptor()</code>.
+</li>
+</ol>
+<br />
+We believe that following these steps will still allow for your application
+to be FIPS 140-2 / FISMA compliant, however, as always, you should check
+with your FIPS auditor before using ESAPI in this manner. Should your
+auditors ask, feel free to point them to the ESAPI source code, which
+we believe will convince them that ESAPI is not a cryptographic module.
+(Rather, it only provides a wrapper to call other cryptographic modules
+through the standard JCE APIs.)
+<H2>Acknowledgments</H2>
+The OWASP ESAPI development team would like to acknowledge the contributions
+of cryptographers David A. Wagner and Ian Grigg. David provided the
+outline of how to securely compute derived keys for confidentiality
+and authenticity from a single master key and Ian convinced us
+to take a packetizing approach to laying out the portable, serialized
+<code>CipherText</code> object.
+<p>
+In addition, Kevin Kenan has agreed to review all the crypto code
+from ESAPI 2.0-rc5 or 2.0-rc6 candidate release. Others are invited
+to participate as well, especially those with a background in cryptography.
+(See <a href="http://www.owasp.org/index.php/Request_to_review_ESAPI_2.0_crypto">
+"Request to review ESAPI 2.0 crypto"</a> for details.)
+</p><p>
+I would also like to thank Jessica Fitzgerald-McKay and Andy Sampson of
+the Systems and Network Analysis Center of the NSA for their excellent feedback in
+response to the review request made in Google Issue #81. Working with bureaucracy
+is not something that I do particularly well, but Jessica and Andy made it as
+painless as it possibly could be. Their feedback, which at this point,
+unfortunately we are unable to quote, was generally favorable, but in the
+places where they had recommendations, these were very precise and
+helpful.
+</p><p>
+Lastly, I would like to thank Jeffrey Walton of Software Integrity, LLC.
+Jeff provided an extremely thorough analysis of ESAPI 2.0's (as of 2.0_rc10)
+Key Derivation Function (KDF). Jeff's analysis convinced me to bring ESAPI's
+KDF more in line with NIST's recommendations for KDFs as described in
+NIST Special Publication 800-108 (and specifically section 5.1). You can
+read about Jeff's review at
+<a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/Analysis-of-ESAPI-2.0-KDF.pdf">
+Analysis of ESAPI 2.0's Key Derivation Function
+</a>
+</p>
+</BODY>
+</HTML>
diff --git a/documentation/.svn/text-base/esapi4java-core-2.1-release-notes.txt.svn-base b/documentation/.svn/text-base/esapi4java-core-2.1-release-notes.txt.svn-base
new file mode 100644
index 0000000..3570d0f
--- /dev/null
+++ b/documentation/.svn/text-base/esapi4java-core-2.1-release-notes.txt.svn-base
@@ -0,0 +1,68 @@
+ESAPI for Java - 2.1.0 Release Notes
+
+1) Fixed security issue #306, a vulnerability discovered by Phillipe Arteau.
+   This fix necessitated removing the deprecated encrypt() and decrupt() methods
+   that were intended to provide backward compatibility with ESAPI 1.4.
+   As it turns out, there was no way to fix this bug without a major rewrite
+   unless these methods were removed. However, as these two methods have been
+   deprecated more than 2 years ago and they are known to be insecure
+   (they are vulnerable to padding oracle attacks), the ESAPI team has
+   decided to remove them in accordance to their support policy.
+   
+   See comments for issue #306 for further details, as well as additional
+   safety precautions that you may wish to take in the unlikely, but possible
+   event that this vulnerability resulted in an actual security breach.
+
+   Finally, since the removal of these methods constitute an interface change
+   (to the Encryptor interface), this is considered a minor release (2.1)
+   rather than simply a patch release (2.0.2).
+
+   Please note that there are further updates planned to further strengthen
+   the MAC that ESAPI crypt uses. However, because they will require some
+   design changes, they may not be out for another month. Note that these
+   fixes do not correct any *known* vulnerabilities, but will address
+   some potential weaknesses in what is not included in the MAC (such as
+   the crypto version).
+
+2) Other Google Issues fixed: 257, 271, and 292 are all fixed in this release.
+
+3) Fixed Javadoc for Encoder.encryptForJavaScript(). [Revision r1879]
+
+4) DefaultEncryptedProperties - made minor Javadoc changes.
+
+5) The ESAPI 2.0 Encryptor.encrypt() methods now all throw an appropriate
+   IllegalArgumentException if any of the arguments are null. Previously,
+   if any of the arguments were null you would either get an AssertionError
+   (if you had assertions enabled) or a default NullPointerException when
+   assertions were disabled.  While IllegalArgumentException is still an
+   unchecked RuntimeException, note that if you were previously catching
+   NullPointerExceptions for these cases, you may need to change your code.
+
+6) The public constructor, CiphertextSerializer(CipherText ct), was changed
+   to explicitly check that the parameter is not null. Previously it had
+   checked with assertions which might later result in a NullPointerException
+   being thrown if assertions were disabled. Now if the parameter is null,
+   an appropriate IllegalArgumentException is thrown. This should not really
+   affect existing code (unless you are experimenting implementing your own
+   crypto) since user code should not really be using CiperTextSerializer
+   directly.
+
+7) Some of the setter methods in KeyDerivationFunction were changed to explicitly
+   check for invalid arguments and throw an IllegalArgumentException rather than
+   checking these parameters via assertions. This should not affect general
+   user code as most would not be calling the KeyDerivationFunction class
+   directly.
+
+8) Other miscellaneous minor code clean-up, mostly to remove compiler warnings.
+
+NOTE: A follow-up patch release is scheduled within the next few months to
+      address some questionable design decisions regarding what data in
+      the serialized ciphertext should be authenticated via the MAC. For
+      instance, presently only the IV+ciphertext is MAC'd (as would be the
+      equivalent case of when you would use an authenticated combined cipher
+      mode such as GCM or CCM). A deeper analysis of the design is required
+      based on findings in Google Issue # 306. I will periodically try
+      to keep the ESAPI mailing lists updated with the progress so watch
+      there for emerging details and anticipated schedule.
+      
+-Kevin W. Wall <kevin.w.wall at gmail.com>, 2013-08-30
diff --git a/documentation/.svn/text-base/esapi4java-disa.JPG.svn-base b/documentation/.svn/text-base/esapi4java-disa.JPG.svn-base
new file mode 100644
index 0000000..5578904
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-disa.JPG.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-google-code.JPG.svn-base b/documentation/.svn/text-base/esapi4java-google-code.JPG.svn-base
new file mode 100644
index 0000000..b2878c0
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-google-code.JPG.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-google-code.doc.svn-base b/documentation/.svn/text-base/esapi4java-google-code.doc.svn-base
new file mode 100644
index 0000000..67dca18
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-google-code.doc.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-waf-2.0-policy-file-spec.docx.svn-base b/documentation/.svn/text-base/esapi4java-waf-2.0-policy-file-spec.docx.svn-base
new file mode 100644
index 0000000..719f91f
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-waf-2.0-policy-file-spec.docx.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-waf-2.0-policy-file-spec.pdf.svn-base b/documentation/.svn/text-base/esapi4java-waf-2.0-policy-file-spec.pdf.svn-base
new file mode 100644
index 0000000..401d251
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-waf-2.0-policy-file-spec.pdf.svn-base differ
diff --git a/documentation/.svn/text-base/esapi4java-waf.JPG.svn-base b/documentation/.svn/text-base/esapi4java-waf.JPG.svn-base
new file mode 100644
index 0000000..80a104b
Binary files /dev/null and b/documentation/.svn/text-base/esapi4java-waf.JPG.svn-base differ
diff --git a/documentation/Analysis-of-ESAPI-2.0-KDF.odt b/documentation/Analysis-of-ESAPI-2.0-KDF.odt
new file mode 100644
index 0000000..7301bc6
Binary files /dev/null and b/documentation/Analysis-of-ESAPI-2.0-KDF.odt differ
diff --git a/documentation/Analysis-of-ESAPI-2.0-KDF.pdf b/documentation/Analysis-of-ESAPI-2.0-KDF.pdf
new file mode 100644
index 0000000..7bc07b1
Binary files /dev/null and b/documentation/Analysis-of-ESAPI-2.0-KDF.pdf differ
diff --git a/documentation/cc.JPG b/documentation/cc.JPG
new file mode 100644
index 0000000..8ca55b7
Binary files /dev/null and b/documentation/cc.JPG differ
diff --git a/documentation/esapi4java-2.0-javadoc-pictures.pptx b/documentation/esapi4java-2.0-javadoc-pictures.pptx
new file mode 100644
index 0000000..388fbd9
Binary files /dev/null and b/documentation/esapi4java-2.0-javadoc-pictures.pptx differ
diff --git a/documentation/esapi4java-2.0-readme.txt b/documentation/esapi4java-2.0-readme.txt
new file mode 100644
index 0000000..de7c40d
--- /dev/null
+++ b/documentation/esapi4java-2.0-readme.txt
@@ -0,0 +1,60 @@
+
+                            Welcome to ESAPI for Java!
+
+(This file best viewed full screen.)
+
+Here are the most significant directories and files included the zip file for this release:
+
+File / Directory                                                        Description
+=========================================================================================
+<root>/	
+|
++---configuration/                                                      Directory of ESAPI configuration files
+|     |
+|     |---.esapi/
+|     |     |---waf-policies/                                           Directory containing Web Application Firewall policies
+|     |     |---ESAPI.properties                                        The main ESAPI configuration file
+|     |     `---validation.properties                                   Regular expressions used by the ESAPI validator
+|     |
+|     `---properties/                                                   Examples of how to internationalize error messages???
+|           |---ESAPI_en_US.properties                                      in US/English
+|           |---ESAPI_fr_FR.properties                                      in French
+|           `---ESAPI_zhs_CN.properties                                     in Chinese
+|
+|---documentation/                                                      ESAPI documentation
+|     |
+|     |---esapi4java-2.0-readme.txt                                     The file you are now reading
+|     |---esapi4java-core-2.0-release-notes.pdf                         ESAPI 2.0 release notes (draft)
+|     |---esapi4java-core-2.0-install-guide.doc                         ESAPI 2.0 installation guide (draft)
+|     |---esapi4java-2.0rc6-override-log4jloggingfactory.txt            How to use log4j to override User logging
+|     |---esapi4java-core-2.0-ciphertext-serialization.pdf              Describes serialization layout of ESAPI 2.0 ciphertext representation
+|     |---esapi4java-core-2.0-crypto-design-goals.doc (draft)           Describes ESAPI 2.0 crypto design goals & design decisions
+|     |---esapi4java-core-2.0-readme-crypto-changes.html                Describes why crypto was changed from what was in ESAPI 1.4
+|     |---esapi4java-core-2.0-symmetric-crypto-user-guide.html          User guide for using symmetric encryption in ESAPI 2.0
+|     |---esapi4java-core-2.1-release-notes.txt                         ESAPI 2.1 release notes
+|     `---esapi4java-waf-2.0-policy-file-spec.pdf                       Describes how to configure ESAPI 2.0's Web Application Firewall
+|
+|---libs/                                                               ESAPI dependencies
+|
+|---site/
+|     |---apidocs                                                       ESAPI Javadoc
+|     |---cobertura
+|     `---testapidocs                                                   ESAPI Javadoc for its JUnit test cases
+|
+|---src/                                                                ESAPI source code
+|
+|---esapi-<vers>.jar                                                    The ESAPI jar for version <vers> (e.g., <vers> == 2.0_rc10)
+|
+|---LICENSE.txt                                                         ESAPI license for source code and documentation
+|
+`---pom.xml                                                             Maven's pom.xml for building ESAPI from source via mvn.
+
+===========================================================
+
+Where to go from here -- please see the installation guide and the release
+notes.
+
+Please address comments and questions concerning the API and this document to
+the ESAPI Users mailing list, <esapi-user at lists.owasp.org>.
+
+Copyright (C) 2009-2010 The OWASP Foundation.
diff --git a/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt b/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt
new file mode 100644
index 0000000..891f769
--- /dev/null
+++ b/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt
@@ -0,0 +1,71 @@
+This release includes critical changes to the ESAPI Log4JLogger that will now allow you to over-ride the user specific 
+message using your own User or java.security.Principal implementation.
+
+There are a three critical steps that need to be taken to over-ride the ESAPI Log4JLogger:
+
+1)  Please make a copy of http://owasp-esapi-java.googlecode.com/svn/trunk/src/main/java/org/owasp/esapi/reference/ExampleExtendedLog4JLogFactory.java and change the package and the class name (something like com.yourcompany.logging.ExtendedLog4JFactory). This class (not very big at all) gives you the exact “shell” that you will need to over-ride the user message of the ESAPI Log4JLogger.
+
+2)  In your new class, please change the following function to use your user object:
+
+        public String getUserInfo() {
+            return "-EXTENDEDUSERINFO-";
+    }
+
+3)  Change your copy of ESAPI.properties to use your new logging class
+
+The ESAPI.properties entry looks like this now:
+
+ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
+
+Please change it to the following, based on how you renamed your new logging class
+
+ESAPI.Logger=com.yourcompany.logging.ExtendedLog4JFactory
+
+And you should be all set!
+
+PS: The original ESAPI Log4JLogging class used a secure random number as a replacement to logging the session ID. This allowed 
+us to tie log messages from the same session together, without exposing the actual session id in the log file. The code looks 
+like this, and you may wish to use it in your over-ridden version of getUserInfo.
+
+HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+if ( request != null ) {
+    HttpSession session = request.getSession( false );
+    if ( session != null ) {
+        sid = (String)session.getAttribute("ESAPI_SESSION");
+        // if there is no session ID for the user yet, we create one and store it in the user's session
+        if ( sid == null ) {
+            sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000);
+            session.setAttribute("ESAPI_SESSION", sid);
+        }
+    }
+}
+
+In fact, here is the entire original getUserInfo() implementation (that was tied to the ESAPI request and user object) – 
+you may wish to emulate some of this.
+
+public String getUserInfo() {
+    // create a random session number for the user to represent the user's 'session', if it doesn't exist already
+    String sid = null;
+    HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+    if ( request != null ) {
+        HttpSession session = request.getSession( false );
+        if ( session != null ) {
+            sid = (String)session.getAttribute("ESAPI_SESSION");
+            // if there is no session ID for the user yet, we create one and store it in the user's session
+            if ( sid == null ) {
+                sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000);
+                session.setAttribute("ESAPI_SESSION", sid);
+            }
+        }
+    }
+    
+    // log user information - username:session at ipaddr
+    User user = ESAPI.authenticator().getCurrentUser();            
+    String userInfo = "";
+    //TODO - make type logging configurable
+    if ( user != null) {
+        userInfo += user.getAccountName()+ ":" + sid + "@"+ user.getLastHostAddress();
+    }
+    
+    return userInfo;
+}
diff --git a/documentation/esapi4java-big-duke.JPG b/documentation/esapi4java-big-duke.JPG
new file mode 100644
index 0000000..6f70077
Binary files /dev/null and b/documentation/esapi4java-big-duke.JPG differ
diff --git a/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf b/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf
new file mode 100644
index 0000000..9d1dc33
Binary files /dev/null and b/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf differ
diff --git a/documentation/esapi4java-core-2.0-ciphertext-serialization.xls b/documentation/esapi4java-core-2.0-ciphertext-serialization.xls
new file mode 100644
index 0000000..7f3106e
Binary files /dev/null and b/documentation/esapi4java-core-2.0-ciphertext-serialization.xls differ
diff --git a/documentation/esapi4java-core-2.0-crypto-design-goals.doc b/documentation/esapi4java-core-2.0-crypto-design-goals.doc
new file mode 100644
index 0000000..836e8c7
Binary files /dev/null and b/documentation/esapi4java-core-2.0-crypto-design-goals.doc differ
diff --git a/documentation/esapi4java-core-2.0-install-guide.doc b/documentation/esapi4java-core-2.0-install-guide.doc
new file mode 100644
index 0000000..d185ecb
Binary files /dev/null and b/documentation/esapi4java-core-2.0-install-guide.doc differ
diff --git a/documentation/esapi4java-core-2.0-install-guide.pdf b/documentation/esapi4java-core-2.0-install-guide.pdf
new file mode 100644
index 0000000..012c497
Binary files /dev/null and b/documentation/esapi4java-core-2.0-install-guide.pdf differ
diff --git a/documentation/esapi4java-core-2.0-readme-crypto-changes.html b/documentation/esapi4java-core-2.0-readme-crypto-changes.html
new file mode 100644
index 0000000..8687f5c
--- /dev/null
+++ b/documentation/esapi4java-core-2.0-readme-crypto-changes.html
@@ -0,0 +1,440 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+    <TITLE>Why Is OWASP Changing ESAPI Encryption?</TITLE>
+	<META NAME="GENERATOR" CONTENT="OpenOffice.org 3.0  (Linux)">
+	<META NAME="CREATED" CONTENT="0;0">
+	<META NAME="CHANGED" CONTENT="20100228;21125900">
+	<META NAME="CHANGEDBY" CONTENT="Kevin W. Wall">
+</HEAD>
+<BODY LANG="en-US" DIR="LTR">
+<H1 ALIGN=CENTER>Why Is OWASP Changing ESAPI Encryption?</H1>
+<H2>Reasons for Change</H2>
+<H3>Symmetric Encryption in ESAPI 1.4</H3>
+<P STYLE="margin-bottom: 0in">The as-delivered, default ESAPI 1.4
+encryption mechanism uses "PBEWithMD5AndDES", a "Password
+Based Encryption" algorithm. As implemented in ESAPI 1.4,
+password (or pass phrase), taken from the property MasterPassword, is
+concatenated with the salt as specified by the property MasterSalt,
+and then repeated hashed 20 times using the MD5 message digest
+algorithm. The intent of the salt and repetitive hashing is to slow
+down dictionary attacks on the password. The result after the 20
+iterations of hashing is used as the DES encryption key. The
+encryption uses Cipher Block Chaining (CBC) cipher mode.</P>
+<H3>Problems with Symmetric Encryption in ESAPI 1.4</H3>
+<P>The problems with symmetric encryption in ESAPI 1.4 are many. Here
+are some of the main issues:</P>
+<OL>
+	<LI><P>The implementation only supported a single encryption key.
+	While this may be sufficient for some uses, clearly there are cases
+	where multiple recipients are involved when you would like to use
+	unique encryption keys for each recipient.</P>
+	<LI><P>ESAPI 1.4 came with a default setting for MasterPassword and
+	MasterSalt, thus not requiring that it be changed. How many of you
+	forgot to change these?</P>
+	<LI><P>Password based encryption is likely to be much weaker
+	randomly choosing a secret key. For instance, if you restrict
+	yourself to printable ASCII characters (which most people will), it
+	would require a pass phrase of roughly 45 characters to be the
+	equivalent of a randomly chosen 128-bit secret key.</P>
+	<LI><P>The default algorithm, DES, only has an effective key length
+	of 56-bits. This key size is too short and is in the realm of brute
+	forcing within a short amount of time for most medium to large
+	companies. (Fortunately, this could be addressed in part by changing
+	the EncryptionAlgorithm property from "PBEWithMD5AndDES"
+	to either "PBEWithHmacSHA1AndDESede" [if you are using JDK
+	1.5 or later] or to "PBEWithMD5AndTripleDES" [if you are
+	still using JDK 1.4]. Unfortunately, because of the implementation
+	of <CODE>JavaEncryptor</CODE>, only "password based encryption"
+	algorithms can be used in ESAPI 1.4.)</P>
+	<LI><P>The symmetric encryption does not provide for any means of
+	confirming message authenticity. Cryptographers consider ensuring
+	message authenticity an important feature of modern cryptosystems.</P>
+</OL>
+<H3>Symmetric Encryption in ESAPI 2.0rc1 and 2.0rc2</H3>
+<P STYLE="margin-bottom: 0in">By default, release candidates ESAPI
+2.0-rc1 and 2.0-rc2 used the 256-bit AES cipher with the cipher mode
+Electronic Code Book (ECB) for encryption with <CODE>Encryptor</CODE>
+via the ESAPI reference implementation, <CODE>JavaEncryptor</CODE>.
+ECB cipher mode is the simplest cipher mode to use, but it is also
+cryptographically very weak. If more than one block of plaintext is
+encrypted with the same key, those identical blocks of ciphertext
+always encrypt to the same ciphertext block, thus revealing patterns
+in the plaintext input. For example, these images from Wikipedia's
+<A HREF="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation">Block
+cipher modes of operation</A> illustrate this point well: 
+</P>
+<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0>
+	<TR>
+		<TD>
+			<P ALIGN=CENTER><FONT COLOR="#000080"><A HREF="http://en.wikipedia.org/wiki/File:Tux.jpg"><FONT COLOR="#000080"><IMG SRC="http://upload.wikimedia.org/wikipedia/commons/5/56/Tux.jpg" NAME="graphics1" ALT="Original Tux image" ALIGN=BOTTOM WIDTH=196 HEIGHT=216 BORDER=1></FONT></A></FONT></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><FONT COLOR="#000080"><A HREF="http://en.wikipedia.org/wiki/File:Tux_ecb.jpg"><FONT COLOR="#000080"><IMG SRC="http://upload.wikimedia.org/wikipedia/commons/f/f0/Tux_ecb.jpg" NAME="graphics2" ALT="Tux image encrypted using ECB mode" ALIGN=BOTTOM WIDTH=196 HEIGHT=216 BORDER=1></FONT></A></FONT></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><FONT COLOR="#000080"><A HREF="http://en.wikipedia.org/wiki/File:Tux_secure.jpg"><FONT COLOR="#000080"><IMG SRC="http://upload.wikimedia.org/wikipedia/commons/a/a0/Tux_secure.jpg" NAME="graphics3" ALT="Tux image encrypted with other modessuch as CBC" ALIGN=BOTTOM WIDTH=196 HEIGHT=216 BORDER=1></FONT></A></FONT></P>
+		</TD>
+	</TR>
+	<TR>
+		<TD>
+			<P ALIGN=CENTER><I>Original Tux image</I></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><I>Image encrypted using ECB mode</I></P>
+		</TD>
+		<TD>
+			<P ALIGN=CENTER><I>Image encrypted using modes other than ECB</I></P>
+		</TD>
+	</TR>
+</TABLE>
+<P>Ciphertext encrypted with ECB cipher mode are also subject to
+"block replay attacks". See Bruce Schneier's <A HREF="http://www.google.com/url?sa=t&source=web&ct=res&cd=2&url=http%3A%2F%2Fbooks.google.com%2Fbooks%3Fid%3DA6ZO2D6ayNwC%26pg%3DPT216%26lpg%3DPT216%26dq%3Decb%2B%2522block%2Breplay%2522%26source%3Dbl%26ots%3DiEbAWQpu0e%26sig%3D8xiUva4XKaAOfPJEPsULPAJPk88%26hl%3Den%26ei%3Da6yISoLQPJOuMI-Z_OkE%26sa%3DX%26oi%3Dbook_result%26ct%3Dresult%26resnum%3D2&ei=a6yISoLQPJOuMI-Z_OkE&rct=j&q=ecb+%22block+replay%22&am [...]
+Cryptography: protocols, algorithms, and source code</I> </A>for
+details. 
+</P>
+<P>In both ESAPI 2.0-rc1 and 2.0-rc2, one can choose other block
+ciphers (e.g. Blowfish) or other key sizes (e.g., 512-bit AES), but
+encryption in ESAPI 2.0-rc1 and 2.0-rc2 <I>only</I> support ECB
+cipher mode with no padding; it cannot be used to decrypt data
+encrypted with anything other than an algorithm using ECB cipher mode
+and no padding. Most encryption (outside of ESAPI) uses some other
+cipher mode (e.g., Cipher Block Chaining (CBC) mode) and some sort of
+padding scheme, such as PKCS#5 padding, meaning that they are many
+situations where ESAPI encryption would not be suitable.</P>
+<H3>Problems with Symmetric Encryption in ESAPI 2.0-rc1 and 2.0-rc2</H3>
+<P>The problems with symmetric encryption in ESAPI 2.0-rc1 and 2.0rc2
+are many. Here are some of the main issues:</P>
+<OL>
+	<LI VALUE=1><P>The implementation only supported a single encryption
+	key. While this may be sufficient for some uses, clearly there are
+	cases where multiple recipients are involved when you would like to
+	use unique encryption keys for each recipient.</P>
+	<LI><P>ESAPI 2.0-rc1 and 2.0-rc2 came with a default settings for
+	Encryptor.MasterKey and Encryptor.MasterSalt, thus not requiring
+	that these be changed. How many of you forgot to change these?</P>
+	<LI><P>Because the reference implementation in <CODE>JavaEncryptor</CODE>
+	allows no means to specify an Initialization Vector (IV), only the
+	weak ECB cipher mode can be used.</P>
+	<LI><P>The symmetric encryption does not provide for any means of
+	confirming message authenticity. Cryptographers consider ensuring
+	message authenticity an important feature of modern cryptosystems.</P>
+</OL>
+<H2>The Encryption Changes in ESAPI 2.0-rc3 and Later</H2>
+<P>Briefly speaking, the changes being implemented for ESAPI Java 2.0
+are: 
+</P>
+<OL>
+	<LI><P STYLE="margin-bottom: 0in">Starting in ESAPI Java 2.0-rc3,
+	the <I>default</I> cipher transformation is changing to
+	"AES-128/CBC/PKCS5Padding" (i.e., 128-bit AES in CBC
+	cipher mode and PKCS#5 padding) with a random IV. The main reason
+	that 128-bit AES was chosen over 256-bit AES for the default is that
+	128-bit AES is slightly faster than 256-bit AES, and it does not
+	require you downloading the Unlimited Strength Jurisdiction Policy
+	files from Sun Microsystems. For those believing that 128-bit AES is
+	not sufficiently strong--bruting forcing it (except perhaps with a
+	reasonable quantum computer) is still not feasible--and certain key
+	related attacks (see Biryukov, Dunkelman, Keller, Khovratovich, and
+	Shamir's <A HREF="http://eprint.iacr.org/2009/374">Cryptology ePrint
+	Archive: Report 2009/374 -- Key Recovery Attacks of Practical
+	Complexity on AES Variants With Up To 10 Rounds </A>for further
+	details) show that longer key sizes in AES may not always be the
+	best.</P>
+	<LI><P STYLE="margin-bottom: 0in">Because cipher modes other than
+	ECB require an Initialization Vector (IV), a mechanism to handle
+	both fixed and random IVs has been added. The use of the “fixed”
+	IV should only be considered if there is a need to be compatible
+	with some legacy software or third party software package. Those
+	cases should be rare.</P>
+	<LI><P STYLE="margin-bottom: 0in">The original attempt to preserve
+	backward compatibility with ESAPI Java 1.4 (via
+	<CODE>org.owasp.esapi.reference.crypto.LegacyJavaEncryptor</CODE>)
+	has been removed. This was put to a vote on Jan 31, 2010 before both
+	the ESAPI Users and ESAPI Developers mailing lists and the lack of
+	response was deafening. There literally was but a single response
+	and that was to kill off <CODE>LegacyJavaEncryptor</CODE><CODE><FONT FACE="Thorndale AMT, serif">.</FONT></CODE>
+	(By this time, the two symmetric encryption interfaces in <CODE>Encryptor</CODE>
+	had already been deprecated.) 
+	</P>
+	<LI><P>The byte-encoding has been changed from native byte encoding
+	to UTF-8 byte-encoding throughout ESAPI 2.0 and not just for
+	encryption. This was needed so that one could (say) encrypt on a
+	Sparc running Solaris and then move the encrypted data to an Intel
+	host running Windows and still be able to decrypt the data. Without
+	this change to use uniform encoding throughout, this could not be
+	guaranteed.</P>
+</OL>
+<H2>The Good, the Bad, and the Ugly</H2>
+<P>Or put another way, there are always trade-offs to be made... 
+</P>
+<H3>The Good</H3>
+<P>We get improved security by encouraging the use of stronger cipher
+modes (and ESAPI is all about "enterprise security",
+right?). Furthermore, it is possible to choose a preferred JCE
+provider for ESAPI and ESAPI will attempt to use that provider. For
+example, if you wish to use Bouncy Castle rather than the SunJCE, you
+can do so. One place where this might be a significant advantage is
+if you need to use a FIPS 140-2 compliant JCE implementation. Whereas
+in production environments, you ultimately will want to ensure this
+by editing your <FONT FACE="DejaVu Sans Mono, sans-serif">$JAVA_HOME/jre/lib/security/java.security</FONT>
+file, this is often inconvenient for developers who simply wish to
+test because this file often requires super-user access to alter. In
+such cases, setting the new ESAPI property
+<FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.PreferredJCEProvider</FONT>
+to the fully qualified class name of your FIPS 140-2 JCE provider in
+your <FONT FACE="Thorndale AMT, serif"><B>ESAPI.properties</B></FONT>
+file and you can test without any other changes to your code or you
+environment.</P>
+<P>We also get message authenticity as long as we don't use any of
+the deprecated encryption methods or intentionally disable it. (The
+only <I>sane</I> reason for intentionally disabling the MAC
+calculation is for FIPS 140-2 compliance. This MAC calculation
+requires deriving two new <FONT FACE="DejaVu Sans Mono, sans-serif">SecretKey</FONT>s
+from the original key—one to use for message authenticity and a
+second one to use for message confidentiality. While we believe that
+this mechanism is secure [it was originally suggested by professional
+cryptographers], this key derivation would not be using FIPS 140-2
+approved code. Therefore if you require FIPS 140-2 compliance, you
+should set “<FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.CipherText.useMAC=false</FONT>”
+in your <FONT FACE="Thorndale AMT, serif"><B>ESAPI.properties</B></FONT>
+file.)</P>
+<H3>The Bad</H3>
+<P>With cipher modes that require an IV, the same IV must be used
+both to encrypt and decrypt. While it is not required that the IV be
+kept secret from adversaries, there are some attacks that are
+possible if the adversary is permitted to alter the IV at will and
+observe the results of the ensuing decryption attempt. 
+</P>
+<P>So that leaves two choices for the IV: 
+</P>
+<UL>
+	<LI><P STYLE="margin-bottom: 0in">Using a <I><B>fixed IV</B></I>:
+	The IV can be agreed upon out-of-band by the parties exchanging
+	encrypted messages. In this way, the encrypted data that is stored
+	and/or transmitted can be limited to only the <I>raw</I> ciphertext.
+	(<U><B>WARNING</B></U><SPAN STYLE="text-decoration: none"><SPAN STYLE="font-weight: normal">:
+	This case should be reserved for situations requiring compatibility
+	with legacy or third party software packages.)</SPAN></SPAN></P>
+	<LI><P>Using a <I><B>random IV</B></I>: Most cryptographers prefer
+	to use a random IV, typically a different one with each message to
+	be encrypted. However, doing so on encrypted data that will be
+	persisted (e.g., to a database) or transmitted to the recipient this
+	random IV must be stored / made known. Therefore, the raw ciphertext
+	can no longer suffice; whatever random IV that was chosen must be
+	communicated. 
+	</P>
+</UL>
+<P>Likewise, the use of padding is going to add some overhead to the
+length of the ciphertext.</P>
+<P STYLE="margin-bottom: 0in">For example, a short plaintext of 0-15
+bytes, this overhead can be around 200% of the total plaintext size.
+This overhead diminishes (as a percent of the total plaintext size)
+as the plaintext size increases. Here is a table that illustrates
+this:</P>
+<A NAME="encryption-overhead">
+<P STYLE="margin-bottom: 0in"><BR>
+</P>
+</A>
+<TABLE WIDTH=100% BORDER=1 BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0>
+	<COL WIDTH=64*>
+	<COL WIDTH=64*>
+	<COL WIDTH=64*>
+	<COL WIDTH=64*>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Original input length of plaintext <BR>(#
+			bytes)</B></P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Base64-encoded length of ciphertext (with IV
+			and padding) encrypted with 128-bit AES in CBC mode, but no MAC<SUP>1</SUP></B></P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Length of raw portable serialized CipherText
+			byte array, containing IV, padding, and MAC, and encrypted with
+			128-bit AES in CBC mode</B></P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=CENTER><B>Length of raw portable serialized CipherText,
+			containing IV, padding, and MAC, and encrypted with 128-bit AES in
+			CBC mode as a Base64-encoded string</B></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>0-15</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>44</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>102</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="137" SDNUM="1033;">
+			<P>137</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>16-31</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>64</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>118</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="162" SDNUM="1033;">
+			<P>162</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>32-47</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>88</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>134</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="182" SDNUM="1033;">
+			<P>182</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>48-63</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>108</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>150</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="202" SDNUM="1033;">
+			<P>202</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>64-79</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>128</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>166</P>
+		</TD>
+		<TD WIDTH=25% SDVAL="222" SDNUM="1033;">
+			<P>222</P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>… etc.  Each additional 16 bytes of plaintext...</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>...adds ~20 additional bytes</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P ALIGN=LEFT>...adds 16 additional bytes</P>
+		</TD>
+		<TD WIDTH=25%>
+			<P>...adds 20 additional bytes</P>
+		</TD>
+	</TR>
+</TABLE>
+<P>__________________________________</P>
+<P>1. That is, the <FONT FACE="DejaVu Sans Mono, sans-serif">String</FONT>
+that is returned by the deprecated <FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.decrypt(String)</FONT>
+method.</P>
+<P>As you can see, since the size of the IV and the amount of padding
+bytes are fixed at a maximum, this overhead goes down as the length
+of the plaintext message increases. The IV is always a fixed length
+for a given cipher; it is always the same as the cipher block size.
+The padding can vary, but for PKCS5 padding, the padding will be
+between 1 to the cipher block size (in bits) / 8 bytes. For AES, the
+cipher block size is 128-bits, but more typically, a cipher's block
+size is 64-bits so the padding would be between 1 to 16 bytes for AES
+and 1 to 8 bytes for a 64-bit block size cipher and the IV would be
+IV would be 16 bytes for AES and 8 bytes for most other ciphers. 
+</P>
+<H3>The Ugly</H3>
+<P>Well, so far, this "bad" news may be bad for you but
+good for your database and/or storage vendor, but you probably can
+live with it, especially if you are willing to do a little bit of
+recoding of your database table sizes.</P>
+<P>But wait Skippy, don't go running off just quite yet. As Robert
+Heinlein wrote in his 1966 novel <I>The Moon is a Harsh Mistress</I>
+"There ain't no such thing as a free lunch". (Some of us
+more hardened cynics know it more commonly as <I>TANSTAAFL</I>.) 
+</P>
+<P>As mentioned earlier, backward compatibility with ESAPI 1.4
+(originally planned via <CODE>LegacyJavaEncryptor</CODE>) has been
+removed because of apparent lack of interest. What this means is if
+you are one of the unlucky stuckeys who are using ESAPI 1.4 symmetric
+encryption to encrypt data and you have persisted this data (and this
+would <I>include </I><SPAN STYLE="font-style: normal">the use of the
+</SPAN><FONT FACE="DejaVu Sans Mono, sans-serif"><SPAN STYLE="font-style: normal">org.owasp.esapi.EncryptedProperties</SPAN></FONT>
+<SPAN STYLE="font-style: normal">class in ESAPI 1.4)</SPAN>, you will
+first have to <I>decrypt </I><SPAN STYLE="font-style: normal">this
+encrypted data using ESAPI 1.4 before using encryption with ESAPI
+2.0. As they say, “we are sorry for the inconvenience”, but there
+was an opportunity to make your voice heard so we refuse to take </SPAN><I>all
+</I><SPAN STYLE="font-style: normal">the blame.</SPAN></P>
+<P>In addition, all this backward compatibility and flexibility comes
+at the extra cost of complexity, and not <I>merely</I> additional
+complexity in the reference implementation. There is also some
+additional complexity in the ESAPI <CODE>Encryptor</CODE> interfaces
+as well.</P>
+<P>In particular, there new classes for plaintext (<FONT FACE="DejaVu Sans Mono, sans-serif">PlainText</FONT>)
+and ciphertext (<CODE>CipherText)</CODE> class to coalesce all this
+complexity of handling the ciphertext result from encryption
+operations. And then there are new encryption and decryption methods
+for the <CODE>Encryptor</CODE> interface. Specifically, the encrypt
+and decrypt methods have been generalized as: 
+</P>
+<PRE STYLE="margin-left: 0.49in"><FONT COLOR="#000000"><FONT FACE="Monospace">CipherText encrypt(SecretKey key, PlainText plaintext)</FONT></FONT>
+<FONT COLOR="#000000">        <FONT FACE="Monospace">throws EncryptionException;</FONT></FONT></PRE><P STYLE="margin-bottom: 0in">
+and 
+</P>
+<PRE STYLE="margin-left: 0.49in"><FONT COLOR="#000000"><FONT FACE="Monospace">PlainText decrypt(SecretKey key, CipherText ciphertext)</FONT></FONT>
+<FONT COLOR="#000000">        <FONT FACE="Monospace">throws EncryptionException</FONT></FONT></PRE><P>
+(There are also two corresponding encrypt / decrypt methods that omit
+the initial <FONT FACE="DejaVu Sans Mono, sans-serif">SecretKey</FONT>
+argument and instead use the <FONT FACE="DejaVu Sans Mono, sans-serif">SecretKey</FONT>
+based on <FONT FACE="DejaVu Sans Mono, sans-serif">Encryptor.MasterKey</FONT>.)</P>
+<P>The two existing interfaces from ESAPI 1.4 and earlier:</P>
+<PRE STYLE="margin-left: 0.49in; margin-bottom: 0.2in">String encrypt(String plaintext) throws EncryptionException</PRE><P STYLE="margin-bottom: 0in">
+and 
+</P>
+<PRE STYLE="margin-left: 0.49in; margin-bottom: 0.2in">String decrypt(String ciphertext) throws EncryptionException</PRE><P>
+are still supported but have been <I>deprecated</I>, mainly because
+they do not support integrity / authenticity checks which most
+cryptographers consider essential. Also these older String-based
+encrypt() / decrypt() interfaces have slightly different semantics
+than do their ESAPI 1.4 counterparts in that these methods default to
+CBC cipher mode, PKCS5Padding, and a random IV. This means that at a
+minimum, the IV must be included as the resulting base64-encoded
+“ciphertext”.</P>
+<P>However, this difference in cipher mode means that if you have
+data that was previously encrypted using ESAPI 1.4 or earlier, you
+will not be able to decrypt it using the ESAPI 2.0</P>
+<PRE STYLE="margin-left: 0.49in; margin-bottom: 0.2in">String decrypt(String ciphertext) throws EncryptionException</PRE><P>
+method. If you require such compatibility, you may contact the
+author, <a href="mailto:kevin.w.wall at gmail.com">Kevin W. Wall</a>,
+for assistance.
+</P>
+<P>Note that while the new String-based encrypt() / decrypt() methods
+use the stronger CBC cipher mode with a random IV, they still do not
+ensure authenticity and integrity so the more general encrypt() /
+decrypt() methods should be preferred over the String-based ones.
+(See below for examples of how to use these new methods.)</P>
+<H2>Further Details</H2>
+<P>Further details are provided in the <CODE>Encryptor</CODE> Javadoc
+and the “Symmetric Encryption” section of the ESAPI User Guide.</P>
+</BODY>
+</HTML>
diff --git a/documentation/esapi4java-core-2.0-release-notes.doc b/documentation/esapi4java-core-2.0-release-notes.doc
new file mode 100644
index 0000000..9853e0d
Binary files /dev/null and b/documentation/esapi4java-core-2.0-release-notes.doc differ
diff --git a/documentation/esapi4java-core-2.0-release-notes.pdf b/documentation/esapi4java-core-2.0-release-notes.pdf
new file mode 100644
index 0000000..351f055
Binary files /dev/null and b/documentation/esapi4java-core-2.0-release-notes.pdf differ
diff --git a/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html b/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
new file mode 100644
index 0000000..2091cad
--- /dev/null
+++ b/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
@@ -0,0 +1,796 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+	<TITLE>ESAPI 2.0 Symmetric Encryption User Guide</TITLE>
+	<META NAME="GENERATOR" CONTENT="OpenOffice.org 3.0  (Linux)">
+	<META NAME="CREATED" CONTENT="20100214;0">
+	<META NAME="CHANGEDBY" CONTENT="Kevin W. Wall">
+	<META NAME="CHANGED" CONTENT="20100304;00400700">
+</HEAD>
+<BODY LANG="en-US" DIR="LTR">
+<TABLE BORDER="0" BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0 STYLE="page-break-before: auto; page-break-after: auto; page-break-inside: auto">
+<TR>
+<TD>
+<OBJECT TYPE="audio/x-mpeg" data="http://www.catonmat.net/download/crypt-o.mp3"
+WIDTH="500" HEIGHT="64" AUTOPLAY="false">
+    <PARAM NAME="src" VALUE="http://www.catonmat.net/download/crypt-o.mp3" />
+    <PARAM NAME="controller" VALUE="true" />
+    <PARAM NAME="autoplay" VALUE="false" />
+    <PARAM NAME="autostart" VALUE="0" />
+</OBJECT>
+</TD>
+<TD>
+<FONT COLOR="#00a444" SIZE="+2">
+<I>Crypto song. Take a listen and enjoy! Harry Belafonte never sounded this good. ;-)</I>
+</FONT>
+</TD>
+</TABLE>
+<H1 ALIGN=CENTER>ESAPI 2.0 Symmetric Encryption User Guide</H1>
+<H2>ESAPI.properties Properties Relevant to Symmetric Encryption</H2>
+<P>Those properties that are new since ESAPI 2.0-rc2 are shown in
+<FONT COLOR="#ff0000">red</FONT>. Values shown in <FONT COLOR="#0000ff">blue</FONT>
+are ones that you would replace.</P>
+<TABLE BORDER=1 BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0 STYLE="page-break-before: auto; page-break-after: auto; page-break-inside: auto">
+	<COL WIDTH=249>
+	<COL WIDTH=202>
+	<COL WIDTH=226>
+	<TR VALIGN=TOP>
+		<TH WIDTH=249>
+			<P>Property Name</P>
+		</TH>
+		<TH WIDTH=202>
+			<P>Default Value</P>
+		</TH>
+		<TH WIDTH=226>
+			<P>Comment</P>
+		</TH>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT SIZE=2>ESAPI.Encryptor</FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT SIZE=2>org.owasp.esapi.reference.crypto.JavaEncryptor</FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>The class implementing the Encryptor interface and
+			returned by ESAPI.encryptor().</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT SIZE=2>Encryptor.MasterKey</FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<P><FONT COLOR="#0000ff"><FONT SIZE=2><initially unset></FONT></FONT></P>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>The base64-encoded SecretKey. The key must be
+			appropriate to the specified key size and cipher algorithm.</FONT></P>
+			<P><FONT SIZE=2>Set as per the instructions in the ESAPI
+			Installation Guide.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT SIZE=2>Encryptor.MasterSalt</FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<P><FONT COLOR="#0000ff"><FONT SIZE=2><initially unset></FONT></FONT></P>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>A base64-encoded random “salt”. This should be
+			at least 20-bytes. It is used to generate a random (but
+			consistent) public/private key pair used in asymmetric encryption
+			and digital signatures.</FONT></P>
+			<P><FONT SIZE=2>Set as per the instructions in the ESAPI
+			Installation Guide.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><STRIKE><FONT SIZE=2>Encryptor.EncryptionAlgorithm</FONT></STRIKE></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT SIZE=2>AES</FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>A deprecated property, superseded by
+			Encryptor.CipherTransformation.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.CipherTransformation</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>AES/CBC/PKCS5Padding</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Specifies the cipher transformation to use for
+			symmetric encryption. The format is
+			cipherAlgorithm/cipherMode/paddingScheme.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.EncryptionKeyLength</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>128</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Key size, in bits. Required for cipher algorithms
+			that support multiple key sizes.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.ChooseIVMethod</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>random</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Legal values are “random” or “fixed”.
+			Random is recommended. Set to “fixed” only if required for
+			compatibility with legacy or third party software. If set to
+			“fixed”, then the property Encryptor.fixedIV must also be
+			set to hex-encoded specific IV that you need to use.
+            </FONT></P><P><FONT SIZE=2>
+            <B>CAUTION:</B> While it is not required that the IV be kept
+            secret, encryption relying on fixed IVs can lead to a known
+            plaintext attack called a "Key Collision Attack". While this
+            attack is probably not practical (for those with modest
+            resources) for ciphers with 128-bit key size, this attack
+            makes it possible to capture the ciphertexts from only
+            2<sup>(N/2)</sup> known plaintexts to discover the
+            encryption key. Loughran and Dowling explain a Java
+            implementation of Eli Biham's key collision attack on DES in
+            their easy to understand paper,
+            <a href="http://homepage.eircom.net/~johnloughran/projects/computer/camDesAttackv4.pdf">
+            A Java Implemented Key Collision Attack on the
+            Data Encryption Standard (DES)</a>. Since attacks only get
+            better and the cost of storage is dropping rapidly, you are
+            urged to avoid using "fixed" IVs except when required for
+            backward compatibility. In particular, should never use
+            fixed IVs just to avoid the storage cost of storing a random
+            IV.
+            </FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.fixedIV</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#0000ff"><FONT SIZE=2>0x000102030405060708090a0b0c0d0e0f</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>A hex-encoded value to use as a fixed IV. Only
+			used if the property Encryptor.fixedIV is set to “fixed”. Intended
+            <I>only</I> for compatibility with legacy code. See the above
+            related above caution for <code>Encryptor.ChooseIVMethod</code>.
+            </FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.CipherText.useMAC</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>true</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Whether or not <code>CipherText</code>
+            should use a message authentication code (MAC) with
+            it. This prevents an adversary from altering the IV
+            as well as allowing a more fool-proof way of
+			determining the decryption failed because of an incorrect key
+			being supplied. This refers to the "separate" MAC
+			calculated and stored in <code>CipherText</code>, not
+            part of any MAC that is calculated as a result
+            of a "combined mode" cipher mode.</FONT></P>
+			<P><FONT SIZE=2>Note: If the cipher mode used is one specified in
+			the comma-separated list of cipher modes given in the property
+			<b>Encryptor.cipher_modes.combined_modes</b>, then a separate MAC
+            is <i>not</i> calculated for <code>CipherText</code> regardless of
+            the setting of this property. (Doing so would be
+            superfluous.)</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.PreferredJCEProvider</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2><i><empty string></i></FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Specifies the <i>preferred</i> JCE provider, that
+            is the JCE provider that is first looked at for JCE algorithms.
+            The <code>Encryptor</code> reference implementation,
+            <code>JavaEncryptor</code>, attempts to
+			load this JCE provider at position the first position when the
+            <code>JavaEncryptor</code> class is first loaded.
+            <br><br>
+            The value may either be a provider name (e.g., “BC” for
+            Bouncy Castle) or the fully qualified class name implementing
+            <code>java.security.Provider</code> for
+			the desired JCE provider. If left set to the empty string (the
+            default) or unset, the effect is to not change the preferred JCE
+            provider so that your application ends up using whatever your Java
+            VM is already using, which is generally determined by the settings
+            in your <code>$JAVA_HOME/jre/lib/security/java.security</code>
+            file.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.cipher_modes.additional_allowed</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>CBC</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Additional cipher modes allowed for ESAPI 2.0
+			symmetric encryption. These cipher modes are in <I>addition</I> to
+			those specified by the property
+			<b>Encryptor.cipher_modes.combined_modes</b>.</FONT></P>
+			<P><FONT SIZE=2>Note: We will add support for streaming modes like
+			CFB & OFB once we add support for 'specified' to the property
+			<b>Encryptor.ChooseIVMethod</b> (probably in ESAPI 2.1).</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.cipher_modes.combined_modes</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>GCM,CCM,IAPM,EAX,OCB,CWC</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Comma-separated list of cipher modes that provide
+			<B><I>both</I></B> confidentiality <B><I>and</I></B>
+            message authenticity. (NIST refers to such cipher
+            modes as "combined modes" so that's what we
+			shall call them.) If any of these cipher modes are used then no
+			MAC is calculated and stored in the <code>CipherText</code>
+            upon encryption.  Likewise, if one of these cipher
+            modes is used with decryption, no attempt will be
+            made to validate the MAC contained in the <code>CipherText</code>
+            object regardless of whether it contains one or not.
+			Since the expectation is that these cipher modes support support
+			message authenticity already, injecting a MAC in the
+            <code>CipherText</code> object would be at best redundant.</FONT>
+			</P>
+			<P><FONT SIZE=2>Note that as of JDK 1.5, the SunJCE provider does
+			not support <I>any</I> of these cipher modes. Of these listed,
+			only GCM and CCM are currently NIST approved.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.PlainText.overwrite</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>TRUE</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>Whether or not the plaintext bytes for the
+			PlainText object may be overwritten with “*” characters and
+			then marked eligible for garbage collection. If not set, this is
+			still treated as 'true'. If this is set to 'true', you will not be
+			able to use any PlainText object after you have used it with one
+			of the Encryptor encrypt() methods.</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.KDF.PDF</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>HmacSHA256</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>
+            This is the Pseudo Random Function (PRF) that ESAPI's Key
+            Derivation Function (KDF) normally uses. NSA wanted us to
+            support something stronger than HmacSHA1 (even though that
+            is considered fine for now--unless perhaps you are guarding
+            nuclear launch codes--and if so, your Java license probably
+            prohibits that ;-).
+			<BR /><BR />
+            (Note this is *only* the PRF used for ESAPI's KDF and *not*
+            what is used for ESAPI's MAC. Currently, HMacSHA1 is always
+            used for the MAC.)
+			<BR /><BR />
+            Currently supported choices for JDK 1.5 and 1.6 are:
+            HMacSHA1 (160 bits), HMacSHA256 (256 bits), HMacSHA384
+            (384 bits), and HMacSHA512 (512 bits).
+			<BR /><BR />
+            Note that HMacMD5 is *not* supported for the PRF
+            used by the KDF even though these JDKs support it.
+            ESAPI 2.0 release candidates prior to 2.0_rc11
+            ALWAYS used HMacSHA1. It is somewhat faster than
+            the SHA2 HMAC variations, but not significantly so.
+            <BR /><BR />
+            The only legitimate reason to tweak this would be to change it to
+            one of the new HMACs based on the future SHA-3 winner once it is
+            announced by NIST and supported in ESAPI and most JDKs. Until then,
+            don't meddle.
+			</FONT></P>
+		</TD>
+	</TR>
+	<TR VALIGN=TOP>
+		<TD WIDTH=249>
+			<PRE><FONT COLOR="#000000"><FONT SIZE=2>Encryptor.CharacterEncoding</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=202>
+			<PRE><FONT COLOR="#000000"><FONT SIZE=2>UTF-8</FONT></FONT></PRE>
+		</TD>
+		<TD WIDTH=226>
+			<P><FONT SIZE=2>The default encoding used for certain aspects such
+			as signing and sealing.</FONT></P>
+		</TD>
+	</TR>
+</TABLE>
+<H2>How the Old (Deprecated) Methods Were Used</H2>
+<P>To encrypt / decrypt using the String-based, deprecated methods
+carried over from ESAPI 1.4, code similar to the following would be
+used. 
+</P>
+<PRE>    String myplaintext = "My plaintext";
+    try {
+        String ciphertext = ESAPI.encryptor().encrypt(myplaintext);
+        String decrypted  = ESAPI.encryptor().decrypt(ciphertext);
+        assert decrypted.equals(myplaintext);
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate
+    }</PRE><P>
+This code will still work, however if you are using the standard
+(default) reference for <CODE>ESAPI.Encryptor</CODE>, which is
+<CODE>org.owasp.esapi.reference.crypto.JavaEncryptor</CODE>, the
+cipher transformation used with be that specified by the property
+<b>Encryptor.CipherTransformation</b> with a key size (when the
+algorithm supports a variable key size) of that specified by
+<b>Encryptor.EncryptionKeyLength</b> and the IV type specified
+by <b>Encryptor.ChooseIVMethod</b>. What is not provided by
+these methods (and why they are deprecated) is that they provide no
+mechanism to ensure message authenticity unless they are used with a
+so-called “combined” cipher mode such as CCM or GCM. <SPAN STYLE="font-style: normal">(Note
+that as of JDK 1.6, the default JCE provider, “SunJCE”, does not
+support any combined cipher modes.)</SPAN></P>
+<H2>Requirements for using ESAPI Symmetric Encryption</H2>
+<P>
+The parties participating in using ESAPI's symmetric encryption
+capabilities must agree on the following:
+<ol>
+<li>
+All parties must share the same encryption key(s). If multiple keys are
+used, each party must know which key to use for encryption / decryption.
+Parties must ensure that exchanging, storing, and all management
+of these encryption keys is done securely.  How this is done is
+presently outside the scope of ESAPI encryptions, but developers may
+find the advice in
+<a href="http://www.owasp.org/index.php/Cryptographic_Storage_Cheat_Sheet">
+OWASP's Cryptographic Storage Cheat Sheet</a> helpful in this regard.
+</li>
+<li>
+If parties are only exchanging raw ciphertext (plus IV, when appropriate),
+then they must also agree on the cipher transformation to use.
+</li>
+<li>
+If the involved parties participating in encryption are <i>not</i> using a
+"combined" cipher mode that provides both confidentiality and authenticity
+(for example, something like GCM or CCM), then they <i>must</i> whether or
+not there will be an additional MAC sent allow with the raw ciphertext and
+other cipher spec information in order to provide evidence of authenticity /
+data integrity. (See the property <b>Encryptor.CipherText.useMAC</b>
+for further details.) Failure to agree on this may allow an adversary to
+carry out certain chosen ciphertext attacks against their encrypted data
+resulting in (possibly complete) leakage of the corresponding plaintext.
+</li>
+</ol>
+<H2>Encrypting / Decrypting with the New Methods -- The Simple Usage</H2>
+<P>Using the new encryption / decryption methods is somewhat more
+complicated, but this is in part because they are more flexible and
+that flexibility means that more information needs to be communicated
+as to the details of the encryption. 
+</P>
+<P>A code snippet using the new methods that use the master
+encryption key would look something like this: 
+</P>
+<PRE>    String myplaintext = "My plaintext";
+    try {
+        CipherText ciphertext =
+            ESAPI.encryptor().encrypt( new PlainText(myplaintext) );
+        PlainText recoveredPlaintext = ESAPI.encryptor().decrypt(ciphertext) );
+        assert myplaintext.equals( recoveredPlaintext.toString() );
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }</PRE><P>
+Yes, this is a bit more complicated, but it will 1) work across
+different hardware platforms and operating systems whereas the older
+methods may not, and 2) it provides for <I>authenticity</I> <I><B>and</B></I>
+<I>confidentiality</I> of the ciphertext regardless of which cipher
+mode is chosen.</P>
+<P>Also, these new methods allow a general byte array to be
+encrypted, not just a Java String. If one needed to encrypt a byte
+array with the old deprecated method, one would first have to use 
+</P>
+<PRE>    byte[] plaintextByteArray = { /* byte array to be encrypted */ };
+    String plaintext = new String(plaintextByteArray, "UTF-8");</PRE><P>
+all the while catching the required <CODE>UnsupportedEncodingException</CODE>.
+For example, to handle this in ESAPI 1.4, one would have to write
+something like:</P>
+<PRE>
+    try {
+        byte[] plaintextByteArray = { /* byte array to be encrypted */ };
+        String myplaintext = new String(plaintextByteArray, "UTF-8");
+        String ciphertext = ESAPI.encryptor().encrypt(myplaintext);
+        String decrypted  = ESAPI.encryptor().decrypt(ciphertext);
+        byte[] recoveredBytes = decrypted.getBytes(“UFT-8”);
+        assert java.util.Arrays.equals( plaintextByteArray, recoveredBytes );
+    } catch( UnsupportedEncodingException ex) {
+        // Should not happen but need to catch and deal with it anyhow.
+        // Log error then return error designation however appropriate.
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }
+</PRE><P>
+However, dealing with this in ESAPI 2.0 is not any more cumbersome
+than dealing with Strings:</P>
+<PRE>
+    try {
+        byte[] plaintextByteArray = { /* byte array to be encrypted */ };
+        CipherText ciphertext =
+            ESAPI.encryptor().encrypt( new PlainText(plaintextByteArray) );
+        PlainText recoveredPlaintext = ESAPI.encryptor().decrypt(ciphertext) );
+
+        assert java.util.Arrays.equals( plaintextByteArray,
+                                        recoveredPlaintext.asBytes() );
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }
+</PRE><P>
+Ideally when you are encrypting sensitive data you do not want the
+plaintext sensitive data to be left lying around after it is
+encrypted. Instead, you should overwrite them after their value as
+been used. However, when you are using immutable Strings, this is not
+possible using native Java methods. But if you are able to pass in
+byte arrays that are passed directly to <CODE>PlainText</CODE>
+objects (as shown above), the <I>default</I> is to overwrite this
+after they are encrypted.
+<!-- Need to verify this. It was like this, but think I may have changed
+it to return a copy rather than a reference based on FindBugs report. -->
+<SPAN STYLE="background: #ffff00">(Note: Verify!) If the default for
+</SPAN><CODE><SPAN STYLE="background: #ffff00">Encryptor.PlainText.overwrite</SPAN></CODE><SPAN STYLE="background: #ffff00">
+of </SPAN><CODE><B><SPAN STYLE="background: #ffff00">true</SPAN></B></CODE><SPAN STYLE="background: #ffff00">
+had been used, then the array </SPAN><CODE><SPAN STYLE="background: #ffff00">plaintextByteArray</SPAN></CODE><SPAN STYLE="background: #ffff00">
+would have been overwritten with ASCII “*” characters.</SPAN></P>
+<H2>Encrypting / Decrypting with the New Methods – Storing Encrypted Data</H2>
+<P><SPAN STYLE="background: transparent">If you use one of the new
+Encryptor encrypt() / decrypt() methods, how do you persist the
+<code>CipherText</code> object returned by the encrypt() methods and how do
+you restore it to pass to the decrypt() method?</SPAN></P>
+<P><SPAN STYLE="background: transparent">The following example code
+snippet will illustrate this. In the following example we will simply
+write out the serialized <code>CipherText</code> object to a local file, but
+obviously you could hex- or base64-encode the serialized byte array
+and store it in a database or sent it in a SOAP XML message to a web
+service, etc.</SPAN></P>
+<PRE>    public class PersistedEncryptedData
+    {
+        public static int persistEncryptedData(PlainText plaintext,
+                                                String filename)
+            throws EncryptionException, IOException
+        {
+            File serializedFile = new File(filename);
+            serializedFile.delete(); // Delete any old serialized file.
+
+            CipherText ct = ESAPI.encryptor().encrypt(plaintext);
+            byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+
+            FileOutputStream fos = new FileOutputStream(serializedFile);
+            fos.write(serializedCiphertext);
+            fos.close();
+            return serializedCiphertext.length;
+        }
+
+        public static PlainText restorePlaintext(String encryptedDataFilename)
+            throws EncryptionException, IOException
+        {
+            File serializedFile = new File(encryptedDataFilename);
+            FileInputStream fis = new FileInputStream(serializedFile);
+            int avail = fis.available();
+            byte[] bytes = new byte[avail];
+            fis.read(bytes, 0, avail);
+
+            CipherText restoredCipherText =
+                                CipherText.fromPortableSerializedBytes(bytes);
+            fis.close();
+            PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
+            return plaintext;
+        }
+    }
+  </PRE>
+<H2>Advanced Usage</H2>
+<H3>Encrypting / Decrypting with the New Methods</H3>
+<P>ESAPI 1.4 and earlier only allowed you to use the master key
+(<FONT FACE="DejaVu Sans Mono, sans-serif">MasterPassword</FONT> in
+ESAPI 1.4; <CODE>Encryptor.MasterKey</CODE> in ESAPI 2.0) to encrypt
+and decrypt with. But encryption with a single key seldom is
+sufficient. For instance, lets say that your application has a need
+to encrypt both bank account numbers and credit card numbers. The
+encrypted bank account numbers are to be sent to one recipient and
+the encrypted credit card numbers are to be sent to a different
+recipient. Obviously in such cases, you do not want to share the same
+key for both recipients. 
+</P>
+<P>In ESAPI 1.4 there was not much you can do, but in ESAPI 2.0 and
+later, there are new encryption / decryption methods that allow you
+to specify a specific <CODE>SecretKey</CODE>. There is also a static
+helper method in <CODE>CryptoHelper</CODE> to allow you to generate a
+<CODE>SecretKey</CODE> of a specific type. (Distribution of this key
+is out of scope for this particular example, but for the moment, we
+will assume that secret keys are first generated, and then
+distributed to the recipients out-of-band. On you could distribute
+them dynamically via asymmetric encryption assuming that you've
+previously exchanged public keys with the recipients.)</P>
+<P>The following illustrates how these new methods might be used. 
+</P>
+<P>First, we would generate some appropriate secret keys and
+distribute them securely (e.g., perhaps over SSL/TLS) or exchange
+them earlier out-of-band to the intended recipients. (E.g., one could
+put them on two separate thumb drives and use a trusted courier to
+distribute them to the recipients or one could use PGP-mail or S/MIME
+to securely email them, etc.) 
+</P>
+<PRE>    // Generate two random, 128-bit AES keys to be distributed out-of-band.
+    import javax.crypto.SecretKey;
+    import org.owasp.esapi.crypto.CryptoHelper;
+    import org.owasp.esapi.codecs.Hex;
+
+    public class MySecretKeys {
+        public void main(String[] args) {
+          try {
+            SecretKey bankAcctKey = CryptoHelper.generateSecretKey("AES", 128);
+            SecretKey credCardKey = CryptoHelper.generateSecretKey("AES", 128);
+
+            System.out.println("Bank account key: " +
+                Hex.encode( bankAcctKey.getEncoding(), true ) );
+            System.out.println("Credit card key: " +
+                Hex.encode( credCardKey.getEncoding(), true ) );
+          } catch(Exception ex) {
+            ex.printStackTrace(System.err);
+            System.exit(1);
+          }
+          System.exit(0);
+        }
+    }</PRE><P>
+Second, these keys would be printed out and stored somewhere secure
+by our application, perhaps using something like ESAPI's
+<CODE>EncryptedProperties</CODE> class, where they could later be
+retrieved and used. 
+</P>
+<P>In the following code, we assume that the <CODE>SecretKey</CODE>
+values have already been initialized elsewhere. 
+</P>
+<PRE>    SecretKey bankAcctKey = ...;        // These might be read from EncryptedProperties
+    SecretKey credCardKey = ...;        // or from a restricted database, etc.
+    ...
+    String bankAccountNumber = ...;     // Assume obtained elsewhere
+    String creditCardNumber = ...;      // Ditto
+    ...
+    try {
+        // Encrypt each with their appropriate secret key
+        CipherText encryptedBankAcct =
+            ESAPI.encryptor().encrypt( bankAcctKey, new PlainText(bankAccountNumber) );
+        CipherText encryptedCreditCard =
+            ESAPI.encryptor().encrypt( credCardKey, new PlainText(creditCardNumber) );
+        ...
+        // Decrypt using appropriate secret key
+        PlainText recoveredBankAcct = ESAPI.encryptor().decrypt( bankAcctKey, encryptedBankAcct ) );
+        assert bankAccountNumber.equals( recoveredBankAcct );
+        ... etc. ...
+    } catch(EncryptionException ex) {
+        // Log error then return error designation however appropriate.
+    }</PRE>
+<H3>Using ESAPI with Multiple Cipher Transformations</H3>
+For the most part, the architecture of ESAPI assumes that you will
+stick to using the defaults in the <b>ESAPI.properties</b> configuration
+file or implment your own classes--possibly by extending the ESAPI
+reference classes--and use these classes instead of the reference
+classes.
+<p>
+For most things, this works well. Most applications likely can
+standardize on a single cipher transformation such as AES/CBC/PKCS5Padding
+with a 128-bit AES key and use that 100% of the time. However, on
+occassion, an application may need to use two separate cipher
+transformations (or even two different cipher algorithms) to
+handle legacy applications or deal with multiple partners.
+</p><p>
+This section discusses how to do this without implementing your own
+classes or extending the ESAPI reference class, <code>JavaEncryptor</code>.
+Note that it is recognized that this approach is somewhat of a kludge.
+A simpler approach is planned for ESAPI 2.1, but the approach shown
+here is workable even though it's not pretty.
+</p><p>
+If you find yourself in need of encrypting with a different cipher
+transformation, the first thing that you should count on is not
+using the same encryption key for each. While in some cases this
+likely would work (e.g., you are only using a different cipher mode
+or you have 256-bit AES key for "Encryptor.MasterKey" but also have
+a need to do encryption with a 128-bit AES key), it is not guaranteed
+to do so. Instead, you should count on generating a separate encryption
+key and using the encrypt / decrypt methods taking an additional
+<code>SecretKey</code> parameter as show in the previous section.
+</p>
+<p>
+Rather than repeating all the details of how to do this in this
+user guide, we encourage you to investigate how this was done in
+the Junit testing for the <code>JavaEncryptor</code> class. Please
+look at the source code for the private method
+<code>runNewEncryptDecryptTestCase(String, int, byte[])</code> in
+the <code>EncryptorTest</code> JUnit test in the source code
+"src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java".
+This code calls:
+<pre>
+		ESAPI.securityConfiguration().setCipherTransformation(cipherXform);
+</pre>
+which sets ESAPI to use the specified cipher transformation,
+<code>cipherXform</code>. As a convenience (for later restoral),
+it returns the previous cipher transformation.
+<p>
+There are also a few non-obvious key size adjustments that are also
+going on with DES and DESede (aka, triple DES) keys that are made there
+as well. This has to do with the fact that for DES keys (which
+includes DESede), the "true" key size differs from the "effective"
+key size. (E.g., in DES, the "true" key size--from the DES algorithms's
+perspective is 64-bits, however the <i>effective</i> key size for DES
+is only 56-bits because of the 8-bits of parity "imposed" by the NSA in
+the early 1970s.) This inconsistency manifests itself in the JCE by
+the fact that <code>KeyGenerator.init()</code> wants the <i>effective</i>
+key size to be specified (so 56-bits for DES, 112- or 168-bits for
+DESede), but <code>SecretKey.getEncoding().length</code> stores the
+"true" key size (e.g., 64-bits or 192-bits for DES and DESede respectively).
+Other cipher algorithms do not have this discrepancy between true and
+effective key sizes. Since a <code>SecretKey</code> is returned by the
+<code>KeyGenerator</code>, this is only all too confusing. The
+reference <code>JavaEncryptor</code> and its JUnit test,
+<code>EncryptorTest</code> attempt to deal with this discrepency.
+See the adjustments made to key size in
+<code>EncryptorTest.runNewEncryptDecryptTestCase()</code>
+for further details.
+</p>
+<H2>Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules</H2>
+If you wish to use ESAPI with a FIPS 140-2 validated, JCE-compliant
+cryptographic module such as the
+<a
+href="http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/1401val2005.htm#497"
+target="_blank">
+IBM Java JCE FIPS 140-2 Cryptographic Module</a>
+(hereafter referred to as IBMJCEFIPS) or the
+<a
+href="http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/1401val2008.htm#1048"
+target="_blank">
+RSA Security BSafe Crypto-J</a>
+(hereafter referred to as Crypto-J), or other similar products, you
+<i>must</i> follow these instructions. Failure to do so will mean that
+you will fail to meet FISMA compliance. (<b>Note</b>: The mention of these
+two specific vendor products does not constitute an endorsement from
+either OWASP or this author.)
+<br />
+<ol>
+<li>Follow the vendor's regular instructions to configure your application
+to use the vendor product in a FIPS 140-2 compliant manner. Generally this
+will include some special configuration and/or initialization requirements
+that must be followed to restrict the vendor software to use FIPS 140-2
+approved algorithms.
+<br /><br />
+If the vendor's instructions do not already include a recommendation to edit
+the <code>$JAVA_HOME/jre/lib/security/java.security</code> file, the OWASP
+team recommends that you do so to prevent you from accidentally using any
+other cryptographic module (such as SunJCE). To do this, you will need
+to change the property "security.provider.1" (which represents the default)
+provider so that it refers to the fully-qualified classname of
+your vendor's JCE <code>Provider</code> class. (By default, this is set to
+"sun.security.provider.Sun".) In fact, you may wish to go as far as to
+either comment out or remove the other providers to reduce the possibility
+that they will accidentally be used by your application.
+</li>
+<li>Edit the <b>ESAPI.properties</b> that your application is going to
+be using and set the property "Encryptor.PreferredJCEProvider" to
+the fully qualified classname of your vendor's FIPS 140-2 compliant
+JCE provider class. (If you are using a Java <code>SecurityManager</code>
+you may also need to grant
+<code>org.owasp.esapi.reference.crypto.JavaEncryptor</code> permissions
+to change the JCE provider.)
+</li>
+<li>Edit the <b>ESAPI.properties</b> that your application is going to
+be using and set the property <b>Encryptor.CipherText.useMAC</b> to
+<b><code>false</code></b>. This is critical as having this property
+set to <b><code>true</code></b> causes ESAPI to use <i>derived</i> keys
+for the actual encryption and MAC calculation, which is against FIPS 140-2
+compliance. (<b>Note</b>: This
+does not mean that OWASP believes that this key derivation is weak--its
+design has been suggested some cryptographers--but it would be creating and
+using a related key for encryption in a manner not reviewed by NIST
+and thus it is not acceptable to FIPS 140-2 approval.)
+</li>
+<li>Edit the <b>ESAPI.properties</b> that your application is going to
+be using and set the property "Encryptor.CipherTransformation" to
+a cipher transformation that is FIPS 140-2 certified for you vendor's
+software. OWASP recommends using a NIST "combined" cipher mode, that is
+one that provides for both message confidentiality <i>and</i> message
+authenticity if such a cipher mode is available from your vendor
+and FIPS 140-2 certified. (Cipher modes "GCM" and "CCM" are the only
+FIPS 140-2 approved as of this writing.)
+If such a "combined" cipher mode is not available, then use a cipher
+transformation like "AES/CBC/PKCS5Padding" with a random IV.
+This is almost always available as one of the FIPS 140-2 certified cipher
+transformations, however it will not provide you with message authenticity
+because to remain FIPS 140-2 compliant you will have had to disable the
+MAC calculation (via setting <b>Encryptor.CipherText.useMAC</b> to
+<b><code>false</code></b>) causing ESAPI to skip calculating an
+explicit MAC for you and hence providing no assurance of data integrity
+to the party attempting to decrypt your data. Without authenticity
+however, your encryption may still be vulnerable to the "padded oracle"
+chose ciphertext attack. Consult with your local cryptographic expert in
+such cases as this depends greatly on the circumstances of how you are
+using encryption.
+</li>
+<li>
+If your vendor softare requires you to explicitly to initialize their
+software for FIPS-mode (e.g., to cause the required FIPS 140-2 "power-on
+self tests" to run) by calling some software method, then this must be
+done <i>by your application code <b>before</b></i> calling any of the ESAPI
+crypto-related code. Specifically, it must be called <i>before</i>
+your application calls <code>ESAPI.encryptor()</code>.
+</li>
+</ol>
+<br />
+We believe that following these steps will still allow for your application
+to be FIPS 140-2 / FISMA compliant, however, as always, you should check
+with your FIPS auditor before using ESAPI in this manner. Should your
+auditors ask, feel free to point them to the ESAPI source code, which
+we believe will convince them that ESAPI is not a cryptographic module.
+(Rather, it only provides a wrapper to call other cryptographic modules
+through the standard JCE APIs.)
+<H2>Acknowledgments</H2>
+The OWASP ESAPI development team would like to acknowledge the contributions
+of cryptographers David A. Wagner and Ian Grigg. David provided the
+outline of how to securely compute derived keys for confidentiality
+and authenticity from a single master key and Ian convinced us
+to take a packetizing approach to laying out the portable, serialized
+<code>CipherText</code> object.
+<p>
+In addition, Kevin Kenan has agreed to review all the crypto code
+from ESAPI 2.0-rc5 or 2.0-rc6 candidate release. Others are invited
+to participate as well, especially those with a background in cryptography.
+(See <a href="http://www.owasp.org/index.php/Request_to_review_ESAPI_2.0_crypto">
+"Request to review ESAPI 2.0 crypto"</a> for details.)
+</p><p>
+I would also like to thank Jessica Fitzgerald-McKay and Andy Sampson of
+the Systems and Network Analysis Center of the NSA for their excellent feedback in
+response to the review request made in Google Issue #81. Working with bureaucracy
+is not something that I do particularly well, but Jessica and Andy made it as
+painless as it possibly could be. Their feedback, which at this point,
+unfortunately we are unable to quote, was generally favorable, but in the
+places where they had recommendations, these were very precise and
+helpful.
+</p><p>
+Lastly, I would like to thank Jeffrey Walton of Software Integrity, LLC.
+Jeff provided an extremely thorough analysis of ESAPI 2.0's (as of 2.0_rc10)
+Key Derivation Function (KDF). Jeff's analysis convinced me to bring ESAPI's
+KDF more in line with NIST's recommendations for KDFs as described in
+NIST Special Publication 800-108 (and specifically section 5.1). You can
+read about Jeff's review at
+<a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/Analysis-of-ESAPI-2.0-KDF.pdf">
+Analysis of ESAPI 2.0's Key Derivation Function
+</a>
+</p>
+</BODY>
+</HTML>
diff --git a/documentation/esapi4java-core-2.1-release-notes.txt b/documentation/esapi4java-core-2.1-release-notes.txt
new file mode 100644
index 0000000..3570d0f
--- /dev/null
+++ b/documentation/esapi4java-core-2.1-release-notes.txt
@@ -0,0 +1,68 @@
+ESAPI for Java - 2.1.0 Release Notes
+
+1) Fixed security issue #306, a vulnerability discovered by Phillipe Arteau.
+   This fix necessitated removing the deprecated encrypt() and decrupt() methods
+   that were intended to provide backward compatibility with ESAPI 1.4.
+   As it turns out, there was no way to fix this bug without a major rewrite
+   unless these methods were removed. However, as these two methods have been
+   deprecated more than 2 years ago and they are known to be insecure
+   (they are vulnerable to padding oracle attacks), the ESAPI team has
+   decided to remove them in accordance to their support policy.
+   
+   See comments for issue #306 for further details, as well as additional
+   safety precautions that you may wish to take in the unlikely, but possible
+   event that this vulnerability resulted in an actual security breach.
+
+   Finally, since the removal of these methods constitute an interface change
+   (to the Encryptor interface), this is considered a minor release (2.1)
+   rather than simply a patch release (2.0.2).
+
+   Please note that there are further updates planned to further strengthen
+   the MAC that ESAPI crypt uses. However, because they will require some
+   design changes, they may not be out for another month. Note that these
+   fixes do not correct any *known* vulnerabilities, but will address
+   some potential weaknesses in what is not included in the MAC (such as
+   the crypto version).
+
+2) Other Google Issues fixed: 257, 271, and 292 are all fixed in this release.
+
+3) Fixed Javadoc for Encoder.encryptForJavaScript(). [Revision r1879]
+
+4) DefaultEncryptedProperties - made minor Javadoc changes.
+
+5) The ESAPI 2.0 Encryptor.encrypt() methods now all throw an appropriate
+   IllegalArgumentException if any of the arguments are null. Previously,
+   if any of the arguments were null you would either get an AssertionError
+   (if you had assertions enabled) or a default NullPointerException when
+   assertions were disabled.  While IllegalArgumentException is still an
+   unchecked RuntimeException, note that if you were previously catching
+   NullPointerExceptions for these cases, you may need to change your code.
+
+6) The public constructor, CiphertextSerializer(CipherText ct), was changed
+   to explicitly check that the parameter is not null. Previously it had
+   checked with assertions which might later result in a NullPointerException
+   being thrown if assertions were disabled. Now if the parameter is null,
+   an appropriate IllegalArgumentException is thrown. This should not really
+   affect existing code (unless you are experimenting implementing your own
+   crypto) since user code should not really be using CiperTextSerializer
+   directly.
+
+7) Some of the setter methods in KeyDerivationFunction were changed to explicitly
+   check for invalid arguments and throw an IllegalArgumentException rather than
+   checking these parameters via assertions. This should not affect general
+   user code as most would not be calling the KeyDerivationFunction class
+   directly.
+
+8) Other miscellaneous minor code clean-up, mostly to remove compiler warnings.
+
+NOTE: A follow-up patch release is scheduled within the next few months to
+      address some questionable design decisions regarding what data in
+      the serialized ciphertext should be authenticated via the MAC. For
+      instance, presently only the IV+ciphertext is MAC'd (as would be the
+      equivalent case of when you would use an authenticated combined cipher
+      mode such as GCM or CCM). A deeper analysis of the design is required
+      based on findings in Google Issue # 306. I will periodically try
+      to keep the ESAPI mailing lists updated with the progress so watch
+      there for emerging details and anticipated schedule.
+      
+-Kevin W. Wall <kevin.w.wall at gmail.com>, 2013-08-30
diff --git a/documentation/esapi4java-disa.JPG b/documentation/esapi4java-disa.JPG
new file mode 100644
index 0000000..5578904
Binary files /dev/null and b/documentation/esapi4java-disa.JPG differ
diff --git a/documentation/esapi4java-google-code.JPG b/documentation/esapi4java-google-code.JPG
new file mode 100644
index 0000000..b2878c0
Binary files /dev/null and b/documentation/esapi4java-google-code.JPG differ
diff --git a/documentation/esapi4java-google-code.doc b/documentation/esapi4java-google-code.doc
new file mode 100644
index 0000000..67dca18
Binary files /dev/null and b/documentation/esapi4java-google-code.doc differ
diff --git a/documentation/esapi4java-waf-2.0-policy-file-spec.docx b/documentation/esapi4java-waf-2.0-policy-file-spec.docx
new file mode 100644
index 0000000..719f91f
Binary files /dev/null and b/documentation/esapi4java-waf-2.0-policy-file-spec.docx differ
diff --git a/documentation/esapi4java-waf-2.0-policy-file-spec.pdf b/documentation/esapi4java-waf-2.0-policy-file-spec.pdf
new file mode 100644
index 0000000..401d251
Binary files /dev/null and b/documentation/esapi4java-waf-2.0-policy-file-spec.pdf differ
diff --git a/documentation/esapi4java-waf.JPG b/documentation/esapi4java-waf.JPG
new file mode 100644
index 0000000..80a104b
Binary files /dev/null and b/documentation/esapi4java-waf.JPG differ
diff --git a/esapi.iml b/esapi.iml
new file mode 100644
index 0000000..5729ef2
--- /dev/null
+++ b/esapi.iml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.5" level="project" />
+    <orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2" level="project" />
+    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.3" level="project" />
+    <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.1" level="project" />
+    <orderEntry type="library" name="Maven: log4j:log4j:1.2.16" level="project" />
+    <orderEntry type="library" name="Maven: logkit:logkit:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: avalon-framework:avalon-framework:4.1.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: javax.servlet:servlet-api:2.4" level="project" />
+    <orderEntry type="library" name="Maven: commons-digester:commons-digester:1.8" level="project" />
+    <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.7.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils-core:1.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.4" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: javax.servlet:jsp-api:2.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.2" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: commons-io:commons-io:1.3" level="project" />
+    <orderEntry type="library" name="Maven: xom:xom:1.1" level="project" />
+    <orderEntry type="library" name="Maven: xerces:xmlParserAPIs:2.6.2" level="project" />
+    <orderEntry type="library" name="Maven: xerces:xercesImpl:2.6.2" level="project" />
+    <orderEntry type="library" name="Maven: xalan:xalan:2.7.0" level="project" />
+    <orderEntry type="library" name="Maven: xml-apis:xml-apis:1.0.b2" level="project" />
+    <orderEntry type="library" name="Maven: jaxen:jaxen:1.1-beta-8" level="project" />
+    <orderEntry type="library" name="Maven: dom4j:dom4j:1.6.1" level="project" />
+    <orderEntry type="library" name="Maven: jdom:jdom:1.0" level="project" />
+    <orderEntry type="library" name="Maven: org.beanshell:bsh-core:2.0b4" level="project" />
+    <orderEntry type="library" name="Maven: org.owasp.antisamy:antisamy:1.4.3" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-css:1.7" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-ext:1.7" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.xmlgraphics:batik-util:1.7" level="project" />
+    <orderEntry type="library" name="Maven: xml-apis:xml-apis-ext:1.3.04" level="project" />
+    <orderEntry type="library" name="Maven: net.sourceforge.nekohtml:nekohtml:1.9.12" level="project" />
+    <orderEntry type="library" name="Maven: commons-httpclient:commons-httpclient:3.1" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.2" level="project" />
+  </component>
+</module>
+
diff --git a/javadoc.xml b/javadoc.xml
new file mode 100644
index 0000000..daa7ba9
--- /dev/null
+++ b/javadoc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project default="javadoc">
+    <target name="javadoc">
+        <javadoc access="public" author="true" classpath="lib/xml-apis-ext.jar;lib/xml-apis.jar;lib/nekohtml.jar;lib/batik-util.jar;C:\eclipse\plugins\org.junit_3.8.2.v20080602-1318\junit.jar;lib/antisamy-bin.1.2.jar;lib/servlet-api.jar;lib/commons-fileupload-1.2.jar;lib/xercesImpl.jar;lib/batik-css.jar;lib/commons-io-1.3.2.jar;lib/jsp-api.jar" destdir="doc" doctitle="OWASP Enterprise Security API (ESAPI)" nodeprecated="false" nodeprecatedlist="false" noindex="false" nonavbar="false" not [...]
+    </target>
+</project>
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..e6ce765
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.owasp.esapi</groupId>
+    <artifactId>esapi</artifactId>
+    <version>2.1.0</version>
+    <packaging>jar</packaging>
+
+    <parent>
+        <groupId>org.sonatype.oss</groupId>
+        <artifactId>oss-parent</artifactId>
+        <version>5</version>
+    </parent>
+
+    <licenses>
+        <license>
+            <name>BSD</name>
+            <url>http://www.opensource.org/licenses/bsd-license.php</url>
+            <comments>Code License - New BSD License</comments>
+        </license>
+        <license>
+            <name>Creative Commons 3.0 BY-SA</name>
+            <url>http://creativecommons.org/licenses/by-sa/3.0/</url>
+            <comments>Content License - Create Commons 3.0 BY-SA</comments>
+        </license>
+    </licenses>
+
+    <name>ESAPI</name>
+    <url>http://www.esapi.org/</url>
+    <description>The Enterprise Security API (ESAPI) project is an OWASP project
+        to create simple strong security controls for every web platform.
+        Security controls are not simple to build. You can read about the
+        hundreds of pitfalls for unwary developers on the OWASP web site. By
+        providing developers with a set of strong controls, we aim to
+        eliminate some of the complexity of creating secure web applications.
+        This can result in significant cost savings across the SDLC.
+    </description>
+
+    <organization>
+        <name>The Open Web Application Security Project (OWASP)</name>
+        <url>http://www.owasp.org/index.php</url>
+    </organization>
+
+    <mailingLists>
+        <mailingList>
+            <name>ESAPI-Users</name>
+            <subscribe>https://lists.owasp.org/mailman/listinfo/esapi-user/</subscribe>
+            <unsubscribe>https://lists.owasp.org/mailman/listinfo/esapi-user/</unsubscribe>
+            <post>mailto:esapi-users at lists.owasp.org</post>
+            <archive>https://lists.owasp.org/pipermail/esapi-users/</archive>
+            <!--This is the OWASP ESAPI mailing list for ESAPI users, regardless of programming language. For example, ESAPI users with questions about ESAPI for Java or ESAPI for PHP would both post here.-->
+        </mailingList>
+        <mailingList>
+            <name>ESAPI-Developers</name>
+            <subscribe>https://lists.owasp.org/mailman/listinfo/esapi-dev/</subscribe>
+            <unsubscribe>https://lists.owasp.org/mailman/listinfo/esapi-dev/</unsubscribe>
+            <post>mailto:esapi-dev at lists.owasp.org</post>
+            <archive>https://lists.owasp.org/pipermail/esapi-dev/</archive>
+            <!--This is the OWASP ESAPI mailing list for ESAPI for Java developers. While the list is not closed, the topics of discussion are likely to be less relevant to those only using ESAPI. Note that this is the list for ESAPI for Java. Most other language implementations, such ESAPI for PHP, have their own mailing lists.-->
+        </mailingList>
+        <mailingList>
+            <name>OWASP-ESAPI (Inactive! Archive only!)</name>
+            <archive>https://lists.owasp.org/pipermail/owasp-esapi/</archive>
+            <!--The name of the obsolete mailing list that previously was a combination of an ESAPI users lists and ESAP development list. While obsolete, it is still sometimes useful for searching through old historical posts. NOTE: NEW POSTS SHOULD NO LONGER BE MADE TO THIS LIST!-->
+        </mailingList>
+    </mailingLists>
+
+    <scm>
+        <connection>scm:svn:http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0</connection>
+        <developerConnection>scm:svn:https://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0</developerConnection>
+        <url>http://code.google.com/p/owasp-esapi-java/source/checkout/tags/esapi-2.1.0</url>
+    </scm>
+
+    <issueManagement>
+        <system>Google Code Issue Tracking</system>
+        <url>http://code.google.com/p/owasp-esapi-java/issues/list</url>
+    </issueManagement>
+
+    <developers>
+        <developer>
+            <name>Jeff Williams</name>
+            <organization>Aspect Security</organization>
+            <roles>
+                <role>Project Owner</role>
+                <role>Architect</role>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Jim Manico</name>
+            <roles>
+                <role>Project Manager</role>
+                <role>BuildMaster</role>
+                <role>Developer</role>
+                <role>Architect</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Chris Schmidt</name>
+            <organization>Aspect Security</organization>
+            <roles>
+                <role>Project Manager</role>
+                <role>Continuous Integration Admin</role>
+                <role>Architect</role>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Kevin W. Wall</name>
+            <organization>Wells Fargo</organization>
+            <roles>
+                <role>Project Manager</role>
+                <role>Architect</role>
+                <role>Developer</role>
+                <role>Crypto Guy</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <contributors>
+        <contributor>
+            <name>Dave Wichers</name>
+            <organization>Aspect Security</organization>
+            <roles>
+                <role>Documentation and Examples</role>
+                <role>Minor Code Contributions</role>
+                <role>Project Guidance</role>
+            </roles>
+        </contributor>
+    </contributors>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>commons-configuration</groupId>
+            <artifactId>commons-configuration</artifactId>
+            <version>1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils-core</artifactId>
+            <version>1.7.0</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>jsp-api</artifactId>
+            <version>2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <!-- unused according to dependancy:analyze
+           this is a optional dependency of commons-fileupload which
+           is at least needed for testing...
+        -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.16</version>
+            <scope>compile</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>xom</groupId>
+            <artifactId>xom</artifactId>
+            <version>1.2.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.beanshell</groupId>
+            <artifactId>bsh-core</artifactId>
+            <version>2.0b4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.owasp.antisamy</groupId>
+            <artifactId>antisamy</artifactId>
+            <version>1.4.3</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                    <debug>true</debug>
+                    <showWarnings>true</showWarnings>
+                    <showDeprecation>false</showDeprecation>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-eclipse-plugin</artifactId>
+                <version>2.8</version>
+                <configuration>
+                    <downloadSources>true</downloadSources>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.3.1</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <!-- Check for updates to dependencies and report on them. -->
+<!--
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>versions-maven-plugin</artifactId>
+                <version>1.2</version>
+                <executions>
+                    <execution>
+                        <id>check-for-dependency-updates</id>
+                        <phase>compile</phase> <!– Eclipse q4e plugin needs this –>
+                        <goals>
+                            <goal>display-dependency-updates</goal>
+                            <goal>display-plugin-updates</goal>
+                            <goal>display-property-updates</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+-->
+        </plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>2.3.1</version>
+                <configuration>
+                    <findbugsXmlOutput>true</findbugsXmlOutput>
+                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+                    <xmlOutput>true</xmlOutput>
+                    <threshold>Low</threshold>
+                    <effort>Max</effort>
+                    <relaxed>false</relaxed>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <formats>
+                        <format>html</format>
+                        <format>xml</format>
+                    </formats>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jdepend-maven-plugin</artifactId>
+                <version>2.0-beta-2</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>2.5</version>
+                <configuration>
+                    <targetJdk>1.5</targetJdk>
+                    <sourceEncoding>utf-8</sourceEncoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.7</version>
+                <configuration>
+                    <detectJavaApiLink>false</detectJavaApiLink>
+                    <detectLinks>false</detectLinks>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>net.sourceforge.maven-taglib</groupId>
+                <artifactId>maven-taglib-plugin</artifactId>
+                <version>2.4</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-changelog-plugin</artifactId>
+                <version>2.2</version>
+                <configuration>
+                    <issueIDRegexPattern>[Ii]ssue[# ]*(\d)+</issueIDRegexPattern>
+                    <issueLinkUrl>http://code.google.com/p/owasp-esapi-java/issues/detail?id=%ISSUE%</issueLinkUrl>
+                    <type>date</type>
+                    <dates>
+                        <date>2011-05-11 00:00:00</date>
+                    </dates>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>versions-maven-plugin</artifactId>
+                <version>1.2</version>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>dependency-updates-report</report>
+                            <report>plugin-updates-report</report>
+                            <report>property-updates-report</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <profiles>
+        <profile>
+            <!-- Activate to sign jars and build distributable download. -->
+            <id>dist</id>
+
+            <!-- This profile is activated when mvn release:perform is called from the command line
+                 to actually do a release. If you need this profile active for some reason outside
+                 of performing a release, use mvn <command> -Pdist
+            -->
+            <activation>
+                <property>
+                    <name>performRelease</name>
+                    <value>true</value>
+                </property>
+            </activation>
+
+            <distributionManagement>
+                <snapshotRepository>
+                    <id>sonatype-nexus-snapshots</id>
+                    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+                </snapshotRepository>
+                <repository>
+                    <id>sonatype-nexus-staging</id>
+                    <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
+                </repository>
+            </distributionManagement>
+
+            <build>
+                <plugins>
+
+                    <!-- Signs all artifacts prior to deploying to maven central -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>deploy</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                                <configuration>
+                                    <keyname>9F460575</keyname>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+					
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-jar-plugin</artifactId>
+						<version>2.3.1</version>
+						<executions>
+							<execution>
+								<phase>package</phase>
+								<goals>
+									<goal>sign</goal>
+								</goals>
+							</execution>
+						</executions>
+						<configuration>
+							<keystore>codesign.keystore</keystore>
+							<alias>owasp foundation, inc.'s godaddy.com, inc. id</alias>
+							<verify>true</verify>
+							<archive>
+								<manifest>
+									<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+									<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+								</manifest>
+								<manifestEntries>
+									<Sealed>true</Sealed>
+								</manifestEntries>
+							</archive>
+						</configuration>
+					</plugin>
+					
+                    <!-- Baseline for ESAPI 2.0 is JDK1.5 - Enforces that a JDK1.5 compiler is being
+                         used to build this release -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>enforce-jdk-version</id>
+                                <goals>
+                                    <goal>enforce</goal>
+                                </goals>
+                                <configuration>
+                                    <rules>
+                                        <requireJavaVersion>
+                                            <version>1.5</version>
+                                            <message>
+                                                ESAPI 2.0 uses the JDK1.5 for it's baseline. Please make sure that your
+                                                JAVA_HOME environment variable is pointed to a JDK1.5 distribution.
+                                            </message>
+                                        </requireJavaVersion>
+                                    </rules>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Attached JavaDocs are required by Sonatype Nexus Repository -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>attach-javadocs</id>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- For building the distribution zip file. -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <version>2.2</version>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>src/main/assembly/dist.xml</descriptor>
+                            </descriptors>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>make-dist</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>single</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Performs a full release. See release documentation for information on how to
+                         perform an ESAPI release using Maven -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-release-plugin</artifactId>
+                        <version>2.1</version>
+                        <configuration>
+                            <tagBase>https://owasp-esapi-java.googlecode.com/svn/tags/releases/</tagBase>
+                        </configuration>
+                    </plugin>
+
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/resources/.svn/all-wcprops b/resources/.svn/all-wcprops
new file mode 100644
index 0000000..5af8218
--- /dev/null
+++ b/resources/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 45
+/svn/!svn/ver/1899/tags/esapi-2.1.0/resources
+END
+owasp-esapi-dev.jks
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/1899/tags/esapi-2.1.0/resources/owasp-esapi-dev.jks
+END
+settings.xml
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/1899/tags/esapi-2.1.0/resources/settings.xml
+END
diff --git a/resources/.svn/entries b/resources/.svn/entries
new file mode 100644
index 0000000..764e08b
--- /dev/null
+++ b/resources/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/resources
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-04-13T17:50:10.636123Z
+1237
+jonathan.ruckwood
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+owasp-esapi-dev.jks
+file
+
+
+
+
+2014-02-18T16:19:56.938045Z
+8b3f531310e4e4c8c437a39f8bad62be
+2010-04-13T17:50:10.636123Z
+1237
+jonathan.ruckwood
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1239
+

+settings.xml
+file
+
+
+
+
+2014-02-18T16:19:56.938045Z
+52630709cb27ccf9dfe5ac84585aecca
+2010-04-13T17:50:10.636123Z
+1237
+jonathan.ruckwood
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+677
+

diff --git a/resources/.svn/prop-base/owasp-esapi-dev.jks.svn-base b/resources/.svn/prop-base/owasp-esapi-dev.jks.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/resources/.svn/prop-base/owasp-esapi-dev.jks.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/resources/.svn/text-base/owasp-esapi-dev.jks.svn-base b/resources/.svn/text-base/owasp-esapi-dev.jks.svn-base
new file mode 100644
index 0000000..6e71f9d
Binary files /dev/null and b/resources/.svn/text-base/owasp-esapi-dev.jks.svn-base differ
diff --git a/resources/.svn/text-base/settings.xml.svn-base b/resources/.svn/text-base/settings.xml.svn-base
new file mode 100644
index 0000000..ba4fb8a
--- /dev/null
+++ b/resources/.svn/text-base/settings.xml.svn-base
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
+                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
+	<!-- Copy file or place following profile into your .m2/settings.xml -->
+    <profiles>
+		<profile>
+			<id>dist</id>
+			<properties>
+				<jarsign.keystore>${basedir}/resources/owasp-esapi-dev.jks</jarsign.keystore>
+                <jarsign.alias>owasp-esapi</jarsign.alias>
+                <jarsign.storepass>changeme</jarsign.storepass>
+			</properties>
+		</profile>
+	</profiles>
+</settings>
\ No newline at end of file
diff --git a/resources/owasp-esapi-dev.jks b/resources/owasp-esapi-dev.jks
new file mode 100644
index 0000000..6e71f9d
Binary files /dev/null and b/resources/owasp-esapi-dev.jks differ
diff --git a/resources/settings.xml b/resources/settings.xml
new file mode 100644
index 0000000..ba4fb8a
--- /dev/null
+++ b/resources/settings.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
+                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
+	<!-- Copy file or place following profile into your .m2/settings.xml -->
+    <profiles>
+		<profile>
+			<id>dist</id>
+			<properties>
+				<jarsign.keystore>${basedir}/resources/owasp-esapi-dev.jks</jarsign.keystore>
+                <jarsign.alias>owasp-esapi</jarsign.alias>
+                <jarsign.storepass>changeme</jarsign.storepass>
+			</properties>
+		</profile>
+	</profiles>
+</settings>
\ No newline at end of file
diff --git a/src/.svn/all-wcprops b/src/.svn/all-wcprops
new file mode 100644
index 0000000..82e7d35
--- /dev/null
+++ b/src/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 39
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src
+END
diff --git a/src/.svn/entries b/src/.svn/entries
new file mode 100644
index 0000000..1ffc38d
--- /dev/null
+++ b/src/.svn/entries
@@ -0,0 +1,40 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+main
+dir
+

+util
+dir
+

+examples
+dir
+

+test
+dir
+

diff --git a/src/examples/.svn/all-wcprops b/src/examples/.svn/all-wcprops
new file mode 100644
index 0000000..ba2f6e5
--- /dev/null
+++ b/src/examples/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 48
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples
+END
+README
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/README
+END
diff --git a/src/examples/.svn/entries b/src/examples/.svn/entries
new file mode 100644
index 0000000..d19b1c8
--- /dev/null
+++ b/src/examples/.svn/entries
@@ -0,0 +1,68 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/examples
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-02-04T05:19:08.157979Z
+1691
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+scripts
+dir
+

+README
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+d64d7fba899cb51687a256e3132ac035
+2011-02-04T05:15:25.345251Z
+1685
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1623
+

+java
+dir
+

diff --git a/src/examples/.svn/text-base/README.svn-base b/src/examples/.svn/text-base/README.svn-base
new file mode 100644
index 0000000..9421f88
--- /dev/null
+++ b/src/examples/.svn/text-base/README.svn-base
@@ -0,0 +1,37 @@
+README  - This file you are now reading.
+java    - Some Java examples.
+scripts - Some scripts to run that interact with the Java examples (but see
+          below). Purpose of script generally described at the beginning of
+          the file as a comment.
+
+
+
+NOTE:   All scripts are meant to be run under *nix (or using something like
+        Cygwin under Windows) from under the 'scripts' directory using either
+        bash or KornShell
+
+        E.g., to compile the Java examples, execute:
+
+                $ cd scripts
+                $ chmod u+x *.sh    # Required as some call each other.
+                $ # "Dot" the appropriate environment file. Either
+                $ #     . ./setenv-zip.sh
+                $ # if ESAPI downloaded via zip file or
+                $ #     . ./setenv-svn.sh
+                $ # if ESAPI downloaded from SVN repository. For the
+                $ # example of this README file, we will assume it was
+                $ # downloaded via the zip file.
+                $ . ./setenv-zip.sh
+                $ sh ./compile.sh
+
+        You MUST be in the 'scripts' directory and execute the scripts as
+        "sh ./scriptname" and NOT as "sh scripts/scriptname".
+
+
+        Note that as delivered, these scripts are configured to run from
+        Eclipse or Maven as pulled directly from the ESAPI Subversion
+        repository or from pulled down from the zip file on Google Code at:
+            http://code.google.com/p/owasp-esapi-java/downloads/list
+
+        If you get it from any other place, then all bets are off.
+                            -- Kevin Wall <kevin.w.wall at gmail.com>
diff --git a/src/examples/README b/src/examples/README
new file mode 100644
index 0000000..9421f88
--- /dev/null
+++ b/src/examples/README
@@ -0,0 +1,37 @@
+README  - This file you are now reading.
+java    - Some Java examples.
+scripts - Some scripts to run that interact with the Java examples (but see
+          below). Purpose of script generally described at the beginning of
+          the file as a comment.
+
+
+
+NOTE:   All scripts are meant to be run under *nix (or using something like
+        Cygwin under Windows) from under the 'scripts' directory using either
+        bash or KornShell
+
+        E.g., to compile the Java examples, execute:
+
+                $ cd scripts
+                $ chmod u+x *.sh    # Required as some call each other.
+                $ # "Dot" the appropriate environment file. Either
+                $ #     . ./setenv-zip.sh
+                $ # if ESAPI downloaded via zip file or
+                $ #     . ./setenv-svn.sh
+                $ # if ESAPI downloaded from SVN repository. For the
+                $ # example of this README file, we will assume it was
+                $ # downloaded via the zip file.
+                $ . ./setenv-zip.sh
+                $ sh ./compile.sh
+
+        You MUST be in the 'scripts' directory and execute the scripts as
+        "sh ./scriptname" and NOT as "sh scripts/scriptname".
+
+
+        Note that as delivered, these scripts are configured to run from
+        Eclipse or Maven as pulled directly from the ESAPI Subversion
+        repository or from pulled down from the zip file on Google Code at:
+            http://code.google.com/p/owasp-esapi-java/downloads/list
+
+        If you get it from any other place, then all bets are off.
+                            -- Kevin Wall <kevin.w.wall at gmail.com>
diff --git a/src/examples/java/.svn/all-wcprops b/src/examples/java/.svn/all-wcprops
new file mode 100644
index 0000000..45fa914
--- /dev/null
+++ b/src/examples/java/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/java
+END
+ESAPILogging.java
+K 25
+svn:wc:ra_dav:version-url
+V 71
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/java/ESAPILogging.java
+END
+PersistedEncryptedData.java
+K 25
+svn:wc:ra_dav:version-url
+V 81
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/java/PersistedEncryptedData.java
+END
+DisplayEncryptedProperties.java
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/java/DisplayEncryptedProperties.java
+END
diff --git a/src/examples/java/.svn/entries b/src/examples/java/.svn/entries
new file mode 100644
index 0000000..4678e37
--- /dev/null
+++ b/src/examples/java/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/examples/java
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-02-04T05:14:32.214099Z
+1684
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+ESAPILogging.java
+file
+
+
+
+
+2014-02-18T16:19:53.501977Z
+f3cae9b21a57fceaf89a3448bf61dd4a
+2011-02-04T05:14:32.214099Z
+1684
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+808
+

+PersistedEncryptedData.java
+file
+
+
+
+
+2014-02-18T16:19:53.501977Z
+1d4c6bd15fee4dcbcd724e5528df109f
+2010-08-24T03:41:19.667571Z
+1484
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7631
+

+DisplayEncryptedProperties.java
+file
+
+
+
+
+2014-02-18T16:19:53.501977Z
+df5995456a95cdc78366f3195df8021c
+2010-08-24T03:41:19.667571Z
+1484
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3035
+

diff --git a/src/examples/java/.svn/prop-base/DisplayEncryptedProperties.java.svn-base b/src/examples/java/.svn/prop-base/DisplayEncryptedProperties.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/examples/java/.svn/prop-base/DisplayEncryptedProperties.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/examples/java/.svn/prop-base/ESAPILogging.java.svn-base b/src/examples/java/.svn/prop-base/ESAPILogging.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/examples/java/.svn/prop-base/ESAPILogging.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/examples/java/.svn/prop-base/PersistedEncryptedData.java.svn-base b/src/examples/java/.svn/prop-base/PersistedEncryptedData.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/examples/java/.svn/prop-base/PersistedEncryptedData.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/examples/java/.svn/text-base/DisplayEncryptedProperties.java.svn-base b/src/examples/java/.svn/text-base/DisplayEncryptedProperties.java.svn-base
new file mode 100644
index 0000000..d71ac7d
--- /dev/null
+++ b/src/examples/java/.svn/text-base/DisplayEncryptedProperties.java.svn-base
@@ -0,0 +1,79 @@
+import java.io.*;
+import java.util.*;
+import org.owasp.esapi.EncryptedProperties;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.crypto.DefaultEncryptedProperties;
+
+// Purpose: Short code snippet to show how to display encrypted property files
+//          that were encrypted using ESAPI's EncryptedProperties class.
+//
+// Usage: java -classpath <cp> DisplayEncryptedProperties encryptedPropFileName
+//        where <cp> is proper classpath, which minimally include esapi.jar & log4j.jar
+public class DisplayEncryptedProperties {
+
+    public DisplayEncryptedProperties() {
+    }
+
+    public void loadProperties(String encryptedPropertiesFilename,
+                                  Properties props )
+        throws IOException
+    {
+         EncryptedProperties loader = new DefaultEncryptedProperties();
+         loader.load( new FileInputStream(
+                                    new File( encryptedPropertiesFilename) ) );
+
+         System.out.println("\n\nLoaded encrypted properties file...");
+
+         try {
+             props.setProperty( "database.driver",
+                                loader.getProperty( "database.driver" ) );
+             props.setProperty( "jdbc.url",
+                                loader.getProperty( "jdbc.url" ) );
+             props.setProperty( "jdbc.username",
+                                loader.getProperty( "jdbc.username" ) );
+             props.setProperty( "jdbc.password",
+                                loader.getProperty( "jdbc.password" ) );
+         } catch( EncryptionException ee ) {
+             ee.printStackTrace();
+         }
+    }
+
+    public void showProperties(Properties props) throws Exception
+    {
+        System.out.println("");
+        String value = null;
+        value = props.getProperty( "database.driver");
+        System.out.println("database.driver=" + value);
+        value = props.getProperty( "jdbc.url");
+        System.out.println("jdbc.url=" + value);
+        value = props.getProperty( "jdbc.username");
+        System.out.println("jdbc.username=" + value);
+        value = props.getProperty( "jdbc.password");
+        System.out.println("jdbc.password=" + value);
+    }
+
+
+    public static void main(String[] args) {
+
+        try {
+            DisplayEncryptedProperties dep = new DisplayEncryptedProperties();
+            Properties props = new Properties();
+
+            String encryptedPropFname = "encrypted.properties";
+            if ( args.length == 1 ) {
+                encryptedPropFname = args[0];
+            } else {
+                System.err.println("Usage: java -classpath <cp> DisplayEncryptedProperties encryptedPropFileName");
+                System.exit(2);
+            }
+
+            dep.loadProperties(encryptedPropFname, props);
+            dep.showProperties(props);
+
+        } catch(Throwable t) {
+            System.err.println("Caught: " + t.getClass().getName() +
+                               "; exception msg: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/src/examples/java/.svn/text-base/ESAPILogging.java.svn-base b/src/examples/java/.svn/text-base/ESAPILogging.java.svn-base
new file mode 100644
index 0000000..338e77e
--- /dev/null
+++ b/src/examples/java/.svn/text-base/ESAPILogging.java.svn-base
@@ -0,0 +1,23 @@
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+// Purpose: Short code snippet to show how ESAPI logging works.
+//
+// Usage: java -classpath <cp> ESAPILogging
+//        where <cp> is proper classpath, which minimally include esapi.jar & log4j.jar
+public class ESAPILogging {
+
+    public static void main(String[] args) {
+
+        try {
+            Logger logger = ESAPI.getLogger("ESAPILogging");
+            
+            logger.warning(Logger.SECURITY_FAILURE, "This is a warning.");
+            logger.always(Logger.SECURITY_AUDIT, "This is an audit log. It always logs.");
+        } catch(Throwable t) {
+            System.err.println("Caught: " + t.getClass().getName() +
+                               "; exception msg: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/src/examples/java/.svn/text-base/PersistedEncryptedData.java.svn-base b/src/examples/java/.svn/text-base/PersistedEncryptedData.java.svn-base
new file mode 100644
index 0000000..a034d0a
--- /dev/null
+++ b/src/examples/java/.svn/text-base/PersistedEncryptedData.java.svn-base
@@ -0,0 +1,185 @@
+import java.io.*;
+import org.owasp.esapi.*;
+import org.owasp.esapi.crypto.*;
+import org.owasp.esapi.errors.*;
+import org.owasp.esapi.codecs.*;
+import javax.servlet.ServletRequest;
+import org.apache.log4j.Logger;
+
+/** A slightly more complex example showing encoding encrypted data and writing
+ *  it out to a file. This is very similar to the example in the ESAPI User
+ *  Guide for "Symmetric Encryption in ESAPI 2.0".
+ */
+public class PersistedEncryptedData
+{
+    public enum OutputEncoding { raw, base64, hex }
+
+    private static final OutputEncoding dfltEncoding = OutputEncoding.raw;
+
+    private static boolean useBase64(OutputEncoding encoding) {
+        if ( encoding.equals(OutputEncoding.base64) ) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static boolean useHex(OutputEncoding encoding) {
+        if ( encoding.equals(OutputEncoding.hex) ) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /** Take the specified plaintext, encrypt it, and then persist it
+     *  to the specified file name according to the specified encoding.
+     *
+     * @param plaintext The {@code PlainText} we wish to encrypt.
+     * @param filemane  Name of the file in which to store the encrypted, encoded data.
+     * @param encoding  How it was encoded. Either base64, hex, or raw (meaning
+     *                  no encoding was used).
+     * @returns
+     * @throws EncryptionException
+     * @throws IOException
+     * @throws UnsupportedEncodingException
+     */
+    public static int persistEncryptedData(PlainText plaintext,
+                                            String filename,
+                                            OutputEncoding encoding)
+        throws EncryptionException, IOException, UnsupportedEncodingException
+    {
+        File serializedFile = new File(filename);
+        serializedFile.delete(); // Delete any old serialized file.
+
+        CipherText ct = ESAPI.encryptor().encrypt(plaintext);
+        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+        String encodedStr = null;
+        byte[] serializedBytes = null;
+
+        if ( useBase64(encoding) ) {
+            encodedStr = Base64.encodeBytes(serializedCiphertext);
+            serializedBytes = encodedStr.getBytes("UTF-8");
+        } else if ( useHex(encoding) ) {
+            encodedStr = Hex.encode(serializedCiphertext, true);
+            serializedBytes = encodedStr.getBytes("UTF-8");
+        } else {    // raw encoding
+            serializedBytes = serializedCiphertext;
+        }
+
+        FileOutputStream fos = new FileOutputStream(serializedFile);
+        fos.write( serializedBytes );
+        fos.close();
+        return serializedBytes.length;
+    }
+ 
+    /** Read the specified file name containing encoded encrypted data,
+     *  and then decode it and decrypt it to retrieve the original plaintext.
+     *
+     * @param encryptedDataFilename Name of the file to read containing the
+     *                  encoded, encrypted data.
+     * @param encoding  How it was encoded. Either base64, hex, or raw (meaning
+     *                  no encoding was used).
+     * @returns     The original {@code PlainText} object.
+     * @throws EncryptionException
+     * @throws IOException
+     * @throws UnsupportedEncodingException
+     */
+    public static PlainText restorePlaintext(String encryptedDataFilename,
+                                             OutputEncoding encoding)
+        throws EncryptionException, IOException, UnsupportedEncodingException
+    {
+        File serializedFile = new File(encryptedDataFilename);
+        FileInputStream fis = new FileInputStream(serializedFile);
+        int avail = fis.available();
+        byte[] bytes = new byte[avail];
+        fis.read(bytes, 0, avail);
+        String encodedEncryptedData = new String(bytes, "UTF-8");
+
+        byte[] serializedCiphertext;
+
+        if ( useBase64(encoding) ) {
+            serializedCiphertext = Base64.decode(encodedEncryptedData);
+        } else if ( useHex(encoding) ) {
+            serializedCiphertext = Hex.decode(encodedEncryptedData);
+        } else {
+            // Raw encoding
+            serializedCiphertext = bytes;
+        }
+        System.out.println("Serialized ciphertext is " + serializedCiphertext.length +
+                           " bytes.");
+
+        CipherText restoredCipherText =
+                            CipherText.fromPortableSerializedBytes(serializedCiphertext);
+        fis.close();
+        PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
+        return plaintext;
+    }
+
+    /**
+     * Usage: PersistedEncryptedData plaintext_string output_filename {raw|base64|hex}
+     */
+    public static void main(String[] args) {
+
+        try {
+            String plaintext = null;
+            String filename  = null;
+            OutputEncoding encoding = dfltEncoding;
+
+            // NOTE: Ordinally, we would write to System.out for informational
+            //       messages, but if something is not working (e.g., your
+            //       classpath is missing a jar, etc.), it makes it a bit
+            //       harder to debug because System.out is buffered and thus
+            //       may not appear when exceptions are thrown.
+
+System.err.println("args.length=" + args.length);
+
+            if ( args.length == 3 ) {
+                plaintext = args[0];
+                filename  = args[1];
+                if ( args[2].equalsIgnoreCase("raw") ) {
+                    encoding = OutputEncoding.raw;
+                } else if ( args[2].equalsIgnoreCase("base64") ) {
+                    encoding = OutputEncoding.base64;
+                } else if ( args[2].equalsIgnoreCase("hex") ) {
+                    encoding = OutputEncoding.hex;
+                } else {
+                    System.err.println(args[2] + ": Unrecognized encoding; using default.");
+                    encoding = dfltEncoding;
+                }
+            } else {
+                System.err.println("Usage: java -classpath <cp> PersistedEncryptedData " +
+                                   "plaintext_string " +
+                                   "output_filename {raw|base64|hex}");
+                System.exit(2);
+            }
+
+            // Add file suffix, appropriate to encoding
+            filename = filename + "." + encoding;
+
+            System.err.println("Encrypting " + plaintext.length() +
+                               " bytes of plaintext and storing in file '" +
+                               filename + "'.");
+
+            int n = PersistedEncryptedData.persistEncryptedData(
+                                                    new PlainText(plaintext),
+                                                    filename, encoding);
+
+            System.err.println("Wrote " + n + " bytes to encrypted file " + filename + ".");
+            File f = new File(filename);
+            PlainText pt = PersistedEncryptedData.restorePlaintext(filename, encoding);
+
+            System.err.println("Plaintext recovered from encrypted file was: " + pt);
+            if ( pt.toString().equals( plaintext ) ) {
+                System.out.println("Plaintext recovered successfully.");
+                System.out.println("Recovered plaintext: " + pt);
+            } else {
+                System.out.println("Recovered plaintext differs from original plaintext.");
+            }
+        } catch(Throwable t) {
+            System.err.println("Caught: " + t.getClass().getName() +
+                               "; exception msg: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/src/examples/java/DisplayEncryptedProperties.java b/src/examples/java/DisplayEncryptedProperties.java
new file mode 100644
index 0000000..d71ac7d
--- /dev/null
+++ b/src/examples/java/DisplayEncryptedProperties.java
@@ -0,0 +1,79 @@
+import java.io.*;
+import java.util.*;
+import org.owasp.esapi.EncryptedProperties;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.crypto.DefaultEncryptedProperties;
+
+// Purpose: Short code snippet to show how to display encrypted property files
+//          that were encrypted using ESAPI's EncryptedProperties class.
+//
+// Usage: java -classpath <cp> DisplayEncryptedProperties encryptedPropFileName
+//        where <cp> is proper classpath, which minimally include esapi.jar & log4j.jar
+public class DisplayEncryptedProperties {
+
+    public DisplayEncryptedProperties() {
+    }
+
+    public void loadProperties(String encryptedPropertiesFilename,
+                                  Properties props )
+        throws IOException
+    {
+         EncryptedProperties loader = new DefaultEncryptedProperties();
+         loader.load( new FileInputStream(
+                                    new File( encryptedPropertiesFilename) ) );
+
+         System.out.println("\n\nLoaded encrypted properties file...");
+
+         try {
+             props.setProperty( "database.driver",
+                                loader.getProperty( "database.driver" ) );
+             props.setProperty( "jdbc.url",
+                                loader.getProperty( "jdbc.url" ) );
+             props.setProperty( "jdbc.username",
+                                loader.getProperty( "jdbc.username" ) );
+             props.setProperty( "jdbc.password",
+                                loader.getProperty( "jdbc.password" ) );
+         } catch( EncryptionException ee ) {
+             ee.printStackTrace();
+         }
+    }
+
+    public void showProperties(Properties props) throws Exception
+    {
+        System.out.println("");
+        String value = null;
+        value = props.getProperty( "database.driver");
+        System.out.println("database.driver=" + value);
+        value = props.getProperty( "jdbc.url");
+        System.out.println("jdbc.url=" + value);
+        value = props.getProperty( "jdbc.username");
+        System.out.println("jdbc.username=" + value);
+        value = props.getProperty( "jdbc.password");
+        System.out.println("jdbc.password=" + value);
+    }
+
+
+    public static void main(String[] args) {
+
+        try {
+            DisplayEncryptedProperties dep = new DisplayEncryptedProperties();
+            Properties props = new Properties();
+
+            String encryptedPropFname = "encrypted.properties";
+            if ( args.length == 1 ) {
+                encryptedPropFname = args[0];
+            } else {
+                System.err.println("Usage: java -classpath <cp> DisplayEncryptedProperties encryptedPropFileName");
+                System.exit(2);
+            }
+
+            dep.loadProperties(encryptedPropFname, props);
+            dep.showProperties(props);
+
+        } catch(Throwable t) {
+            System.err.println("Caught: " + t.getClass().getName() +
+                               "; exception msg: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/src/examples/java/ESAPILogging.java b/src/examples/java/ESAPILogging.java
new file mode 100644
index 0000000..338e77e
--- /dev/null
+++ b/src/examples/java/ESAPILogging.java
@@ -0,0 +1,23 @@
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+// Purpose: Short code snippet to show how ESAPI logging works.
+//
+// Usage: java -classpath <cp> ESAPILogging
+//        where <cp> is proper classpath, which minimally include esapi.jar & log4j.jar
+public class ESAPILogging {
+
+    public static void main(String[] args) {
+
+        try {
+            Logger logger = ESAPI.getLogger("ESAPILogging");
+            
+            logger.warning(Logger.SECURITY_FAILURE, "This is a warning.");
+            logger.always(Logger.SECURITY_AUDIT, "This is an audit log. It always logs.");
+        } catch(Throwable t) {
+            System.err.println("Caught: " + t.getClass().getName() +
+                               "; exception msg: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/src/examples/java/PersistedEncryptedData.java b/src/examples/java/PersistedEncryptedData.java
new file mode 100644
index 0000000..a034d0a
--- /dev/null
+++ b/src/examples/java/PersistedEncryptedData.java
@@ -0,0 +1,185 @@
+import java.io.*;
+import org.owasp.esapi.*;
+import org.owasp.esapi.crypto.*;
+import org.owasp.esapi.errors.*;
+import org.owasp.esapi.codecs.*;
+import javax.servlet.ServletRequest;
+import org.apache.log4j.Logger;
+
+/** A slightly more complex example showing encoding encrypted data and writing
+ *  it out to a file. This is very similar to the example in the ESAPI User
+ *  Guide for "Symmetric Encryption in ESAPI 2.0".
+ */
+public class PersistedEncryptedData
+{
+    public enum OutputEncoding { raw, base64, hex }
+
+    private static final OutputEncoding dfltEncoding = OutputEncoding.raw;
+
+    private static boolean useBase64(OutputEncoding encoding) {
+        if ( encoding.equals(OutputEncoding.base64) ) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static boolean useHex(OutputEncoding encoding) {
+        if ( encoding.equals(OutputEncoding.hex) ) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /** Take the specified plaintext, encrypt it, and then persist it
+     *  to the specified file name according to the specified encoding.
+     *
+     * @param plaintext The {@code PlainText} we wish to encrypt.
+     * @param filemane  Name of the file in which to store the encrypted, encoded data.
+     * @param encoding  How it was encoded. Either base64, hex, or raw (meaning
+     *                  no encoding was used).
+     * @returns
+     * @throws EncryptionException
+     * @throws IOException
+     * @throws UnsupportedEncodingException
+     */
+    public static int persistEncryptedData(PlainText plaintext,
+                                            String filename,
+                                            OutputEncoding encoding)
+        throws EncryptionException, IOException, UnsupportedEncodingException
+    {
+        File serializedFile = new File(filename);
+        serializedFile.delete(); // Delete any old serialized file.
+
+        CipherText ct = ESAPI.encryptor().encrypt(plaintext);
+        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+        String encodedStr = null;
+        byte[] serializedBytes = null;
+
+        if ( useBase64(encoding) ) {
+            encodedStr = Base64.encodeBytes(serializedCiphertext);
+            serializedBytes = encodedStr.getBytes("UTF-8");
+        } else if ( useHex(encoding) ) {
+            encodedStr = Hex.encode(serializedCiphertext, true);
+            serializedBytes = encodedStr.getBytes("UTF-8");
+        } else {    // raw encoding
+            serializedBytes = serializedCiphertext;
+        }
+
+        FileOutputStream fos = new FileOutputStream(serializedFile);
+        fos.write( serializedBytes );
+        fos.close();
+        return serializedBytes.length;
+    }
+ 
+    /** Read the specified file name containing encoded encrypted data,
+     *  and then decode it and decrypt it to retrieve the original plaintext.
+     *
+     * @param encryptedDataFilename Name of the file to read containing the
+     *                  encoded, encrypted data.
+     * @param encoding  How it was encoded. Either base64, hex, or raw (meaning
+     *                  no encoding was used).
+     * @returns     The original {@code PlainText} object.
+     * @throws EncryptionException
+     * @throws IOException
+     * @throws UnsupportedEncodingException
+     */
+    public static PlainText restorePlaintext(String encryptedDataFilename,
+                                             OutputEncoding encoding)
+        throws EncryptionException, IOException, UnsupportedEncodingException
+    {
+        File serializedFile = new File(encryptedDataFilename);
+        FileInputStream fis = new FileInputStream(serializedFile);
+        int avail = fis.available();
+        byte[] bytes = new byte[avail];
+        fis.read(bytes, 0, avail);
+        String encodedEncryptedData = new String(bytes, "UTF-8");
+
+        byte[] serializedCiphertext;
+
+        if ( useBase64(encoding) ) {
+            serializedCiphertext = Base64.decode(encodedEncryptedData);
+        } else if ( useHex(encoding) ) {
+            serializedCiphertext = Hex.decode(encodedEncryptedData);
+        } else {
+            // Raw encoding
+            serializedCiphertext = bytes;
+        }
+        System.out.println("Serialized ciphertext is " + serializedCiphertext.length +
+                           " bytes.");
+
+        CipherText restoredCipherText =
+                            CipherText.fromPortableSerializedBytes(serializedCiphertext);
+        fis.close();
+        PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
+        return plaintext;
+    }
+
+    /**
+     * Usage: PersistedEncryptedData plaintext_string output_filename {raw|base64|hex}
+     */
+    public static void main(String[] args) {
+
+        try {
+            String plaintext = null;
+            String filename  = null;
+            OutputEncoding encoding = dfltEncoding;
+
+            // NOTE: Ordinally, we would write to System.out for informational
+            //       messages, but if something is not working (e.g., your
+            //       classpath is missing a jar, etc.), it makes it a bit
+            //       harder to debug because System.out is buffered and thus
+            //       may not appear when exceptions are thrown.
+
+System.err.println("args.length=" + args.length);
+
+            if ( args.length == 3 ) {
+                plaintext = args[0];
+                filename  = args[1];
+                if ( args[2].equalsIgnoreCase("raw") ) {
+                    encoding = OutputEncoding.raw;
+                } else if ( args[2].equalsIgnoreCase("base64") ) {
+                    encoding = OutputEncoding.base64;
+                } else if ( args[2].equalsIgnoreCase("hex") ) {
+                    encoding = OutputEncoding.hex;
+                } else {
+                    System.err.println(args[2] + ": Unrecognized encoding; using default.");
+                    encoding = dfltEncoding;
+                }
+            } else {
+                System.err.println("Usage: java -classpath <cp> PersistedEncryptedData " +
+                                   "plaintext_string " +
+                                   "output_filename {raw|base64|hex}");
+                System.exit(2);
+            }
+
+            // Add file suffix, appropriate to encoding
+            filename = filename + "." + encoding;
+
+            System.err.println("Encrypting " + plaintext.length() +
+                               " bytes of plaintext and storing in file '" +
+                               filename + "'.");
+
+            int n = PersistedEncryptedData.persistEncryptedData(
+                                                    new PlainText(plaintext),
+                                                    filename, encoding);
+
+            System.err.println("Wrote " + n + " bytes to encrypted file " + filename + ".");
+            File f = new File(filename);
+            PlainText pt = PersistedEncryptedData.restorePlaintext(filename, encoding);
+
+            System.err.println("Plaintext recovered from encrypted file was: " + pt);
+            if ( pt.toString().equals( plaintext ) ) {
+                System.out.println("Plaintext recovered successfully.");
+                System.out.println("Recovered plaintext: " + pt);
+            } else {
+                System.out.println("Recovered plaintext differs from original plaintext.");
+            }
+        } catch(Throwable t) {
+            System.err.println("Caught: " + t.getClass().getName() +
+                               "; exception msg: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/src/examples/scripts/.svn/all-wcprops b/src/examples/scripts/.svn/all-wcprops
new file mode 100644
index 0000000..9628a4b
--- /dev/null
+++ b/src/examples/scripts/.svn/all-wcprops
@@ -0,0 +1,59 @@
+K 25
+svn:wc:ra_dav:version-url
+V 56
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts
+END
+runClass.sh
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/runClass.sh
+END
+findjar.sh
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/findjar.sh
+END
+encrypted.properties
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/encrypted.properties
+END
+setMasterKey.sh
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/setMasterKey.sh
+END
+encryptProperties.sh
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/encryptProperties.sh
+END
+persistEncryptedData.sh
+K 25
+svn:wc:ra_dav:version-url
+V 80
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/persistEncryptedData.sh
+END
+setenv-zip.sh
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/setenv-zip.sh
+END
+compile.sh
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/compile.sh
+END
+setenv-svn.sh
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/examples/scripts/setenv-svn.sh
+END
diff --git a/src/examples/scripts/.svn/entries b/src/examples/scripts/.svn/entries
new file mode 100644
index 0000000..83d030f
--- /dev/null
+++ b/src/examples/scripts/.svn/entries
@@ -0,0 +1,334 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/examples/scripts
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-02-04T05:19:08.157979Z
+1691
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+encryptProperties.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+7d9db95f7f2b503769f5bf0542be740d
+2011-02-04T05:18:45.563950Z
+1690
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2923
+

+persistEncryptedData.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+adb65b587593815dbd90c075a41a4f74
+2011-02-04T05:18:25.392179Z
+1689
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1298
+

+setenv-zip.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+1925b891cec59f68d2294cae01a9cba6
+2011-02-04T05:17:20.632590Z
+1688
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2212
+

+compile.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+9d39ad11c663e13ef709048bb469b3aa
+2010-08-24T03:42:03.826847Z
+1485
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+433
+

+setenv-svn.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+c7732774bddca3bb34e8c21710e00060
+2011-02-04T05:16:58.255340Z
+1687
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2212
+

+runClass.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+4636d3f1792828cc45df02063d8c6857
+2011-02-04T05:16:01.302804Z
+1686
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+886
+

+findjar.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+3ebf831e9d02a56e1860fb8afdcc3abc
+2010-03-01T03:49:40.523041Z
+1199
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+953
+

+encrypted.properties
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+0c20cdc3ef949c3141f68d2def8c062b
+2010-08-24T03:42:03.826847Z
+1485
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+800
+

+setMasterKey.sh
+file
+
+
+
+
+2014-02-18T16:19:53.509977Z
+eee0469715b6187894e5a1d217ce9848
+2011-02-04T05:19:08.157979Z
+1691
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+790
+

diff --git a/src/examples/scripts/.svn/prop-base/compile.sh.svn-base b/src/examples/scripts/.svn/prop-base/compile.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/compile.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/prop-base/encryptProperties.sh.svn-base b/src/examples/scripts/.svn/prop-base/encryptProperties.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/encryptProperties.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/prop-base/encrypted.properties.svn-base b/src/examples/scripts/.svn/prop-base/encrypted.properties.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/encrypted.properties.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/examples/scripts/.svn/prop-base/findjar.sh.svn-base b/src/examples/scripts/.svn/prop-base/findjar.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/findjar.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/prop-base/persistEncryptedData.sh.svn-base b/src/examples/scripts/.svn/prop-base/persistEncryptedData.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/persistEncryptedData.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/prop-base/runClass.sh.svn-base b/src/examples/scripts/.svn/prop-base/runClass.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/runClass.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/prop-base/setMasterKey.sh.svn-base b/src/examples/scripts/.svn/prop-base/setMasterKey.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/setMasterKey.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/prop-base/setenv-svn.sh.svn-base b/src/examples/scripts/.svn/prop-base/setenv-svn.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/setenv-svn.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/prop-base/setenv-zip.sh.svn-base b/src/examples/scripts/.svn/prop-base/setenv-zip.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/examples/scripts/.svn/prop-base/setenv-zip.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/examples/scripts/.svn/text-base/compile.sh.svn-base b/src/examples/scripts/.svn/text-base/compile.sh.svn-base
new file mode 100644
index 0000000..95203d7
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/compile.sh.svn-base
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+cd ../java
+set -x
+javac -classpath "$esapi_classpath" ${1:-*.java}
diff --git a/src/examples/scripts/.svn/text-base/encryptProperties.sh.svn-base b/src/examples/scripts/.svn/text-base/encryptProperties.sh.svn-base
new file mode 100644
index 0000000..bc60683
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/encryptProperties.sh.svn-base
@@ -0,0 +1,73 @@
+#!/bin/bash
+# Create or display encrypted properties.
+#
+# Usage: encryptedProperties.sh {-display|-create} [encrypted_properties_filename]
+#        You can use the file 'encrypted.properties' in this directory
+#        as a sample if you so wish. That's what it defaults to.
+
+USAGE="Usage: encryptedProperties.sh {-display|-create} [encrypted_properties_filename]"
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+case $1 in
+-display|-create)   action="$1" ;;
+*)  echo "Missing '-display' or '-create' arg."; echo $USAGE; exit 2    ;;
+esac
+
+filename=${2:-encrypted.properties}
+case "$filename" in
+/*) ;;
+*)   filename="$PWD/$filename"    ;;
+esac
+
+if [[ -f "$filename" && "$action" == "-create" ]]
+then    echo "Output file '$filename' already exists; will not overwrite."
+        echo "Remove manually if you want it overwritten."
+        exit 1
+elif [[ -f "$filename" && "$action" == "-display" ]]
+then
+    [[ ! -s "$filename" ]] && { echo "file has zero size"; exit 1; }
+else    # File doesn't exist, so try to create it to see if we can write it.
+        > "$filename" || exit 1
+fi
+
+cd ../java
+# Here, we want to use the ESAPI.properties in $esapi_resources_test since
+# we know the Encryptor.MasterKey that it was encrypted with and we need
+# to decrypt with the same one. The one in $esapi_resources doesn't have
+# one set by default, and if 'setMasterKey.sh' is called first to create
+# that property, it will differ what was used in the 'encrypted.properties'
+# file.
+if [[ "$action" == "-display" ]]
+then
+    set -x
+    java -Dlog4j.configuration="file:$log4j_properties" \
+         -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+         -classpath "$esapi_classpath" \
+         DisplayEncryptedProperties "$filename"
+else
+    echo
+    echo ======================= Instructions ======================
+    echo "When you see 'Enter key: ', enter the property name."
+    echo "When you see 'Enter value: ', enter the property value."
+    echo "The property value will be encrypted and the value will be in plaintext"
+    echo "and they will be placed in the specified output file."
+    echo "End entering key/value pairs by entering an empty key & value."
+    echo ===========================================================
+    echo
+    echo "Hit <Enter> to continue..."; read GO
+    set -x
+    java -Dlog4j.configuration="file:$log4j_properties" \
+         -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+         -classpath "$esapi_classpath" \
+      org.owasp.esapi.reference.crypto.DefaultEncryptedProperties "$filename" &&
+      echo "Output of encrypted properties in file: $filename"
+fi
diff --git a/src/examples/scripts/.svn/text-base/encrypted.properties.svn-base b/src/examples/scripts/.svn/text-base/encrypted.properties.svn-base
new file mode 100644
index 0000000..54b9111
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/encrypted.properties.svn-base
@@ -0,0 +1,6 @@
+#Encrypted Properties File
+#Sat Apr 24 22:48:37 EDT 2010
+jdbc.url=AAAAAAEytBoAAAEqnVoOoQAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABBnm1MbEbWLB/q+xTJAjtCvAAAAQD9JZlTKv78Dx8c2OQ6hJrvMpRz2YXuNPIXbKkartTPgG2Rljj0nwiFltdeYtXVkbpW62tvbQQddGNZo1E0NfzQAFNIbLlHoNLdGOnSzonvJL/xnGlGY
+database.driver=AAAAAAEytBoAAAEqnVWMrgAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABCDNOMJ+EeKtzHaAJuLikDcAAAAIBNgqefmRItecW16grynaqNH768VeEUraAUmGFB/PPWzABTmDDoPWg9DHOwRrvT4h5jouLGIfg\=\=
+jdbc.username=AAAAAAEytBoAAAEoMtoomAAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABAha165LauV/fMx6iY1OLDAAAAAINwPGRVoSKSyunLSE5HyjvEPrlTR+TRyiv6+M/4UtmFSABSRs7m648MkHV92GH0ToWRURjDRnA\=\=
+jdbc.password=AAAAAAEytBoAAAEoMtsy9wAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABAr1UdBfxmI+RxQmBqfQB9YAAAAIKiaF7vCiA5woJs90oZ/YAe4jGzwd+FZoUtfidJoGCthABTbotqgkU2Qt+9q2nXf6ut00pqQ4Q\=\=
diff --git a/src/examples/scripts/.svn/text-base/findjar.sh.svn-base b/src/examples/scripts/.svn/text-base/findjar.sh.svn-base
new file mode 100644
index 0000000..611c329
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/findjar.sh.svn-base
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Purpose: Find specified jar and provide full path name to it for use in
+#          Java classpath.
+#
+############################################################################
+
+USAGE="Usage: ${0##*/} [-start starting_dir] jar_pattern"
+PROG=${0##*/}
+
+#   Default starting directory is Maven2 repository under $HOME ...
+starting_dir=$HOME/.m2/repository
+
+case "$1" in
+-start) shift; starting_dir="$1"; shift ;;
+-\?)    echo "$USAGE" >&2; exit 2 ;;
+-*)     echo "$PROG: Unknown option: $1; treating as a jar pattern." >&2 ;;
+esac
+
+jar_pattern="$1"
+case "$jar_pattern" in
+*.zip|*.jar)    ;;  # Suffix already present
+"") echo "$PROG: Missing jar pattern.\n$USAGE" >&2; exit 2    ;;
+*)  jar_pattern="${jar_pattern}*.jar"                   ;;
+esac
+
+# echo "Starting location: $starting_dir"   # DEBUG
+# echo "Jar pattern: $jar_pattern"          # DEBUG
+find "$starting_dir" -type f -name "$jar_pattern" -print |
+    egrep -v 'javadoc|sources'
diff --git a/src/examples/scripts/.svn/text-base/persistEncryptedData.sh.svn-base b/src/examples/scripts/.svn/text-base/persistEncryptedData.sh.svn-base
new file mode 100644
index 0000000..e7dbbe4
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/persistEncryptedData.sh.svn-base
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Purpose: Persist some encrypted data by saving it to a file. Shows how
+#          the serialization works. See ../java/PersistedEncryptedData.java
+#          for details.
+# Usage: ./persistEncryptedData.sh plaintext_string output_file {hex|base64|raw}
+#           The last argument refers to how the encrypted data will be encoded.
+#           The output file name will also be named with this as the file
+#           suffix.
+##############################################################################
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+cd ../java
+set -x
+# Since this is just an illustration, we will use the test ESAPI.properties in
+# $esapi_resources_test. That way, it won't matter if the user has neglected
+# to run the 'setMasterKey.sh' example before running this one.
+java -Dlog4j.configuration="file:$log4j_properties" \
+    -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+    -ea -classpath "$esapi_classpath" \
+    PersistedEncryptedData "$@"
diff --git a/src/examples/scripts/.svn/text-base/runClass.sh.svn-base b/src/examples/scripts/.svn/text-base/runClass.sh.svn-base
new file mode 100644
index 0000000..1c79082
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/runClass.sh.svn-base
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Purpose: Run an example class in ../java directory
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+cd ../java
+className=${1%.class}
+shift
+if [[ ! -r ${className}.class ]]
+then echo >2&1 "Can't find class file: ${className}.class"
+     exit 1
+fi
+echo "Your ESAPI.properties file: ${esapi_resources_test:?}/ESAPI.properties"
+echo "Your log4j properties file: ${log4j_properties:?}"
+echo
+set -x
+java -Dlog4j.configuration="file:$log4j_properties" \
+     -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+     -classpath "$esapi_classpath" \
+     ${className} "$@"
diff --git a/src/examples/scripts/.svn/text-base/setMasterKey.sh.svn-base b/src/examples/scripts/.svn/text-base/setMasterKey.sh.svn-base
new file mode 100644
index 0000000..7075532
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/setMasterKey.sh.svn-base
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+cd ../java
+echo "Your ESAPI.properties file: $esapi_properties"
+echo
+# set -x
+# This should use the real ESAPI.properties in $esapi_resources that does
+# not yet have Encryptor.MasterKey and Encryptor.MasterSalt yet set.
+java -Dlog4j.configuration="file:$log4j_properties" \
+     -Dorg.owasp.esapi.resources="$esapi_resources" \
+     -classpath "$esapi_classpath" \
+     org.owasp.esapi.reference.crypto.JavaEncryptor "$@"
diff --git a/src/examples/scripts/.svn/text-base/setenv-svn.sh.svn-base b/src/examples/scripts/.svn/text-base/setenv-svn.sh.svn-base
new file mode 100644
index 0000000..4102485
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/setenv-svn.sh.svn-base
@@ -0,0 +1,52 @@
+#/bin/bash
+# Purpose:  Use to set up environment to compile and run examples if ESAPI
+#           downloaded from the SVN repository.
+# Usage:    From csh, tcsh:
+#               $ source ./setenv-svn.sh
+#           From most other *nix shells:
+#               $ . ./setenv-svn.sh
+#
+#           where '$' represents the shell command line prompt.
+###########################################################################
+
+# IMPORTANT NOTE:  Since you may have multiple (say) log4j jars under
+#                  your Maven2 repository under $HOME/.m2/respository, we
+#                  look for the specific versions that ESAPI was using as of
+#                  ESAPI 2.0_RC10 release on 2010/10/18. If these versions
+#                  changed, they will have to be reflected here.
+#
+esapi_classpath=".:\
+../../../target/classes:\
+$(ls ../../../target/esapi-*.jar 2>&- || echo .):\
+$(./findjar.sh log4j-1.2.16.jar):\
+$(./findjar.sh commons-fileupload-1.2.jar):\
+$(./findjar.sh servlet-api-2.4.jar)"
+
+esapi_resources="$(\cd ../../../configuration/esapi >&- 2>&- && pwd)"
+esapi_resources_test="$(\cd ../../../src/test/resources/esapi >&- 2>&- && pwd)"
+
+log4j_properties="../../../src/test/resources/log4j.xml"
+
+if [[ ! -r "$esapi_resources"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$esapi_resources_test"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources_test"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$log4j_properties" ]]
+then echo 2>&1 "setenv-svn.sh: Can't read log4j.xml: $log4j_properties"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+echo ############################################################
+echo "esapi_resources=$esapi_resources"
+echo "esapi_resources_test=$esapi_resources_test"
+echo "log4j_properties=$log4j_properties"
+echo "esapi_classpath=$esapi_classpath"
+echo ############################################################
+
+export esapi_classpath esapi_resources esapi_resources_test log4j_properties
diff --git a/src/examples/scripts/.svn/text-base/setenv-zip.sh.svn-base b/src/examples/scripts/.svn/text-base/setenv-zip.sh.svn-base
new file mode 100644
index 0000000..2a45579
--- /dev/null
+++ b/src/examples/scripts/.svn/text-base/setenv-zip.sh.svn-base
@@ -0,0 +1,50 @@
+#!/bin/bash
+# Purpose:  Use to set up environment to compile and run examples if ESAPI
+#           downloaded as a zip file.
+# Usage:    From csh, tcsh:
+#               $ source ./setenv-zip.sh
+#           From most other *nix shells:
+#               $ . ./setenv-zip.sh
+#
+#           where '$' represents the shell command line prompt.
+###########################################################################
+
+# Here we don't look for the specific versions of the dependent libraries
+# since the specific version of the library is delivered as part of the
+# ESAPI zip file. In this manner, we do not have to update this if these
+# versions change. For the record, at the time of this writing, these were
+# log4j-1.2.16.jar, commons-fileupload-1.2.jar, and servlet-api-2.4.jar.
+esapi_classpath=".:\
+$(ls ../../../esapi*.jar):\
+$(./findjar.sh -start ../../../libs log4j-*.jar):\
+$(./findjar.sh -start ../../../libs commons-fileupload-*.jar):\
+$(./findjar.sh -start ../../../libs servlet-api-*.jar)"
+
+esapi_resources="$(\cd ../../../configuration/esapi >&- 2>&- && pwd)"
+esapi_resources_test="$(\cd ../../../src/test/resources/esapi >&- 2>&- && pwd)"
+
+log4j_properties="../../../src/test/resources/log4j.xml"
+
+if [[ ! -r "$esapi_resources"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$esapi_resources_test"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources_test"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$log4j_properties" ]]
+then echo 2>&1 "setenv-svn.sh: Can't read log4j.xml: $log4j_properties"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+echo ############################################################
+echo "esapi_resources=$esapi_resources"
+echo "esapi_resources_test=$esapi_resources_test"
+echo "log4j_properties=$log4j_properties"
+echo "esapi_classpath=$esapi_classpath"
+echo ############################################################
+
+export esapi_classpath esapi_resources esapi_resources_test log4j_properties
diff --git a/src/examples/scripts/compile.sh b/src/examples/scripts/compile.sh
new file mode 100755
index 0000000..95203d7
--- /dev/null
+++ b/src/examples/scripts/compile.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+cd ../java
+set -x
+javac -classpath "$esapi_classpath" ${1:-*.java}
diff --git a/src/examples/scripts/encryptProperties.sh b/src/examples/scripts/encryptProperties.sh
new file mode 100755
index 0000000..bc60683
--- /dev/null
+++ b/src/examples/scripts/encryptProperties.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+# Create or display encrypted properties.
+#
+# Usage: encryptedProperties.sh {-display|-create} [encrypted_properties_filename]
+#        You can use the file 'encrypted.properties' in this directory
+#        as a sample if you so wish. That's what it defaults to.
+
+USAGE="Usage: encryptedProperties.sh {-display|-create} [encrypted_properties_filename]"
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+case $1 in
+-display|-create)   action="$1" ;;
+*)  echo "Missing '-display' or '-create' arg."; echo $USAGE; exit 2    ;;
+esac
+
+filename=${2:-encrypted.properties}
+case "$filename" in
+/*) ;;
+*)   filename="$PWD/$filename"    ;;
+esac
+
+if [[ -f "$filename" && "$action" == "-create" ]]
+then    echo "Output file '$filename' already exists; will not overwrite."
+        echo "Remove manually if you want it overwritten."
+        exit 1
+elif [[ -f "$filename" && "$action" == "-display" ]]
+then
+    [[ ! -s "$filename" ]] && { echo "file has zero size"; exit 1; }
+else    # File doesn't exist, so try to create it to see if we can write it.
+        > "$filename" || exit 1
+fi
+
+cd ../java
+# Here, we want to use the ESAPI.properties in $esapi_resources_test since
+# we know the Encryptor.MasterKey that it was encrypted with and we need
+# to decrypt with the same one. The one in $esapi_resources doesn't have
+# one set by default, and if 'setMasterKey.sh' is called first to create
+# that property, it will differ what was used in the 'encrypted.properties'
+# file.
+if [[ "$action" == "-display" ]]
+then
+    set -x
+    java -Dlog4j.configuration="file:$log4j_properties" \
+         -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+         -classpath "$esapi_classpath" \
+         DisplayEncryptedProperties "$filename"
+else
+    echo
+    echo ======================= Instructions ======================
+    echo "When you see 'Enter key: ', enter the property name."
+    echo "When you see 'Enter value: ', enter the property value."
+    echo "The property value will be encrypted and the value will be in plaintext"
+    echo "and they will be placed in the specified output file."
+    echo "End entering key/value pairs by entering an empty key & value."
+    echo ===========================================================
+    echo
+    echo "Hit <Enter> to continue..."; read GO
+    set -x
+    java -Dlog4j.configuration="file:$log4j_properties" \
+         -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+         -classpath "$esapi_classpath" \
+      org.owasp.esapi.reference.crypto.DefaultEncryptedProperties "$filename" &&
+      echo "Output of encrypted properties in file: $filename"
+fi
diff --git a/src/examples/scripts/encrypted.properties b/src/examples/scripts/encrypted.properties
new file mode 100644
index 0000000..54b9111
--- /dev/null
+++ b/src/examples/scripts/encrypted.properties
@@ -0,0 +1,6 @@
+#Encrypted Properties File
+#Sat Apr 24 22:48:37 EDT 2010
+jdbc.url=AAAAAAEytBoAAAEqnVoOoQAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABBnm1MbEbWLB/q+xTJAjtCvAAAAQD9JZlTKv78Dx8c2OQ6hJrvMpRz2YXuNPIXbKkartTPgG2Rljj0nwiFltdeYtXVkbpW62tvbQQddGNZo1E0NfzQAFNIbLlHoNLdGOnSzonvJL/xnGlGY
+database.driver=AAAAAAEytBoAAAEqnVWMrgAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABCDNOMJ+EeKtzHaAJuLikDcAAAAIBNgqefmRItecW16grynaqNH768VeEUraAUmGFB/PPWzABTmDDoPWg9DHOwRrvT4h5jouLGIfg\=\=
+jdbc.username=AAAAAAEytBoAAAEoMtoomAAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABAha165LauV/fMx6iY1OLDAAAAAINwPGRVoSKSyunLSE5HyjvEPrlTR+TRyiv6+M/4UtmFSABSRs7m648MkHV92GH0ToWRURjDRnA\=\=
+jdbc.password=AAAAAAEytBoAAAEoMtsy9wAUQUVTL0NCQy9QS0NTNVBhZGRpbmcAgAAQABAr1UdBfxmI+RxQmBqfQB9YAAAAIKiaF7vCiA5woJs90oZ/YAe4jGzwd+FZoUtfidJoGCthABTbotqgkU2Qt+9q2nXf6ut00pqQ4Q\=\=
diff --git a/src/examples/scripts/findjar.sh b/src/examples/scripts/findjar.sh
new file mode 100755
index 0000000..611c329
--- /dev/null
+++ b/src/examples/scripts/findjar.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Purpose: Find specified jar and provide full path name to it for use in
+#          Java classpath.
+#
+############################################################################
+
+USAGE="Usage: ${0##*/} [-start starting_dir] jar_pattern"
+PROG=${0##*/}
+
+#   Default starting directory is Maven2 repository under $HOME ...
+starting_dir=$HOME/.m2/repository
+
+case "$1" in
+-start) shift; starting_dir="$1"; shift ;;
+-\?)    echo "$USAGE" >&2; exit 2 ;;
+-*)     echo "$PROG: Unknown option: $1; treating as a jar pattern." >&2 ;;
+esac
+
+jar_pattern="$1"
+case "$jar_pattern" in
+*.zip|*.jar)    ;;  # Suffix already present
+"") echo "$PROG: Missing jar pattern.\n$USAGE" >&2; exit 2    ;;
+*)  jar_pattern="${jar_pattern}*.jar"                   ;;
+esac
+
+# echo "Starting location: $starting_dir"   # DEBUG
+# echo "Jar pattern: $jar_pattern"          # DEBUG
+find "$starting_dir" -type f -name "$jar_pattern" -print |
+    egrep -v 'javadoc|sources'
diff --git a/src/examples/scripts/persistEncryptedData.sh b/src/examples/scripts/persistEncryptedData.sh
new file mode 100755
index 0000000..e7dbbe4
--- /dev/null
+++ b/src/examples/scripts/persistEncryptedData.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Purpose: Persist some encrypted data by saving it to a file. Shows how
+#          the serialization works. See ../java/PersistedEncryptedData.java
+#          for details.
+# Usage: ./persistEncryptedData.sh plaintext_string output_file {hex|base64|raw}
+#           The last argument refers to how the encrypted data will be encoded.
+#           The output file name will also be named with this as the file
+#           suffix.
+##############################################################################
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+cd ../java
+set -x
+# Since this is just an illustration, we will use the test ESAPI.properties in
+# $esapi_resources_test. That way, it won't matter if the user has neglected
+# to run the 'setMasterKey.sh' example before running this one.
+java -Dlog4j.configuration="file:$log4j_properties" \
+    -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+    -ea -classpath "$esapi_classpath" \
+    PersistedEncryptedData "$@"
diff --git a/src/examples/scripts/runClass.sh b/src/examples/scripts/runClass.sh
new file mode 100755
index 0000000..1c79082
--- /dev/null
+++ b/src/examples/scripts/runClass.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Purpose: Run an example class in ../java directory
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+cd ../java
+className=${1%.class}
+shift
+if [[ ! -r ${className}.class ]]
+then echo >2&1 "Can't find class file: ${className}.class"
+     exit 1
+fi
+echo "Your ESAPI.properties file: ${esapi_resources_test:?}/ESAPI.properties"
+echo "Your log4j properties file: ${log4j_properties:?}"
+echo
+set -x
+java -Dlog4j.configuration="file:$log4j_properties" \
+     -Dorg.owasp.esapi.resources="$esapi_resources_test" \
+     -classpath "$esapi_classpath" \
+     ${className} "$@"
diff --git a/src/examples/scripts/setMasterKey.sh b/src/examples/scripts/setMasterKey.sh
new file mode 100755
index 0000000..7075532
--- /dev/null
+++ b/src/examples/scripts/setMasterKey.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if [[ -z "$esapi_classpath" ]]
+then
+    echo 2>&1 "esapi_classpath not set. Did you dot the appropriate env file?"
+    echo 2>&1 "If you are using ESAPI from downloaded zip file, use:"
+    echo 2>&1 "        . ./setenv-zip.sh"
+    echo 2>&1 "If you are using ESAPI pulled from SVN repository, use:"
+    echo 2>&1 "        . ./setenv-svn.sh"
+    exit 1
+fi
+
+cd ../java
+echo "Your ESAPI.properties file: $esapi_properties"
+echo
+# set -x
+# This should use the real ESAPI.properties in $esapi_resources that does
+# not yet have Encryptor.MasterKey and Encryptor.MasterSalt yet set.
+java -Dlog4j.configuration="file:$log4j_properties" \
+     -Dorg.owasp.esapi.resources="$esapi_resources" \
+     -classpath "$esapi_classpath" \
+     org.owasp.esapi.reference.crypto.JavaEncryptor "$@"
diff --git a/src/examples/scripts/setenv-svn.sh b/src/examples/scripts/setenv-svn.sh
new file mode 100755
index 0000000..4102485
--- /dev/null
+++ b/src/examples/scripts/setenv-svn.sh
@@ -0,0 +1,52 @@
+#/bin/bash
+# Purpose:  Use to set up environment to compile and run examples if ESAPI
+#           downloaded from the SVN repository.
+# Usage:    From csh, tcsh:
+#               $ source ./setenv-svn.sh
+#           From most other *nix shells:
+#               $ . ./setenv-svn.sh
+#
+#           where '$' represents the shell command line prompt.
+###########################################################################
+
+# IMPORTANT NOTE:  Since you may have multiple (say) log4j jars under
+#                  your Maven2 repository under $HOME/.m2/respository, we
+#                  look for the specific versions that ESAPI was using as of
+#                  ESAPI 2.0_RC10 release on 2010/10/18. If these versions
+#                  changed, they will have to be reflected here.
+#
+esapi_classpath=".:\
+../../../target/classes:\
+$(ls ../../../target/esapi-*.jar 2>&- || echo .):\
+$(./findjar.sh log4j-1.2.16.jar):\
+$(./findjar.sh commons-fileupload-1.2.jar):\
+$(./findjar.sh servlet-api-2.4.jar)"
+
+esapi_resources="$(\cd ../../../configuration/esapi >&- 2>&- && pwd)"
+esapi_resources_test="$(\cd ../../../src/test/resources/esapi >&- 2>&- && pwd)"
+
+log4j_properties="../../../src/test/resources/log4j.xml"
+
+if [[ ! -r "$esapi_resources"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$esapi_resources_test"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources_test"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$log4j_properties" ]]
+then echo 2>&1 "setenv-svn.sh: Can't read log4j.xml: $log4j_properties"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+echo ############################################################
+echo "esapi_resources=$esapi_resources"
+echo "esapi_resources_test=$esapi_resources_test"
+echo "log4j_properties=$log4j_properties"
+echo "esapi_classpath=$esapi_classpath"
+echo ############################################################
+
+export esapi_classpath esapi_resources esapi_resources_test log4j_properties
diff --git a/src/examples/scripts/setenv-zip.sh b/src/examples/scripts/setenv-zip.sh
new file mode 100755
index 0000000..2a45579
--- /dev/null
+++ b/src/examples/scripts/setenv-zip.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+# Purpose:  Use to set up environment to compile and run examples if ESAPI
+#           downloaded as a zip file.
+# Usage:    From csh, tcsh:
+#               $ source ./setenv-zip.sh
+#           From most other *nix shells:
+#               $ . ./setenv-zip.sh
+#
+#           where '$' represents the shell command line prompt.
+###########################################################################
+
+# Here we don't look for the specific versions of the dependent libraries
+# since the specific version of the library is delivered as part of the
+# ESAPI zip file. In this manner, we do not have to update this if these
+# versions change. For the record, at the time of this writing, these were
+# log4j-1.2.16.jar, commons-fileupload-1.2.jar, and servlet-api-2.4.jar.
+esapi_classpath=".:\
+$(ls ../../../esapi*.jar):\
+$(./findjar.sh -start ../../../libs log4j-*.jar):\
+$(./findjar.sh -start ../../../libs commons-fileupload-*.jar):\
+$(./findjar.sh -start ../../../libs servlet-api-*.jar)"
+
+esapi_resources="$(\cd ../../../configuration/esapi >&- 2>&- && pwd)"
+esapi_resources_test="$(\cd ../../../src/test/resources/esapi >&- 2>&- && pwd)"
+
+log4j_properties="../../../src/test/resources/log4j.xml"
+
+if [[ ! -r "$esapi_resources"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$esapi_resources_test"/ESAPI.properties ]]
+then echo 2>&1 "setenv-svn.sh: Can't read ESAPI.properties in $esapi_resources_test"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+if [[ ! -r "$log4j_properties" ]]
+then echo 2>&1 "setenv-svn.sh: Can't read log4j.xml: $log4j_properties"
+     return 1   # Don't use 'exit' here or it will kill their current shell.
+fi
+
+echo ############################################################
+echo "esapi_resources=$esapi_resources"
+echo "esapi_resources_test=$esapi_resources_test"
+echo "log4j_properties=$log4j_properties"
+echo "esapi_classpath=$esapi_classpath"
+echo ############################################################
+
+export esapi_classpath esapi_resources esapi_resources_test log4j_properties
diff --git a/src/main/.svn/all-wcprops b/src/main/.svn/all-wcprops
new file mode 100644
index 0000000..6a938e6
--- /dev/null
+++ b/src/main/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main
+END
diff --git a/src/main/.svn/entries b/src/main/.svn/entries
new file mode 100644
index 0000000..9949853
--- /dev/null
+++ b/src/main/.svn/entries
@@ -0,0 +1,34 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+assembly
+dir
+

+java
+dir
+

diff --git a/src/main/assembly/.svn/all-wcprops b/src/main/assembly/.svn/all-wcprops
new file mode 100644
index 0000000..64053e7
--- /dev/null
+++ b/src/main/assembly/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/assembly
+END
+dist.xml
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/assembly/dist.xml
+END
diff --git a/src/main/assembly/.svn/entries b/src/main/assembly/.svn/entries
new file mode 100644
index 0000000..5efca31
--- /dev/null
+++ b/src/main/assembly/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/assembly
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-05-09T22:33:50.432955Z
+1777
+chrisisbeef at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+dist.xml
+file
+
+
+
+
+2014-02-18T16:19:52.509957Z
+f9c8ead55f00f570af2fba6caa4ab328
+2011-05-09T22:33:50.432955Z
+1777
+chrisisbeef at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2052
+

diff --git a/src/main/assembly/.svn/text-base/dist.xml.svn-base b/src/main/assembly/.svn/text-base/dist.xml.svn-base
new file mode 100644
index 0000000..b192b90
--- /dev/null
+++ b/src/main/assembly/.svn/text-base/dist.xml.svn-base
@@ -0,0 +1,55 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd">
+    <id>dist</id>
+    <formats>
+        <format>zip</format>
+        <format>tar.gz</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <includeSiteDirectory>true</includeSiteDirectory>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>LICENSE*</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory/>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+            <excludes>
+                <exclude>*-sources.jar</exclude>
+                <exclude>*-javadoc.jar</exclude>
+            </excludes>
+        </fileSet>
+        <fileSet>
+            <directory>src</directory>
+            <outputDirectory>src</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>documentation</directory>
+            <outputDirectory>documentation</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>configuration</directory>
+            <outputDirectory>configuration</outputDirectory>
+            <includes>
+                <include>.esapi/**/*</include>
+                <include>log4j.dtd</include>
+                <include>log4j.xml</include>
+                <include>properties/**/*</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+    <dependencySets>
+        <dependencySet>
+            <!-- Some provided deps are being brought in, this may be related: http://jira.codehaus.org/browse/MASSEMBLY-324 -->
+            <scope>compile</scope>
+            <outputDirectory>libs</outputDirectory>
+            <useProjectArtifact>false</useProjectArtifact>
+        </dependencySet>
+    </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/src/main/assembly/dist.xml b/src/main/assembly/dist.xml
new file mode 100644
index 0000000..b192b90
--- /dev/null
+++ b/src/main/assembly/dist.xml
@@ -0,0 +1,55 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd">
+    <id>dist</id>
+    <formats>
+        <format>zip</format>
+        <format>tar.gz</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <includeSiteDirectory>true</includeSiteDirectory>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>LICENSE*</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory/>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+            <excludes>
+                <exclude>*-sources.jar</exclude>
+                <exclude>*-javadoc.jar</exclude>
+            </excludes>
+        </fileSet>
+        <fileSet>
+            <directory>src</directory>
+            <outputDirectory>src</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>documentation</directory>
+            <outputDirectory>documentation</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>configuration</directory>
+            <outputDirectory>configuration</outputDirectory>
+            <includes>
+                <include>.esapi/**/*</include>
+                <include>log4j.dtd</include>
+                <include>log4j.xml</include>
+                <include>properties/**/*</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+    <dependencySets>
+        <dependencySet>
+            <!-- Some provided deps are being brought in, this may be related: http://jira.codehaus.org/browse/MASSEMBLY-324 -->
+            <scope>compile</scope>
+            <outputDirectory>libs</outputDirectory>
+            <useProjectArtifact>false</useProjectArtifact>
+        </dependencySet>
+    </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/src/main/java/.svn/all-wcprops b/src/main/java/.svn/all-wcprops
new file mode 100644
index 0000000..90f5098
--- /dev/null
+++ b/src/main/java/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java
+END
diff --git a/src/main/java/.svn/entries b/src/main/java/.svn/entries
new file mode 100644
index 0000000..b4cd119
--- /dev/null
+++ b/src/main/java/.svn/entries
@@ -0,0 +1,34 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+org
+dir
+

+META-INF
+dir
+

diff --git a/src/main/java/META-INF/.svn/all-wcprops b/src/main/java/META-INF/.svn/all-wcprops
new file mode 100644
index 0000000..d672eee
--- /dev/null
+++ b/src/main/java/META-INF/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/META-INF
+END
+MANIFEST.MF
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/META-INF/MANIFEST.MF
+END
diff --git a/src/main/java/META-INF/.svn/entries b/src/main/java/META-INF/.svn/entries
new file mode 100644
index 0000000..ad78f04
--- /dev/null
+++ b/src/main/java/META-INF/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/META-INF
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-10-23T03:45:57.765953Z
+718
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+MANIFEST.MF
+file
+
+
+
+
+2014-02-18T16:19:52.513957Z
+b10f37c8bb1803d98c127a01d1a71cc5
+2009-05-10T02:45:33.780997Z
+497
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+39
+

diff --git a/src/main/java/META-INF/.svn/text-base/MANIFEST.MF.svn-base b/src/main/java/META-INF/.svn/text-base/MANIFEST.MF.svn-base
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/src/main/java/META-INF/.svn/text-base/MANIFEST.MF.svn-base
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path: 
+
diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path: 
+
diff --git a/src/main/java/org/.svn/all-wcprops b/src/main/java/org/.svn/all-wcprops
new file mode 100644
index 0000000..65c1eb0
--- /dev/null
+++ b/src/main/java/org/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org
+END
diff --git a/src/main/java/org/.svn/entries b/src/main/java/org/.svn/entries
new file mode 100644
index 0000000..6833e36
--- /dev/null
+++ b/src/main/java/org/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+owasp
+dir
+

diff --git a/src/main/java/org/owasp/.svn/all-wcprops b/src/main/java/org/owasp/.svn/all-wcprops
new file mode 100644
index 0000000..de4dec7
--- /dev/null
+++ b/src/main/java/org/owasp/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp
+END
diff --git a/src/main/java/org/owasp/.svn/entries b/src/main/java/org/owasp/.svn/entries
new file mode 100644
index 0000000..b90789d
--- /dev/null
+++ b/src/main/java/org/owasp/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+esapi
+dir
+

diff --git a/src/main/java/org/owasp/esapi/.svn/all-wcprops b/src/main/java/org/owasp/esapi/.svn/all-wcprops
new file mode 100644
index 0000000..2ff5ca0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/all-wcprops
@@ -0,0 +1,155 @@
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi
+END
+StringUtilities.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/StringUtilities.java
+END
+AccessController.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/AccessController.java
+END
+LogFactory.java
+K 25
+svn:wc:ra_dav:version-url
+V 81
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/LogFactory.java
+END
+Randomizer.java
+K 25
+svn:wc:ra_dav:version-url
+V 81
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/Randomizer.java
+END
+EncryptedProperties.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/EncryptedProperties.java
+END
+Authenticator.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/Authenticator.java
+END
+HTTPUtilities.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/HTTPUtilities.java
+END
+EncoderConstants.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/EncoderConstants.java
+END
+SafeFile.java
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/SafeFile.java
+END
+User.java
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/User.java
+END
+AccessReferenceMap.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/AccessReferenceMap.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/package.html
+END
+Encoder.java
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/Encoder.java
+END
+Logger.java
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/Logger.java
+END
+ValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/ValidationRule.java
+END
+IntrusionDetector.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/IntrusionDetector.java
+END
+Validator.java
+K 25
+svn:wc:ra_dav:version-url
+V 80
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/Validator.java
+END
+Encryptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 80
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/Encryptor.java
+END
+PreparedString.java
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/PreparedString.java
+END
+AccessControlRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/AccessControlRule.java
+END
+Executor.java
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/Executor.java
+END
+SecurityConfiguration.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/SecurityConfiguration.java
+END
+ValidationErrorList.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/ValidationErrorList.java
+END
+ESAPI.java
+K 25
+svn:wc:ra_dav:version-url
+V 76
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/ESAPI.java
+END
+ExecuteResult.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/ExecuteResult.java
+END
diff --git a/src/main/java/org/owasp/esapi/.svn/entries b/src/main/java/org/owasp/esapi/.svn/entries
new file mode 100644
index 0000000..10cf62f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/entries
@@ -0,0 +1,905 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+c7d8e8d7813983c150f0a441afbb8b4c
+2009-11-13T13:07:02.305925Z
+815
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3805
+

+SafeFile.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+d2292dc299f6359784110013c31e63e0
+2010-01-17T22:06:50.493737Z
+949
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3674
+

+User.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+21fb3afa339cf257d6cf35794577ba50
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+21423
+

+AccessReferenceMap.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+2598385033227e2fa8d0656262a44bf4
+2010-10-14T04:59:11.324393Z
+1561
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6513
+

+codecs
+dir
+

+IntrusionDetector.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+4caa2cf844d5f2a4d37e05c31de0a808
+2010-10-14T04:59:11.324393Z
+1561
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2992
+

+Validator.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+2ccd28fdbff6fe04adc972d8c2860314
+2011-02-28T22:01:36.497148Z
+1726
+steven.van.der.baan at owasp.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+34351
+

+Encryptor.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+e16b9053b61d6ce3c272d365a22fd027
+2013-08-29T00:49:41.830245Z
+1880
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13845
+

+filters
+dir
+

+errors
+dir
+

+waf
+dir
+

+util
+dir
+

+StringUtilities.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+17ee0f701d1db74dc7a0830f6dfef015
+2009-12-05T03:08:44.297728Z
+854
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6324
+

+AccessController.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+2ae8f05a487c1977cb96b01729a75520
+2010-10-14T04:59:11.324393Z
+1561
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+16920
+

+Randomizer.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+9a16070d5e12966db946d7b757bb6159
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6200
+

+Authenticator.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+d61ee4b19d8724b8f73a40f662160fce
+2010-11-17T04:23:01.349932Z
+1656
+jtmelton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+12562
+

+EncryptedProperties.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+e99d03c6df9f2f9ee84677b323f2ea4b
+2012-07-22T00:19:30.280565Z
+1870
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3862
+

+tags
+dir
+

+reference
+dir
+

+Logger.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+9d1b7b36bb8654865fde37ac9e2aaac6
+2011-03-01T04:29:54.836839Z
+1727
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15262
+

+Encoder.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+60960609d38550f190f50790ce5f6063
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+20337
+

+ValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+be9a8d108a1d82e610dad63a31d97986
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1988
+

+doc-files
+dir
+

+PreparedString.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+2801385f109317e9558f13e17b5eecb8
+2010-11-04T02:21:00.694583Z
+1640
+jtmelton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5042
+

+AccessControlRule.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+c699e1c94c0c66cd214da9db0a11ea7b
+2009-05-10T04:33:18.539487Z
+510
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+233
+

+Executor.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+75fae4bf01a722b1b5915d485a488e7b
+2010-10-14T04:59:11.324393Z
+1561
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2944
+

+SecurityConfiguration.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+77cca82b2331ea7795ef4ff2078a8b0f
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+25612
+

+ValidationErrorList.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+71206d1ce4a7df0bfbd2c3d514711fc6
+2010-10-14T04:59:11.324393Z
+1561
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4557
+

+crypto
+dir
+

+ESAPI.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+b4e3744324798bd8e35517b7634f6d8f
+2010-11-01T04:47:16.605616Z
+1634
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8503
+

+ExecuteResult.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+a96cc10cb2fa4e385a3617faedebd82f
+2010-09-15T23:43:49.847233Z
+1529
+patrick.allen.higgins
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1932
+

+LogFactory.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+c3d61cfb0677bb5362337f943c866fbe
+2009-07-02T03:07:07.304941Z
+554
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2238
+

+HTTPUtilities.java
+file
+
+
+
+
+2014-02-18T16:19:53.489977Z
+f58cd752b58ae94c2e491b9692358887
+2010-10-14T04:59:11.324393Z
+1561
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+26938
+

+EncoderConstants.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+ea9415d9db22074de283fabc5a10a7ff
+2009-12-02T05:39:03.859482Z
+843
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3806
+

diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/AccessControlRule.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/AccessControlRule.java.svn-base
new file mode 100644
index 0000000..becdb99
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/AccessControlRule.java.svn-base
@@ -0,0 +1,8 @@
+package org.owasp.esapi;
+
+
+public interface AccessControlRule<P, R> {
+	public void setPolicyParameters(P policyParameter);
+	public P getPolicyParameters();
+	public boolean isAuthorized(R runtimeParameter) throws Exception;
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/AccessController.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/AccessController.java.svn-base
new file mode 100644
index 0000000..e6269e1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/AccessController.java.svn-base
@@ -0,0 +1,351 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AccessControlException;
+
+
+
+/**
+ * The AccessController interface defines a set of methods that can be used in a wide variety of applications to
+ * enforce access control. In most applications, access control must be performed in multiple different locations across
+ * the various application layers. This class provides access control for URLs, business functions, data, services, and
+ * files.
+ * <P>
+ * The implementation of this interface will need to access the current User object (from Authenticator.getCurrentUser())
+ * to determine roles or permissions. In addition, the implementation
+ * will also need information about the resources that are being accessed. Using the user information and the resource
+ * information, the implementation should return an access control decision. 
+ * <P>
+ * Implementers are encouraged to implement the ESAPI access control rules, like assertAuthorizedForFunction() using 
+ * existing access control mechanisms, such as methods like isUserInRole() or hasPrivilege(). While powerful, 
+ * methods like isUserInRole() can be confusing for developers, as users may be in multiple roles or possess multiple 
+ * overlapping privileges. Direct use of these finer grained access control methods encourages the use of complex boolean 
+ * tests throughout the code, which can easily lead to developer mistakes.
+ * <P>
+ * The point of the ESAPI access control interface is to centralize access control logic behind easy to use calls like 
+ * assertAuthorized() so that access control is easy to use and easy to verify. Here is an example of a very 
+ * straightforward to implement, understand, and verify ESAPI access control check: 
+ * 
+ * <pre>
+ * try {
+ *     ESAPI.accessController().assertAuthorized("businessFunction", runtimeData);
+ *     // execute BUSINESS_FUNCTION
+ * } catch (AccessControlException ace) {
+ * ... attack in progress
+ * }
+ * </pre>
+ * 
+ * Note that in the user interface layer, access control checks can be used to control whether particular controls are
+ * rendered or not. These checks are supposed to fail when an unauthorized user is logged in, and do not represent
+ * attacks. Remember that regardless of how the user interface appears, an attacker can attempt to invoke any business
+ * function or access any data in your application. Therefore, access control checks in the user interface should be
+ * repeated in both the business logic and data layers.
+ * 
+ * <pre>
+ * <% if ( ESAPI.accessController().isAuthorized( "businessFunction", runtimeData ) ) { %>
+ * <a href="/doAdminFunction">ADMIN</a>
+ * <% } else { %>
+ * <a href="/doNormalFunction">NORMAL</a>
+ * <% } %>
+ * </pre>
+ * 
+ * @author Mike H. Fauzy (mike.fauzy at aspectsecurity.com) ESAPI v1.6-
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com) ESAPI v0-1.5
+ */
+public interface AccessController {
+
+	/**
+	 * <code>isAuthorized</code> executes the <code>AccessControlRule</code> 
+	 * that is identified by <code>key</code> and listed in the 
+	 * <code>resources/ESAPI-AccessControlPolicy.xml</code> file. It returns 
+	 * true if the <code>AccessControlRule</code> decides that the operation 
+	 * should be allowed. Otherwise, it returns false. Any exception thrown by 
+	 * the <code>AccessControlRule</code> must result in false. If 
+	 * <code>key</code> does not map to an <code>AccessControlRule</code>, then 
+	 * false is returned. 
+	 *  
+	 * Developers should call isAuthorized to control execution flow. For 
+	 * example, if you want to decide whether to display a UI widget in the 
+	 * browser using the same logic that you will use to enforce permissions
+	 * on the server, then isAuthorized is the method that you want to use.
+	 * 
+	 * Typically, assertAuthorized should be used to enforce permissions on the 
+	 * server.
+	 *  
+	 * @param key <code>key</code> maps to 
+	 * <code><AccessControlPolicy><AccessControlRules>
+	 *     <AccessControlRule name="key"</code>
+	 * @param runtimeParameter runtimeParameter can contain anything that 
+	 *        the AccessControlRule needs from the runtime system. 
+	 * @return Returns <code>true</code> if and only if the AccessControlRule specified 
+	 *        by <code>key</code> exists and returned <code>true</code>. 
+	 *        Otherwise returns <code>false</code> 
+	 */
+	public boolean isAuthorized(Object key, Object runtimeParameter);
+	
+	/**
+	 * <code>assertAuthorized</code> executes the <code>AccessControlRule</code> 
+	 * that is identified by <code>key</code> and listed in the 
+	 * <code>resources/ESAPI-AccessControlPolicy.xml</code> file. It does 
+	 * nothing if the <code>AccessControlRule</code> decides that the operation 
+	 * should be allowed. Otherwise, it throws an 
+	 * <code>org.owasp.esapi.errors.AccessControlException</code>. Any exception
+	 * thrown by the <code>AccessControlRule</code> will also result in an 
+	 * <code>AccesControlException</code>. If <code>key</code> does not map to 
+	 * an <code>AccessControlRule</code>, then an <code>AccessControlException
+	 * </code> is thrown.  
+	 *  
+	 * Developers should call {@code assertAuthorized} to enforce privileged access to 
+	 * the system. It should be used to answer the question: "Should execution 
+	 * continue." Ideally, the call to <code>assertAuthorized</code> should
+	 * be integrated into the application framework so that it is called 
+	 * automatically. 
+	 *  
+	 * @param key <code>key</code> maps to 
+	 * <AccessControlPolicy><AccessControlRules>
+	 *     <AccessControlRule name="key"
+	 * @param runtimeParameter runtimeParameter can contain anything that 
+	 *        the AccessControlRule needs from the runtime system.
+	 */
+	public void assertAuthorized(Object key, Object runtimeParameter)
+		throws AccessControlException;
+
+		
+
+	
+	/*** Below this line has been deprecated as of ESAPI 1.6 ***/ 
+	
+	
+	
+	
+    /**
+     * Checks if the current user is authorized to access the referenced URL. Generally, this method should be invoked in the
+     * application's controller or a filter as follows:
+     * <PRE>ESAPI.accessController().isAuthorizedForURL(request.getRequestURI().toString());</PRE>
+     * 
+     * The implementation of this method should call assertAuthorizedForURL(String url), and if an AccessControlException is 
+     * not thrown, this method should return true. This way, if the user is not authorized, false would be returned, and the 
+     * exception would be logged.
+     * 
+     * @param url 
+     * 		the URL as returned by request.getRequestURI().toString()
+     * 
+     * @return 
+     * 		true, if is authorized for URL
+     */
+    boolean isAuthorizedForURL(String url);
+
+    /**
+     * Checks if the current user is authorized to access the referenced function. 
+     * 
+     * The implementation of this method should call assertAuthorizedForFunction(String functionName), and if an 
+     * AccessControlException is not thrown, this method should return true.
+     * 
+     * @param functionName 
+     * 		the name of the function
+     * 
+     * @return 
+     * 		true, if is authorized for function
+     */
+    boolean isAuthorizedForFunction(String functionName);
+
+
+    /**
+     * Checks if the current user is authorized to access the referenced data, represented as an Object. 
+     * 
+     * The implementation of this method should call assertAuthorizedForData(String action, Object data), and if an 
+     * AccessControlException is not thrown, this method should return true.
+     * 
+     * @param action
+     *      The action to verify for an access control decision, such as a role, or an action being performed on the object 
+     *      (e.g., Read, Write, etc.), or the name of the function the data is being passed to.
+     * 
+     * @param data
+     * 		The actual object or object identifier being accessed or a reference to the object being accessed.
+     * 
+     * @return 
+     * 		true, if is authorized for the data
+     */
+    boolean isAuthorizedForData(String action, Object data);
+    
+    /**
+     * Checks if the current user is authorized to access the referenced file. 
+     * 
+     * The implementation of this method should call assertAuthorizedForFile(String filepath), and if an AccessControlException 
+     * is not thrown, this method should return true.
+     *   
+     * @param filepath 
+     * 		the path of the file to be checked, including filename
+     * 
+     * @return 
+     * 		true, if is authorized for the file
+     */
+    boolean isAuthorizedForFile(String filepath);
+
+    /**
+     * Checks if the current user is authorized to access the referenced service. This can be used in applications that
+     * provide access to a variety of back end services.
+     * 
+     * The implementation of this method should call assertAuthorizedForService(String serviceName), and if an 
+     * AccessControlException is not thrown, this method should return true.
+     * 
+     * @param serviceName 
+     * 		the service name
+     * 
+     * @return 
+     * 		true, if is authorized for the service
+     */
+    boolean isAuthorizedForService(String serviceName);
+
+    /**
+     * Checks if the current user is authorized to access the referenced URL. The implementation should allow
+     * access to be granted to any part of the URL. Generally, this method should be invoked in the
+     * application's controller or a filter as follows:
+     * <PRE>ESAPI.accessController().assertAuthorizedForURL(request.getRequestURI().toString());</PRE> 
+     * 
+     * This method throws an AccessControlException if access is not authorized, or if the referenced URL does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the resource exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * @param url 
+     * 		the URL as returned by request.getRequestURI().toString()
+     * 
+     * @throws AccessControlException 
+     * 		if access is not permitted
+     */
+    void assertAuthorizedForURL(String url) throws AccessControlException;
+    
+    /**
+     * Checks if the current user is authorized to access the referenced function. The implementation should define the
+     * function "namespace" to be enforced. Choosing something simple like the class name of action classes or menu item
+     * names will make this implementation easier to use.
+     * <P>
+     * This method throws an AccessControlException if access is not authorized, or if the referenced function does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the function exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param functionName 
+     * 		the function name
+     * 
+     * @throws AccessControlException 
+     * 		if access is not permitted
+     */
+    void assertAuthorizedForFunction(String functionName) throws AccessControlException;
+    
+
+    /**
+     * Checks if the current user is authorized to access the referenced data.  This method simply returns if access is authorized.  
+     * It throws an AccessControlException if access is not authorized, or if the referenced data does not exist.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the resource exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param action
+     *      The action to verify for an access control decision, such as a role, or an action being performed on the object 
+     *      (e.g., Read, Write, etc.), or the name of the function the data is being passed to.
+     * 
+     * @param data
+     * 		The actual object or object identifier being accessed or a reference to the object being accessed.
+     * 
+     * @throws AccessControlException 
+     * 		if access is not permitted
+     */
+    void assertAuthorizedForData(String action, Object data) throws AccessControlException;
+   
+    /**
+     * Checks if the current user is authorized to access the referenced file. The implementation should validate and canonicalize the 
+     * input to be sure the filepath is not malicious.
+     * <P>
+     * This method throws an AccessControlException if access is not authorized, or if the referenced File does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the File exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param filepath
+     * 			Path to the file to be checked
+     * @throws AccessControlException if access is denied
+     */
+    void assertAuthorizedForFile(String filepath) throws AccessControlException;
+    
+    /**
+     * Checks if the current user is authorized to access the referenced service. This can be used in applications that
+     * provide access to a variety of backend services.
+     * <P>
+     * This method throws an AccessControlException if access is not authorized, or if the referenced service does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the service exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param serviceName 
+     * 		the service name
+     * 
+     * @throws AccessControlException
+     * 		if access is not permitted
+     */				
+    void assertAuthorizedForService(String serviceName) throws AccessControlException;
+    
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/AccessReferenceMap.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/AccessReferenceMap.java.svn-base
new file mode 100644
index 0000000..a20338e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/AccessReferenceMap.java.svn-base
@@ -0,0 +1,176 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AccessControlException;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * The AccessReferenceMap interface is used to map from a set of internal
+ * direct object references to a set of indirect references that are safe to
+ * disclose publicly. This can be used to help protect database keys,
+ * filenames, and other types of direct object references. As a rule, developers
+ * should not expose their direct object references as it enables attackers to
+ * attempt to manipulate them.
+ * <P>
+ * Indirect references are handled as strings, to facilitate their use in HTML.
+ * Implementations can generate simple integers or more complicated random
+ * character strings as indirect references. Implementations should probably add
+ * a constructor that takes a list of direct references.
+ * <P>
+ * Note that in addition to defeating all forms of parameter tampering attacks,
+ * there is a side benefit of the AccessReferenceMap. Using random strings as indirect object
+ * references, as opposed to simple integers makes it impossible for an attacker to
+ * guess valid identifiers. So if per-user AccessReferenceMaps are used, then request
+ * forgery (CSRF) attacks will also be prevented.
+ * 
+ * <pre>
+ * Set fileSet = new HashSet();
+ * fileSet.addAll(...); // add direct references (e.g. File objects)
+ * AccessReferenceMap map = new AccessReferenceMap( fileSet );
+ * // store the map somewhere safe - like the session!
+ * String indRef = map.getIndirectReference( file1 );
+ * String href = "http://www.aspectsecurity.com/esapi?file=" + indRef );
+ * ...
+ * // if the indirect reference doesn't exist, it's likely an attack
+ * // getDirectReference throws an AccessControlException
+ * // you should handle as appropriate
+ * String indref = request.getParameter( "file" );
+ * File file = (File)map.getDirectReference( indref );
+ * </pre>
+ * 
+ * <P>
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author Chris Schmidt (chrisisbeef at gmail.com)
+ */
+public interface AccessReferenceMap<K> extends Serializable {
+
+	/**
+	 * Get an iterator through the direct object references. No guarantee is made as 
+	 * to the order of items returned.
+	 * 
+	 * @return the iterator
+	 */
+	Iterator iterator();
+
+	/**
+	 * Get a safe indirect reference to use in place of a potentially sensitive
+	 * direct object reference. Developers should use this call when building
+	 * URL's, form fields, hidden fields, etc... to help protect their private
+	 * implementation information.
+	 * 
+	 * @param directReference
+	 * 		the direct reference
+	 * 
+	 * @return 
+	 * 		the indirect reference
+	 */
+	<T> K getIndirectReference(T directReference);
+
+	/**
+	 * Get the original direct object reference from an indirect reference.
+	 * Developers should use this when they get an indirect reference from a
+	 * request to translate it back into the real direct reference. If an
+	 * invalid indirect reference is requested, then an AccessControlException is
+	 * thrown.
+    *
+    * If a type is implied the requested object will be cast to that type, if the
+    * object is not of the requested type, a AccessControlException will be thrown to
+    * the caller.
+    *
+    * For example:
+    * <pre>
+    * UserProfile profile = arm.getDirectReference( indirectRef );
+    * </pre>
+    *
+    * Will throw a AccessControlException if the object stored in memory is not of type
+    * UserProfile.
+    *
+    * However,
+    * <pre>
+    * Object uncastObject = arm.getDirectReference( indirectRef );
+    * </pre>
+    *
+    * Will never throw a AccessControlException as long as the object exists. If you are
+    * unsure of the object type of that an indirect reference references you should get
+    * the uncast object and test for type in the calling code.
+    * <pre>
+    * Object uncastProfile = arm.getDirectReference( indirectRef );
+    * if ( uncastProfile instanceof UserProfile ) {
+    *     UserProfile userProfile = (UserProfile) uncastProfile;
+    *     // ...
+    * } else {
+    *     EmployeeProfile employeeProfile = (EmployeeProfile) uncastProfile;
+    *     // ...
+    * }
+    * </pre>
+	 * 
+	 * @param indirectReference
+	 * 		the indirect reference
+	 * 
+	 * @return 
+	 * 		the direct reference
+	 * 
+	 * @throws AccessControlException 
+	 * 		if no direct reference exists for the specified indirect reference
+    * @throws ClassCastException
+    *       if the implied type is not the same as the referenced object type
+	 */
+	<T> T getDirectReference(K indirectReference) throws AccessControlException;
+
+	/**
+	 * Adds a direct reference to the AccessReferenceMap, then generates and returns 
+	 * an associated indirect reference.
+	 *  
+	 * @param direct 
+	 * 		the direct reference
+	 * 
+	 * @return 
+	 * 		the corresponding indirect reference
+	 */
+	<T> K addDirectReference(T direct);
+	
+	/**
+	 * Removes a direct reference and its associated indirect reference from the AccessReferenceMap.
+	 * 
+	 * @param direct 
+	 * 		the direct reference to remove
+	 * 
+	 * @return 
+	 * 		the corresponding indirect reference
+	 * 
+	 * @throws AccessControlException
+    *          if the reference does not exist.
+	 */
+	<T> K removeDirectReference(T direct) throws AccessControlException;
+
+	/**
+	 * Updates the access reference map with a new set of direct references, maintaining
+	 * any existing indirect references associated with items that are in the new list.
+	 * New indirect references could be generated every time, but that
+	 * might mess up anything that previously used an indirect reference, such
+	 * as a URL parameter. 
+	 * 
+	 * @param directReferences
+	 * 		a Set of direct references to add
+	 */
+	void update(Set directReferences);
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/Authenticator.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/Authenticator.java.svn-base
new file mode 100644
index 0000000..9cb4013
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/Authenticator.java.svn-base
@@ -0,0 +1,324 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Set;
+
+
+/**
+ * The Authenticator interface defines a set of methods for generating and
+ * handling account credentials and session identifiers. The goal of this
+ * interface is to encourage developers to protect credentials from disclosure
+ * to the maximum extent possible.
+ * <P>
+ * One possible implementation relies on the use of a thread local variable to
+ * store the current user's identity. The application is responsible for calling
+ * setCurrentUser() as soon as possible after each HTTP request is received. The
+ * value of getCurrentUser() is used in several other places in this API. This
+ * eliminates the need to pass a user object to methods throughout the library.
+ * For example, all of the logging, access control, and exception calls need
+ * access to the currently logged in user.
+ * <P>
+ * The goal is to minimize the responsibility of the developer for
+ * authentication. In this example, the user simply calls authenticate with the
+ * current request and the name of the parameters containing the username and
+ * password. The implementation should verify the password if necessary, create
+ * a session if necessary, and set the user as the current user.
+ * 
+ * <pre>
+ * public void doPost(ServletRequest request, ServletResponse response) {
+ * try {
+ * User user = ESAPI.authenticator().login(request, response);
+ * // continue with authenticated user
+ * } catch (AuthenticationException e) {
+ * // handle failed authentication (it's already been logged)
+ * }
+ * </pre>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Authenticator {
+
+	/**
+	 * Clears the current User. This allows the thread to be reused safely.
+     * 
+     * This clears all threadlocal variables from the thread. This should ONLY be called after
+     * all possible ESAPI operations have concluded. If you clear too early, many calls will
+     * fail, including logging, which requires the user identity.  
+	 */
+	void clearCurrent();
+
+	/**
+	 * Calls login with the *current* request and response.
+	 * @return Authenticated {@code User} if login is successful.
+	 * @see HTTPUtilities#setCurrentHTTP(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+	 */
+	User login() throws AuthenticationException;
+	
+	/**
+	 * This method should be called for every HTTP request, to login the current user either from the session of HTTP
+     * request. This method will set the current user so that getCurrentUser() will work properly. 
+	 * 
+	 * Authenticates the user's credentials from the HttpServletRequest if
+	 * necessary, creates a session if necessary, and sets the user as the
+	 * current user.
+	 * 
+	 * Specification:  The implementation should do the following:
+     * 	1) Check if the User is already stored in the session
+     * 		a. If so, check that session absolute and inactivity timeout have not expired
+     * 		b. Step 2 may not be required if 1a has been satisfied
+     * 	2) Verify User credentials
+     * 		a. It is recommended that you use 
+     * 			loginWithUsernameAndPassword(HttpServletRequest, HttpServletResponse) to verify credentials
+     * 	3) Set the last host of the User (ex.  user.setLastHostAddress(address) )
+     * 	4) Verify that the request is secure (ex. over SSL)
+     * 	5) Verify the User account is allowed to be logged in
+     * 		a. Verify the User is not disabled, expired or locked
+     * 	6) Assign User to session variable      	
+	 * 
+	 * @param request
+	 *            the current HTTP request
+	 * @param response
+	 *            the HTTP response
+	 * 
+	 * @return 
+	 * 		the User
+	 * 
+	 * @throws AuthenticationException
+	 *             if the credentials are not verified, or if the account is disabled, locked, expired, or timed out
+	 */
+	User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException;
+
+	/**
+	 * Verify that the supplied password matches the password for this user. Password should
+	 * be stored as a hash. It is recommended you use the hashPassword(password, accountName) method
+	 * in this class.
+	 * This method is typically used for "reauthentication" for the most sensitive functions, such
+	 * as transactions, changing email address, and changing other account information.
+	 * 
+	 * @param user 
+	 * 		the user who requires verification
+	 * @param password 
+	 * 		the hashed user-supplied password
+	 * 
+	 * @return 
+	 * 		true, if the password is correct for the specified user
+	 */
+	boolean verifyPassword(User user, String password);
+	
+	/**
+	 * Logs out the current user.
+	 * 
+	 * This is usually done by calling User.logout on the current User. 
+	 */
+    void logout();
+
+	/**
+	 * Creates a new User with the information provided. Implementations should check
+	 * accountName and password for proper format and strength against brute force 
+	 * attacks ( verifyAccountNameStrength(String), verifyPasswordStrength(String, String)  ).
+	 * 
+	 * Two copies of the new password are required to encourage user interface designers to
+	 * include a "re-type password" field in their forms. Implementations should verify that 
+	 * both are the same. 
+	 * 
+	 * @param accountName 
+	 * 		the account name of the new user
+	 * @param password1 
+	 * 		the password of the new user
+	 * @param password2 
+	 * 		the password of the new user.  This field is to encourage user interface designers to include two password fields in their forms.
+	 * 
+	 * @return 
+	 * 		the User that has been created 
+	 * 
+	 * @throws AuthenticationException 
+	 * 		if user creation fails due to any of the qualifications listed in this method's description
+	 */
+	User createUser(String accountName, String password1, String password2) throws AuthenticationException;
+
+	/**
+	 * Generate a strong password. Implementations should use a large character set that does not
+	 * include confusing characters, such as i I 1 l 0 o and O.  There are many algorithms to
+	 * generate strong memorable passwords that have been studied in the past.
+	 * 
+	 * @return 
+	 * 		a password with strong password strength
+	 */
+	String generateStrongPassword();
+
+	/**
+	 * Generate strong password that takes into account the user's information and old password. Implementations
+	 * should verify that the new password does not include information such as the username, fragments of the
+	 * old password, and other information that could be used to weaken the strength of the password.
+	 * 
+	 * @param user 
+	 * 		the user whose information to use when generating password
+	 * @param oldPassword 
+	 * 		the old password to use when verifying strength of new password.  The new password may be checked for fragments of oldPassword.
+	 * 
+	 * @return 
+	 * 		a password with strong password strength
+	 */
+	String generateStrongPassword(User user, String oldPassword);
+
+	/**
+	 * Changes the password for the specified user. This requires the current password, as well as 
+	 * the password to replace it with. The new password should be checked against old hashes to be sure the new password does not closely resemble or equal any recent passwords for that User.
+	 * Password strength should also be verified.  This new password must be repeated to ensure that the user has typed it in correctly.
+	 * 
+	 * @param user 
+	 * 		the user to change the password for
+	 * @param currentPassword 
+	 * 		the current password for the specified user
+	 * @param newPassword 
+	 * 		the new password to use
+	 * @param newPassword2 
+	 * 		a verification copy of the new password
+	 * 
+	 * @throws AuthenticationException 
+	 * 		if any errors occur
+	 */
+	void changePassword(User user, String currentPassword, String newPassword, String newPassword2) throws AuthenticationException;
+	
+	/**
+	 * Returns the User matching the provided accountId.  If the accoundId is not found, an Anonymous
+	 * User or null may be returned.
+	 * 
+	 * @param accountId
+	 *            the account id
+	 * 
+	 * @return 
+	 * 		the matching User object, or the Anonymous User if no match exists
+	 */
+	User getUser(long accountId);
+		
+	/**
+	 * Returns the User matching the provided accountName.  If the accoundId is not found, an Anonymous
+	 * User or null may be returned.
+	 * 
+	 * @param accountName
+	 *            the account name
+	 * 
+	 * @return 
+	 * 		the matching User object, or the Anonymous User if no match exists
+	 */
+	User getUser(String accountName);
+
+	/**
+	 * Gets a collection containing all the existing user names.
+	 * 
+	 * @return 
+	 * 		a set of all user names
+	 */
+	Set getUserNames();
+
+	/**
+	 * Returns the currently logged in User.
+	 * 
+	 * @return 
+	 * 		the matching User object, or the Anonymous User if no match
+	 *         exists
+	 */
+	User getCurrentUser();
+
+	/**
+	 * Sets the currently logged in User.
+	 * 
+	 * @param user
+	 *          the user to set as the current user
+	 */
+	void setCurrentUser(User user);
+
+	/**
+	 * Returns a string representation of the hashed password, using the
+	 * accountName as the salt. The salt helps to prevent against "rainbow"
+	 * table attacks where the attacker pre-calculates hashes for known strings.
+	 * This method specifies the use of the user's account name as the "salt"
+	 * value. The Encryptor.hash method can be used if a different salt is
+	 * required.
+	 * 
+	 * @param password
+	 *            the password to hash
+	 * @param accountName
+	 *            the account name to use as the salt
+	 * 
+	 * @return 
+     * 		the hashed password
+     * @throws EncryptionException
+	 */
+	String hashPassword(String password, String accountName) throws EncryptionException;
+
+	/**
+	 * Removes the account of the specified accountName.
+	 * 
+	 * @param accountName
+	 *            the account name to remove
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception if user does not exist
+	 */
+	void removeUser(String accountName) throws AuthenticationException;
+
+	/**
+	 * Ensures that the account name passes site-specific complexity requirements, like minimum length.
+	 * 
+	 * @param accountName
+	 *            the account name
+	 * 
+	 * @throws AuthenticationException
+	 *             if account name does not meet complexity requirements
+	 */
+	void verifyAccountNameStrength(String accountName) throws AuthenticationException;
+
+	/**
+	 * Ensures that the password meets site-specific complexity requirements, like length or number 
+	 * of character sets. This method takes the old password so that the algorithm can analyze the 
+	 * new password to see if it is too similar to the old password. Note that this has to be
+	 * invoked when the user has entered the old password, as the list of old
+	 * credentials stored by ESAPI is all hashed.
+	 * Additionally, the user object is taken in order to verify the password and account name differ.
+	 * 
+	 * @param oldPassword
+	 *            the old password
+	 * @param newPassword
+	 *            the new password
+	 * @param user
+	 * 			  the user
+	 * 
+	 * @throws AuthenticationException
+	 *				if newPassword is too similar to oldPassword or if newPassword does not meet complexity requirements
+	 */
+	void verifyPasswordStrength(String oldPassword, String newPassword, User user) throws AuthenticationException;
+
+	/**
+	 * Determine if the account exists.
+	 * 
+	 * @param accountName
+	 *            the account name
+	 * 
+	 * @return true, if the account exists
+	 */
+	boolean exists(String accountName);
+
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/ESAPI.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/ESAPI.java.svn-base
new file mode 100644
index 0000000..de93a73
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/ESAPI.java.svn-base
@@ -0,0 +1,224 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2008 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Mike Fauzy <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Rogan Dawes <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2008
+ */
+package org.owasp.esapi;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.util.ObjFactory;
+
+/**
+ * ESAPI locator class is provided to make it easy to gain access to the current ESAPI classes in use.
+ * Use the set methods to override the reference implementations with instances of any custom ESAPI implementations.
+ */
+public final class ESAPI {
+	private static String securityConfigurationImplName = System.getProperty("org.owasp.esapi.SecurityConfiguration", "org.owasp.esapi.reference.DefaultSecurityConfiguration");
+
+	/**
+	 * prevent instantiation of this class
+	 */
+	private ESAPI() {
+	}
+	
+    /**
+	 * Clears the current User, HttpRequest, and HttpResponse associated with the current thread. This method
+	 * MUST be called as some containers do not properly clear threadlocal variables when the execution of
+	 * a thread is complete. The suggested approach is to put this call in a finally block inside a filter.
+	 * <pre>
+		public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException {
+			try {
+				HttpServletRequest request = (HttpServletRequest) req;
+				HttpServletResponse response = (HttpServletResponse) resp;
+				ESAPI.httpUtilities().setCurrentHTTP(request, response);
+				ESAPI.authenticator().login();
+				chain.doFilter(request, response);
+			} catch (Exception e) {
+				logger.error( Logger.SECURITY_FAILURE, "Error in ESAPI security filter: " + e.getMessage(), e );
+			} finally {
+				// VERY IMPORTANT
+				// clear out ThreadLocal variables
+				ESAPI.clearCurrent();
+			}
+		}
+	 * </pre>
+	 * The advantages of having identity everywhere are worth the risk here.
+	 */
+	public static void clearCurrent() {
+		authenticator().clearCurrent();
+		httpUtilities().clearCurrent();
+	}
+
+	/**
+	 * Get the current HTTP Servlet Request being processed.
+	 * @return the current HTTP Servlet Request.
+	 */
+	public static HttpServletRequest currentRequest() {
+		return httpUtilities().getCurrentRequest();
+	}
+	
+	/**
+	 * Get the current HTTP Servlet Response being generated.
+	 * @return the current HTTP Servlet Response.
+	 */
+	public static HttpServletResponse currentResponse() {
+		return httpUtilities().getCurrentResponse();
+	}
+	
+	/**
+	 * @return the current ESAPI AccessController object being used to maintain the access control rules for this application. 
+	 */
+	public static AccessController accessController() {
+        return ObjFactory.make( securityConfiguration().getAccessControlImplementation(), "AccessController" );
+	}
+
+	/**
+	 * @return the current ESAPI Authenticator object being used to authenticate users for this application. 
+	 */
+	public static Authenticator authenticator() {
+        return ObjFactory.make( securityConfiguration().getAuthenticationImplementation(), "Authenticator" );
+	}
+
+	/**
+	 * @return the current ESAPI Encoder object being used to encode and decode data for this application. 
+	 */
+	public static Encoder encoder() {
+        return ObjFactory.make( securityConfiguration().getEncoderImplementation(), "Encoder" );
+	}
+
+	/**
+	 * @return the current ESAPI Encryptor object being used to encrypt and decrypt data for this application. 
+	 */
+	public static Encryptor encryptor() {
+        return ObjFactory.make( securityConfiguration().getEncryptionImplementation(), "Encryptor" );
+	}
+
+	/**
+	 * @return the current ESAPI Executor object being used to safely execute OS commands for this application. 
+	 */
+	public static Executor executor() {
+        return ObjFactory.make( securityConfiguration().getExecutorImplementation(), "Executor" );
+	}
+
+	/**
+	 * @return the current ESAPI HTTPUtilities object being used to safely access HTTP requests and responses 
+	 * for this application. 
+	 */
+	public static HTTPUtilities httpUtilities() {
+        return ObjFactory.make( securityConfiguration().getHTTPUtilitiesImplementation(), "HTTPUtilities" );
+	}
+
+	/**
+	 * @return the current ESAPI IntrusionDetector being used to monitor for intrusions in this application. 
+	 */
+	public static IntrusionDetector intrusionDetector() {
+        return ObjFactory.make( securityConfiguration().getIntrusionDetectionImplementation(), "IntrusionDetector" );
+	}
+
+	/**
+	 * Get the current LogFactory being used by ESAPI. If there isn't one yet, it will create one, and then 
+	 * return this same LogFactory from then on.
+	 * @return The current LogFactory being used by ESAPI.
+	 */
+	private static LogFactory logFactory() {
+        return ObjFactory.make( securityConfiguration().getLogImplementation(), "LogFactory" );
+	}
+	
+	/**
+	 * @param clazz The class to associate the logger with.
+	 * @return The current Logger associated with the specified class.
+	 */
+	@SuppressWarnings("unchecked")		// Because Eclipse wants Class<T> instead.
+	public static Logger getLogger(Class clazz) {
+		return logFactory().getLogger(clazz);
+	}
+	
+	/**
+	 * @param moduleName The module to associate the logger with.
+	 * @return The current Logger associated with the specified module.
+	 */
+	public static Logger getLogger(String moduleName) {
+		return logFactory().getLogger(moduleName);
+	}
+	
+	/**
+	 * @return The default Logger.
+	 */
+	public static Logger log() {
+        return logFactory().getLogger("DefaultLogger");
+    }
+	
+	/**
+	 * @return the current ESAPI Randomizer being used to generate random numbers in this application. 
+	 */
+	public static Randomizer randomizer() {
+        return ObjFactory.make( securityConfiguration().getRandomizerImplementation(), "Randomizer" );
+	}
+
+    private static volatile SecurityConfiguration overrideConfig = null;
+
+	/**
+	 * @return the current ESAPI SecurityConfiguration being used to manage the security configuration for 
+	 * ESAPI for this application. 
+	 */
+	public static SecurityConfiguration securityConfiguration() {
+		// copy the volatile into a non-volatile to prevent TOCTTOU race condition
+		SecurityConfiguration override = overrideConfig;
+		if ( override != null ) {
+			return override;
+        }
+
+        return ObjFactory.make( securityConfigurationImplName, "SecurityConfiguration" );
+	}
+
+	/**
+	 * @return the current ESAPI Validator being used to validate data in this application. 
+	 */
+	public static Validator validator() {
+        return ObjFactory.make( securityConfiguration().getValidationImplementation(), "Validator" );
+	}
+
+    // TODO: This should probably use the SecurityManager or some value within the current
+    // securityConfiguration to determine if this method is allowed to be called. This could
+    // allow for unit tests internal to ESAPI to modify the configuration for the purpose of
+    // testing stuff, and allow developers to allow this in development environments but make
+    // it so the securityConfiguration implementation *cannot* be modified in production environments.
+    //
+    // The purpose of this method is to replace the functionality provided by the setSecurityConfiguration
+    // method that is no longer on this class, and allow the context configuration of the ESAPI
+    // to be modified at Runtime.
+    public static String initialize( String impl ) {
+        String oldImpl = securityConfigurationImplName;
+        securityConfigurationImplName = impl;
+        return oldImpl;
+    }
+
+    /**
+     * Overrides the current security configuration with a new implementation. This is meant
+     * to be used as a temporary means to alter the behavior of the ESAPI and should *NEVER*
+     * be used in a production environment as it will affect the behavior and configuration of
+     * the ESAPI *GLOBALLY*.
+     *
+     * To clear an overridden Configuration, simple call this method with null for the config
+     * parameter.
+     *
+     * @param config
+     * @return
+     */
+    public static void override( SecurityConfiguration config ) {
+        overrideConfig = config;
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/Encoder.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/Encoder.java.svn-base
new file mode 100644
index 0000000..3bce42f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/Encoder.java.svn-base
@@ -0,0 +1,516 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.IOException;
+
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.errors.EncodingException;
+
+
+/**
+ * The Encoder interface contains a number of methods for decoding input and encoding output
+ * so that it will be safe for a variety of interpreters. To prevent
+ * double-encoding, callers should make sure input does not already contain encoded characters
+ * by calling canonicalize. Validator implementations should call canonicalize on user input
+ * <b>before</b> validating to prevent encoded attacks.
+ * <p>
+ * All of the methods must use a "whitelist" or "positive" security model.
+ * For the encoding methods, this means that all characters should be encoded, except for a specific list of
+ * "immune" characters that are known to be safe.
+ * <p>
+ * The Encoder performs two key functions, encoding and decoding. These functions rely
+ * on a set of codecs that can be found in the org.owasp.esapi.codecs package. These include:
+ * <ul><li>CSS Escaping</li>
+ * <li>HTMLEntity Encoding</li>
+ * <li>JavaScript Escaping</li>
+ * <li>MySQL Escaping</li>
+ * <li>Oracle Escaping</li>
+ * <li>Percent Encoding (aka URL Encoding)</li>
+ * <li>Unix Escaping</li>
+ * <li>VBScript Escaping</li>
+ * <li>Windows Encoding</li></ul>
+ * <p>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Encoder {
+	
+	/**
+	 * Standard character sets
+	 */
+
+	/**  
+	 * @deprecated Use {@link EncoderConstants#CHAR_LOWERS} instead
+	 */
+	@Deprecated
+	public final static char[] CHAR_LOWERS = EncoderConstants.CHAR_LOWERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_UPPERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_UPPERS = EncoderConstants.CHAR_UPPERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_DIGITS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_DIGITS = EncoderConstants.CHAR_DIGITS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_SPECIALS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_SPECIALS = EncoderConstants.CHAR_SPECIALS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_LETTERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_LETTERS = EncoderConstants.CHAR_LETTERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_ALPHANUMERICS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_ALPHANUMERICS = EncoderConstants.CHAR_ALPHANUMERICS;
+	
+	
+	/**
+	 * Password character set, is alphanumerics (without l, i, I, o, O, and 0)
+	 * selected specials like + (bad for URL encoding, | is like i and 1,
+	 * etc...)
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LOWERS} instead
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_LOWERS = EncoderConstants.CHAR_PASSWORD_LOWERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_UPPERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_UPPERS = EncoderConstants.CHAR_PASSWORD_UPPERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_DIGITS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_DIGITS = EncoderConstants.CHAR_PASSWORD_DIGITS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_SPECIALS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_SPECIALS = EncoderConstants.CHAR_PASSWORD_SPECIALS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LETTERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_LETTERS = EncoderConstants.CHAR_PASSWORD_LETTERS;
+    
+
+
+	/**
+	 * This method is equivalent to calling <pre>Encoder.canonicalize(input, restrictMultiple, restrictMixed);</pre>
+	 *
+	 * The default values for restrictMultiple and restrictMixed come from ESAPI.properties
+	 * <pre>
+	 * Encoder.AllowMultipleEncoding=false
+	 * Encoder.AllowMixedEncoding=false
+	 * </pre>
+	 *
+	 * @see Encoder#canonicalize(String, boolean, boolean) canonicalize
+	 * @see <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4">W3C specifications</a>
+	 * 
+	 * @param input the text to canonicalize
+	 * @return a String containing the canonicalized text
+	 */
+	String canonicalize(String input);
+	
+	/**
+	 * This method is the equivalent to calling <pre>Encoder.canonicalize(input, strict, strict);</pre>
+	 *
+	 * @see Encoder#canonicalize(String, boolean, boolean) canonicalize
+	 * @see <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4">W3C specifications</a>
+	 *  
+	 * @param input 
+	 * 		the text to canonicalize
+	 * @param strict 
+	 * 		true if checking for multiple and mixed encoding is desired, false otherwise
+	 * 
+	 * @return a String containing the canonicalized text
+	 */
+	String canonicalize(String input, boolean strict);
+
+	/**
+	 * Canonicalization is simply the operation of reducing a possibly encoded
+	 * string down to its simplest form. This is important, because attackers
+	 * frequently use encoding to change their input in a way that will bypass
+	 * validation filters, but still be interpreted properly by the target of
+	 * the attack. Note that data encoded more than once is not something that a
+	 * normal user would generate and should be regarded as an attack.
+	 * <p>
+     * Everyone <a href="http://cwe.mitre.org/data/definitions/180.html">says</a> you shouldn't do validation
+     * without canonicalizing the data first. This is easier said than done. The canonicalize method can
+     * be used to simplify just about any input down to its most basic form. Note that canonicalize doesn't
+     * handle Unicode issues, it focuses on higher level encoding and escaping schemes. In addition to simple
+     * decoding, canonicalize also handles:
+     * <ul><li>Perverse but legal variants of escaping schemes</li>
+     * <li>Multiple escaping (%2526 or &#x26;lt;)</li>
+     * <li>Mixed escaping (%26lt;)</li>
+     * <li>Nested escaping (%%316 or &%6ct;)</li>
+     * <li>All combinations of multiple, mixed, and nested encoding/escaping (%2&#x35;3c or &#x2526gt;)</li></ul>
+     * <p>
+     * Using canonicalize is simple. The default is just...
+     * <pre>
+     *     String clean = ESAPI.encoder().canonicalize( request.getParameter("input"));
+     * </pre>
+     * You need to decode untrusted data so that it's safe for ANY downstream interpreter or decoder. For
+     * example, if your data goes into a Windows command shell, then into a database, and then to a browser,
+     * you're going to need to decode for all of those systems. You can build a custom encoder to canonicalize
+     * for your application like this...
+     * <pre>
+     *     ArrayList list = new ArrayList();
+     *     list.add( new WindowsCodec() );
+     *     list.add( new MySQLCodec() );
+     *     list.add( new PercentCodec() );
+     *     Encoder encoder = new DefaultEncoder( list );
+     *     String clean = encoder.canonicalize( request.getParameter( "input" ));
+     * </pre>
+     * In ESAPI, the Validator uses the canonicalize method before it does validation.  So all you need to
+     * do is to validate as normal and you'll be protected against a host of encoded attacks.
+     * <pre>
+     *     String input = request.getParameter( "name" );
+     *     String name = ESAPI.validator().isValidInput( "test", input, "FirstName", 20, false);
+     * </pre>
+     * However, the default canonicalize() method only decodes HTMLEntity, percent (URL) encoding, and JavaScript
+     * encoding. If you'd like to use a custom canonicalizer with your validator, that's pretty easy too.
+     * <pre>
+     *     ... setup custom encoder as above
+     *     Validator validator = new DefaultValidator( encoder );
+     *     String input = request.getParameter( "name" );
+     *     String name = validator.isValidInput( "test", input, "name", 20, false);
+     * </pre>
+     * Although ESAPI is able to canonicalize multiple, mixed, or nested encoding, it's safer to not accept
+     * this stuff in the first place. In ESAPI, the default is "strict" mode that throws an IntrusionException
+     * if it receives anything not single-encoded with a single scheme. This is configurable
+     * in ESAPI.properties using the properties:
+	 * <pre>
+	 * Encoder.AllowMultipleEncoding=false
+	 * Encoder.AllowMixedEncoding=false
+	 * </pre>
+	 * This method allows you to override the default behavior by directly specifying whether to restrict
+	 * multiple or mixed encoding. Even if you disable restrictions, you'll still get
+     * warning messages in the log about each multiple encoding and mixed encoding received.
+     * <pre>
+     *     // disabling strict mode to allow mixed encoding
+     *     String url = ESAPI.encoder().canonicalize( request.getParameter("url"), false, false);
+     * </pre>
+	 *
+	 * @see <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4">W3C specifications</a>
+	 *
+	 * @param input
+	 * 		the text to canonicalize
+	 * @param restrictMultiple
+	 * 		true if checking for multiple encoding is desired, false otherwise
+	 * @param restrictMixed
+	 * 		true if checking for mixed encoding is desired, false otherwise
+	 *
+	 * @return a String containing the canonicalized text
+	 */
+	String canonicalize(String input, boolean restrictMultiple, boolean restrictMixed);
+
+	/**
+	 * Encode data for use in Cascading Style Sheets (CSS) content.
+	 * 
+	 * @see <a href="http://www.w3.org/TR/CSS21/syndata.html#escaped-characters">CSS Syntax [w3.org]</a>
+	 * 
+	 * @param input 
+	 * 		the text to encode for CSS
+	 * 
+	 * @return input encoded for CSS
+	 */
+	String encodeForCSS(String input);
+
+	/**
+	 * Encode data for use in HTML using HTML entity encoding
+	 * <p> 
+	 * Note that the following characters:
+	 * 00-08, 0B-0C, 0E-1F, and 7F-9F
+	 * <p>cannot be used in HTML. 
+	 * 
+	 * @see <a href="http://en.wikipedia.org/wiki/Character_encodings_in_HTML">HTML Encodings [wikipedia.org]</a> 
+	 * @see <a href="http://www.w3.org/TR/html4/sgml/sgmldecl.html">SGML Specification [w3.org]</a>
+     * @see <a href="http://www.w3.org/TR/REC-xml/#charsets">XML Specification [w3.org]</a>
+	 * 
+	 * @param input 
+	 * 		the text to encode for HTML
+	 * 
+	 * @return input encoded for HTML
+	 */
+	String encodeForHTML(String input);
+
+	/**
+     * Decodes HTML entities.
+     * @param input the <code>String</code> to decode
+     * @return the newly decoded <code>String</code>
+     */
+	String decodeForHTML(String input);
+		
+	/**
+	 * Encode data for use in HTML attributes.
+	 * 
+	 * @param input 
+	 * 		the text to encode for an HTML attribute
+	 * 
+	 * @return input encoded for use as an HTML attribute
+	 */
+	String encodeForHTMLAttribute(String input);
+
+
+    /**
+     * Encode data for insertion inside a data value or function argument in JavaScript. Including user data 
+     * directly inside a script is quite dangerous. Great care must be taken to prevent including user data
+     * directly into script code itself, as no amount of encoding will prevent attacks there.
+     * 
+     * Please note there are some JavaScript functions that can never safely receive untrusted data 
+     * as input – even if the user input is encoded.
+     * 
+     * For example:
+     * <pre>
+     *  <script>
+     *      window.setInterval('<%= EVEN IF YOU ENCODE UNTRUSTED DATA YOU ARE XSSED HERE %>');
+     *  </script>
+     * </pre>
+     * @param input 
+     *          the text to encode for JavaScript
+     * 
+     * @return input encoded for use in JavaScript
+     */
+	String encodeForJavaScript(String input);
+
+	/**
+	 * Encode data for insertion inside a data value in a Visual Basic script. Putting user data directly
+	 * inside a script is quite dangerous. Great care must be taken to prevent putting user data
+	 * directly into script code itself, as no amount of encoding will prevent attacks there.
+	 * 
+	 * This method is not recommended as VBScript is only supported by Internet Explorer
+	 * 
+	 * @param input 
+	 * 		the text to encode for VBScript
+	 * 
+	 * @return input encoded for use in VBScript
+	 */
+	String encodeForVBScript(String input);
+
+
+	/**
+	 * Encode input for use in a SQL query, according to the selected codec 
+	 * (appropriate codecs include the MySQLCodec and OracleCodec).
+	 * 
+	 * This method is not recommended. The use of the PreparedStatement 
+	 * interface is the preferred approach. However, if for some reason 
+	 * this is impossible, then this method is provided as a weaker 
+	 * alternative. 
+	 * 
+	 * The best approach is to make sure any single-quotes are double-quoted.
+	 * Another possible approach is to use the {escape} syntax described in the
+	 * JDBC specification in section 1.5.6.
+	 * 
+	 * However, this syntax does not work with all drivers, and requires
+	 * modification of all queries.
+	 * 
+	 * @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/statement.html">JDBC Specification</a>
+	 *  
+	 * @param codec 
+	 * 		a Codec that declares which database 'input' is being encoded for (ie. MySQL, Oracle, etc.)
+	 * @param input 
+	 * 		the text to encode for SQL
+	 * 
+	 * @return input encoded for use in SQL
+	 */
+	String encodeForSQL(Codec codec, String input);
+
+    /**
+     * Encode for an operating system command shell according to the selected codec (appropriate codecs include the WindowsCodec and UnixCodec). 
+     *
+     * Please note the following recommendations before choosing to use this method: 
+     * 
+     * 1)      It is strongly recommended that applications avoid making direct OS system calls if possible as such calls are not portable, and they are potentially unsafe. Please use language provided features if at all possible, rather than native OS calls to implement the desired feature.
+     * 2)      If an OS call cannot be avoided, then it is recommended that the program to be invoked be invoked directly (e.g., System.exec("nameofcommand" + "parameterstocommand");) as this avoids the use of the command shell. The "parameterstocommand" should of course be validated before passing them to the OS command.
+     * 3)      If you must use this method, then we recommend validating all user supplied input passed to the command shell as well, in addition to using this method in order to make the command shell invocation safe.
+     *  
+     * An example use of this method would be: System.exec("dir " + ESAPI.encodeForOS(WindowsCodec, "parameter(s)tocommandwithuserinput");
+     * 
+     * @param codec 
+     *      a Codec that declares which operating system 'input' is being encoded for (ie. Windows, Unix, etc.)
+     * @param input 
+     *      the text to encode for the command shell
+     * 
+     * @return input encoded for use in command shell
+     */
+	String encodeForOS(Codec codec, String input);
+
+	/**
+	 * Encode data for use in LDAP queries.
+	 * 
+	 * @param input 
+	 * 		the text to encode for LDAP
+	 * 
+	 * @return input encoded for use in LDAP
+	 */
+	String encodeForLDAP(String input);
+
+	/**
+	 * Encode data for use in an LDAP distinguished name.
+	 * 
+	 *  @param input 
+	 *  		the text to encode for an LDAP distinguished name
+	 * 
+	 *  @return input encoded for use in an LDAP distinguished name
+	 */
+	String encodeForDN(String input);
+
+	/**
+	 * Encode data for use in an XPath query.
+	 * 
+	 * NB: The reference implementation encodes almost everything and may over-encode. 
+	 * 
+	 * The difficulty with XPath encoding is that XPath has no built in mechanism for escaping
+	 * characters. It is possible to use XQuery in a parameterized way to
+	 * prevent injection. 
+	 * 
+	 * For more information, refer to <a
+	 * href="http://www.ibm.com/developerworks/xml/library/x-xpathinjection.html">this
+	 * article</a> which specifies the following list of characters as the most
+	 * dangerous: ^&"*';<>(). <a
+	 * href="http://www.packetstormsecurity.org/papers/bypass/Blind_XPath_Injection_20040518.pdf">This
+	 * paper</a> suggests disallowing ' and " in queries.
+	 * 
+	 * @see <a href="http://www.ibm.com/developerworks/xml/library/x-xpathinjection.html">XPath Injection [ibm.com]</a>
+	 * @see <a href="http://www.packetstormsecurity.org/papers/bypass/Blind_XPath_Injection_20040518.pdf">Blind XPath Injection [packetstormsecurity.org]</a>
+	 *  
+	 * @param input
+	 *      the text to encode for XPath
+	 * @return 
+	 * 		input encoded for use in XPath
+	 */
+	String encodeForXPath(String input);
+
+	/**
+	 * Encode data for use in an XML element. The implementation should follow the <a
+	 * href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding
+	 * Standard</a> from the W3C.
+	 * <p>
+	 * The use of a real XML parser is strongly encouraged. However, in the
+	 * hopefully rare case that you need to make sure that data is safe for
+	 * inclusion in an XML document and cannot use a parse, this method provides
+	 * a safe mechanism to do so.
+	 * 
+	 * @see <a href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding Standard</a>
+	 * 
+	 * @param input
+	 * 			the text to encode for XML
+	 * 
+	 * @return
+	 *			input encoded for use in XML
+	 */
+	String encodeForXML(String input);
+
+	/**
+	 * Encode data for use in an XML attribute. The implementation should follow
+	 * the <a href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding
+	 * Standard</a> from the W3C.
+	 * <p>
+	 * The use of a real XML parser is highly encouraged. However, in the
+	 * hopefully rare case that you need to make sure that data is safe for
+	 * inclusion in an XML document and cannot use a parse, this method provides
+	 * a safe mechanism to do so.
+	 * 
+	 * @see <a href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding Standard</a>
+	 * 
+	 * @param input
+	 * 			the text to encode for use as an XML attribute
+	 * 
+	 * @return 
+	 * 			input encoded for use in an XML attribute
+	 */
+	String encodeForXMLAttribute(String input);
+
+	/**
+	 * Encode for use in a URL. This method performs <a
+	 * href="http://en.wikipedia.org/wiki/Percent-encoding">URL encoding</a>
+	 * on the entire string.
+	 * 
+	 * @see <a href="http://en.wikipedia.org/wiki/Percent-encoding">URL encoding</a>
+	 * 
+	 * @param input 
+	 * 		the text to encode for use in a URL
+	 * 
+	 * @return input 
+	 * 		encoded for use in a URL
+	 * 
+	 * @throws EncodingException 
+	 * 		if encoding fails
+	 */
+	String encodeForURL(String input) throws EncodingException;
+
+	/**
+	 * Decode from URL. Implementations should first canonicalize and
+	 * detect any double-encoding. If this check passes, then the data is decoded using URL
+	 * decoding.
+	 * 
+	 * @param input 
+	 * 		the text to decode from an encoded URL
+	 * 
+	 * @return 
+	 * 		the decoded URL value
+	 * 
+	 * @throws EncodingException 
+	 * 		if decoding fails
+	 */
+	String decodeFromURL(String input) throws EncodingException;
+
+	/**
+	 * Encode for Base64.
+	 * 
+	 * @param input 
+	 * 		the text to encode for Base64
+	 * @param wrap
+	 * 		the encoder will wrap lines every 64 characters of output
+	 * 
+	 * @return input encoded for Base64
+	 */
+	String encodeForBase64(byte[] input, boolean wrap);
+
+	/**
+	 * Decode data encoded with BASE-64 encoding.
+	 * 
+	 * @param input 
+	 * 		the Base64 text to decode
+	 * 
+	 * @return input 
+	 * 		decoded from Base64
+	 * 
+	 * @throws IOException
+	 */
+	byte[] decodeFromBase64(String input) throws IOException;
+
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/EncoderConstants.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/EncoderConstants.java.svn-base
new file mode 100644
index 0000000..d148027
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/EncoderConstants.java.svn-base
@@ -0,0 +1,117 @@
+package org.owasp.esapi;
+
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * Common character classes used for input validation, output encoding, verifying password strength
+ * CSRF token generation, generating salts, etc
+ * @author Neil Matatall (neil.matatall .at. gmail.com)
+ * @see User
+ */
+public class EncoderConstants {
+	/**
+	 * !$*-.=?@_
+	 */
+	public final static char[] CHAR_PASSWORD_SPECIALS = { '!', '$', '*', '-', '.', '=', '?', '@', '_' };
+	public final static Set<Character> PASSWORD_SPECIALS;
+	static {
+		PASSWORD_SPECIALS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS);
+	}
+	
+	/**
+	 * a-b
+	 */
+	public final static char[] CHAR_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
+	public final static Set<Character> LOWERS;
+	static {
+		LOWERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS);
+	}
+	
+	/**
+	 * A-Z
+	 */
+	public final static char[] CHAR_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
+	public final static Set<Character> UPPERS;
+	static {
+		UPPERS = CollectionsUtil.arrayToSet(CHAR_UPPERS);
+	}
+	/**
+	 * 0-9
+	 */
+	public final static char[] CHAR_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+	public final static Set<Character> DIGITS;
+	static {
+		DIGITS = CollectionsUtil.arrayToSet(CHAR_DIGITS);
+	}
+	
+	/**
+	 * !$*+-.=?@^_|~
+	 */
+	public final static char[] CHAR_SPECIALS = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' };
+	public final static Set<Character> SPECIALS;
+	static {
+		SPECIALS = CollectionsUtil.arrayToSet(CHAR_SPECIALS);
+	}
+	
+	/**
+	 * CHAR_LOWERS union CHAR_UPPERS
+	 */
+	public final static char[] CHAR_LETTERS = StringUtilities.union(CHAR_LOWERS, CHAR_UPPERS);
+	public final static Set<Character> LETTERS;
+	static {
+		LETTERS = CollectionsUtil.arrayToSet(CHAR_LETTERS);
+	}
+	
+	/**
+	 * CHAR_LETTERS union CHAR_DIGITS
+	 */
+	public final static char[] CHAR_ALPHANUMERICS = StringUtilities.union(CHAR_LETTERS, CHAR_DIGITS);
+	public final static Set<Character> ALPHANUMERICS;
+	static {
+		ALPHANUMERICS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS);
+	}
+	
+	/**
+	 * Password character set, is alphanumerics (without l, i, I, o, O, and 0)
+	 * selected specials like + (bad for URL encoding, | is like i and 1,
+	 * etc...)
+	 */
+	public final static char[] CHAR_PASSWORD_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
+	public final static Set<Character> PASSWORD_LOWERS;
+	static {
+		PASSWORD_LOWERS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS);
+	}
+	
+	/**
+	 * 
+	 */
+	public final static char[] CHAR_PASSWORD_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
+	public final static Set<Character> PASSWORD_UPPERS;
+	static {
+		PASSWORD_UPPERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_UPPERS);
+	}
+	
+	/**
+	 * 2-9
+	 */
+	public final static char[] CHAR_PASSWORD_DIGITS = { '2', '3', '4', '5', '6', '7', '8', '9' };
+	public final static Set<Character> PASSWORD_DIGITS;
+	static {
+		PASSWORD_DIGITS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_DIGITS);
+	}
+	
+	/**
+	 * CHAR_PASSWORD_LOWERS union CHAR_PASSWORD_UPPERS
+	 */
+	public final static char[] CHAR_PASSWORD_LETTERS = StringUtilities.union( CHAR_PASSWORD_LOWERS, CHAR_PASSWORD_UPPERS );
+	public final static Set<Character> PASSWORD_LETTERS;
+	static {
+		PASSWORD_LETTERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_LETTERS);
+	}
+
+	private EncoderConstants() {
+		// prevent instantiation
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/EncryptedProperties.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/EncryptedProperties.java.svn-base
new file mode 100644
index 0000000..e95b1b8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/EncryptedProperties.java.svn-base
@@ -0,0 +1,112 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Set;
+
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The {@code EncryptedProperties} interface represents a properties file
+ * where all the data is encrypted before it is added, and decrypted when it
+ * retrieved. This interface can be implemented in a number of ways, the
+ * simplest being extending {@link java.util.Properties} and overloading
+ * the {@code getProperty} and {@code setProperty} methods. In all cases,
+ * the master encryption key, as given by the {@code Encryptor.MasterKey}
+ * property in <b><code>ESAPI.properties</code></b> file.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface EncryptedProperties {
+
+	/**
+	 * Gets the property value from the encrypted store, decrypts it, and
+	 * returns the plaintext value to the caller.
+	 * 
+	 * @param key
+	 *      the name of the property to get 
+	 * 
+	 * @return 
+	 * 	The decrypted property value. null if the key is not set.
+	 * 
+	 * @throws EncryptionException
+	 *      if the property could not be decrypted
+	 */
+	String getProperty(String key) throws EncryptionException;
+
+	/**
+	 * Encrypts the plaintext property value and stores the ciphertext value
+	 * in the encrypted store.
+	 * 
+	 * @param key
+	 *      the name of the property to set
+	 * @param value
+	 * 		the value of the property to set
+	 * 
+	 * @return 
+	 * 		the previously encrypted property value for the specified key, or
+	 *      {@code null} if it did not have one.
+	 * 
+	 * @throws EncryptionException
+	 *      if the property could not be encrypted
+	 */
+	String setProperty(String key, String value) throws EncryptionException;
+	
+	/**
+	 * Returns a {@code Set} view of properties. The {@code Set} is backed by a
+	 * {@code java.util.Hashtable}, so changes to the {@code Hashtable} are
+	 * reflected in the {@code Set}, and vice-versa. The {@code Set} supports element 
+	 * removal (which removes the corresponding entry from the {@code Hashtable),
+	 * but not element addition.
+	 * 
+	 * @return 
+	 * 		a set view of the properties contained in this map.
+	 */
+	public Set<?> keySet();
+		
+	/**
+	 * Reads a property list (key and element pairs) from the input stream.
+	 * 
+	 * @param in
+	 * 		the input stream that contains the properties file
+	 * 
+	 * @throws IOException
+	 *      Signals that an I/O exception has occurred.
+	 */
+	public void load(InputStream in) throws IOException;
+	
+	/**
+	 * Writes this property list (key and element pairs) in this Properties table to 
+	 * the output stream in a format suitable for loading into a Properties table using the load method. 
+	 * 
+	 * @param out
+	 * 		the output stream that contains the properties file
+	 * @param comments
+	 *            a description of the property list (ex. "Encrypted Properties File").
+	 * 
+	 * @throws IOException
+	 *             Signals that an I/O exception has occurred.
+	 */
+	public void store(OutputStream out, String comments) throws IOException;	
+	
+	
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/Encryptor.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/Encryptor.java.svn-base
new file mode 100644
index 0000000..35bf094
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/Encryptor.java.svn-base
@@ -0,0 +1,318 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2007,2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import javax.crypto.SecretKey;
+
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.IntegrityException;
+
+
+/**
+ * The Encryptor interface provides a set of methods for performing common
+ * encryption, random number, and hashing operations. Implementations should
+ * rely on a strong cryptographic implementation, such as JCE or BouncyCastle.
+ * Implementors should take care to ensure that they initialize their
+ * implementation with a strong "master key", and that they protect this secret
+ * as much as possible.
+ * <P>
+ * The main property controlling the selection of the implementation class is the
+ * property {@code ESAPI.Encryptor} in {@code ESAPI.properties}. Most of the
+ * the other encryption related properties have property names that start with
+ * the string "Encryptor.". These properties all you to do things such as
+ * select the encryption algorithms, the preferred JCE provider, etc.
+ * </P><P>
+ * In addition, there are two important properties (initially delivered as unset
+ * from the ESAPI download) named {@code Encryptor.MasterKey} and
+ * {@code Encryptor.MasterSalt} that must be set before using ESAPI encryption.
+ * There is a <i>bash</i>(1) shell script provided with the standard ESAPI distribution
+ * called 'setMasterKey.sh' that will assist you in setting these two properties. The
+ * script is in 'src/examples/scripts/setMasterKey.sh'.
+ * </P><P>
+ * Possible future enhancements (depending on feedback) are discussed in
+ * section 4 of
+ * <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-crypto-design-goals.doc">
+ * Design Goals in OWASP ESAPI Cryptography</a>.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html">User Guide for Symmetric Encryption in ESAPI 2.0</a>
+ */
+public interface Encryptor {
+
+	/**
+	 * Returns a string representation of the hash of the provided plaintext and
+	 * salt. The salt helps to protect against a rainbow table attack by mixing
+	 * in some extra data with the plaintext. Some good choices for a salt might
+	 * be an account name or some other string that is known to the application
+	 * but not to an attacker.
+	 * See <a href="http://www.matasano.com/log/958/enough-with-the-rainbow-tables-what-you-need-to-know-about-secure-password-schemes/">
+	 * this article</a> for more information about hashing as it pertains to password schemes.
+	 * 
+	 * @param plaintext
+	 * 		the plaintext String to encrypt
+	 * @param salt
+	 *      the salt to add to the plaintext String before hashing
+	 * 
+	 * @return 
+	 * 		the encrypted hash of 'plaintext' stored as a String
+	 * 
+	 * @throws EncryptionException
+	 *      if the specified hash algorithm could not be found or another problem exists with 
+	 *      the hashing of 'plaintext'
+	 */
+	String hash(String plaintext, String salt) throws EncryptionException;
+
+	/**
+	 * Returns a string representation of the hash of the provided plaintext and
+	 * salt. The salt helps to protect against a rainbow table attack by mixing
+	 * in some extra data with the plaintext. Some good choices for a salt might
+	 * be an account name or some other string that is known to the application
+	 * but not to an attacker. 
+	 * See <a href="http://www.matasano.com/log/958/enough-with-the-rainbow-tables-what-you-need-to-know-about-secure-password-schemes/">
+	 * this article</a> for more information about hashing as it pertains to password schemes.
+	 * 
+	 * @param plaintext
+	 * 		the plaintext String to encrypt
+	 * @param salt
+	 *      the salt to add to the plaintext String before hashing
+	 * @param iterations
+	 *      the number of times to iterate the hash
+	 * 
+	 * @return 
+	 * 		the encrypted hash of 'plaintext' stored as a String
+	 * 
+	 * @throws EncryptionException
+	 *      if the specified hash algorithm could not be found or another problem exists with 
+	 *      the hashing of 'plaintext'
+	 */
+	String hash(String plaintext, String salt, int iterations) throws EncryptionException;
+	
+	/**
+	 * Encrypts the provided plaintext bytes using the cipher transformation
+	 * specified by the property <code>Encryptor.CipherTransformation</code>
+	 * and the <i>master encryption key</i> as specified by the property
+	 * {@code Encryptor.MasterKey} as defined in the <code>ESAPI.properties</code> file.
+	 * </p>
+	 * 
+	 * @param plaintext	The {@code PlainText} to be encrypted.
+	 * @return the {@code CipherText} object from which the raw ciphertext, the
+	 * 				IV, the cipher transformation, and many other aspects about
+	 * 				the encryption detail may be extracted.
+	 * @throws EncryptionException Thrown if something should go wrong such as
+	 * 				the JCE provider cannot be found, the cipher algorithm,
+	 * 				cipher mode, or padding scheme not being supported, specifying
+	 * 				an unsupported key size, specifying an IV of incorrect length,
+	 * 				etc.
+	 * @see #encrypt(SecretKey, PlainText)
+	 * @since 2.0
+	 */
+	 CipherText encrypt(PlainText plaintext) throws EncryptionException;
+
+
+	 /**
+	  * Encrypts the provided plaintext bytes using the cipher transformation
+	  * specified by the property <code>Encryptor.CipherTransformation</code>
+	  * as defined in the <code>ESAPI.properties</code> file and the
+	  * <i>specified secret key</i>.
+	  * </p><p>
+	  * This method is similar to {@link #encrypt(PlainText)} except that it
+	  * permits a specific {@code SecretKey} to be used for encryption.
+	  *
+	  * @param key		The {@code SecretKey} to use for encrypting the plaintext.
+	  * @param plaintext	The byte stream to be encrypted. Note if a Java
+	  * 				{@code String} is to be encrypted, it should be converted
+	  * 				using {@code "some string".getBytes("UTF-8")}.
+	  * @return the {@code CipherText} object from which the raw ciphertext, the
+	  * 				IV, the cipher transformation, and many other aspects about
+	  * 				the encryption detail may be extracted.
+	  * @throws EncryptionException Thrown if something should go wrong such as
+	  * 				the JCE provider cannot be found, the cipher algorithm,
+	  * 				cipher mode, or padding scheme not being supported, specifying
+	  * 				an unsupported key size, specifying an IV of incorrect length,
+	  * 				etc.
+	  * @see #encrypt(PlainText)
+	  * @since 2.0
+	  */
+	 CipherText encrypt(SecretKey key, PlainText plaintext)
+	 		throws EncryptionException;
+
+	/**
+	 * Decrypts the provided {@link CipherText} using the information from it
+	 * and the <i>master encryption key</i> as specified by the property
+	 * {@code Encryptor.MasterKey} as defined in the {@code ESAPI.properties}
+	 * file.
+	 * </p>
+	 * @param ciphertext The {@code CipherText} object to be decrypted.
+	 * @return The {@code PlainText} object resulting from decrypting the specified
+	 * 		   ciphertext. Note that it it is desired to convert the returned
+	 * 		   plaintext byte array to a Java String is should be done using
+	 * 		   {@code new String(byte[], "UTF-8");} rather than simply using
+	 * 		   {@code new String(byte[]);} which uses native encoding and may
+	 * 		   not be portable across hardware and/or OS platforms.
+	 * @throws EncryptionException  Thrown if something should go wrong such as
+	 * 				the JCE provider cannot be found, the cipher algorithm,
+	 * 				cipher mode, or padding scheme not being supported, specifying
+	 * 				an unsupported key size, or incorrect encryption key was
+	 * 				specified or a {@code PaddingException} occurs.
+	 * @see #decrypt(SecretKey, CipherText)
+	 */
+	PlainText decrypt(CipherText ciphertext) throws EncryptionException;
+	
+	/**
+	 * Decrypts the provided {@link CipherText} using the information from it
+	 * and the <i>specified secret key</i>.
+	 * </p><p>
+	 * This decrypt method is similar to {@link #decrypt(CipherText)} except that
+	 * it allows decrypting with a secret key other than the <i>master secret key</i>.
+	 * </p>
+	 * @param key		The {@code SecretKey} to use for encrypting the plaintext.
+	 * @param ciphertext The {@code CipherText} object to be decrypted.
+	 * @return The {@code PlainText} object resulting from decrypting the specified
+	 * 		   ciphertext. Note that it it is desired to convert the returned
+	 * 		   plaintext byte array to a Java String is should be done using
+	 * 		   {@code new String(byte[], "UTF-8");} rather than simply using
+	 * 		   {@code new String(byte[]);} which uses native encoding and may
+	 * 		   not be portable across hardware and/or OS platforms.
+	 * @throws EncryptionException  Thrown if something should go wrong such as
+	 * 				the JCE provider cannot be found, the cipher algorithm,
+	 * 				cipher mode, or padding scheme not being supported, specifying
+	 * 				an unsupported key size, or incorrect encryption key was
+	 * 				specified or a {@code PaddingException} occurs.
+	 * @see #decrypt(CipherText)
+	 */
+	PlainText decrypt(SecretKey key, CipherText ciphertext) throws EncryptionException;
+	
+	/**
+	 * Create a digital signature for the provided data and return it in a
+	 * string.
+	 * <p>
+	 * <b>Limitations:</b> A new public/private key pair used for ESAPI 2.0 digital
+	 * signatures with this method and {@link #verifySignature(String, String)}
+	 * are dynamically created when the default reference implementation class,
+	 * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created.
+	 * Because this key pair is not persisted nor is the public key shared,
+	 * this method and the corresponding {@link #verifySignature(String, String)}
+	 * can not be used with expected results across JVM instances. This limitation
+	 * will be addressed in ESAPI 2.1.
+	 * </p>
+	 * 
+	 * @param data
+	 *      the data to sign
+	 * 
+	 * @return 
+	 * 		the digital signature stored as a String
+	 * 
+	 * @throws EncryptionException
+	 * 		if the specified signature algorithm cannot be found
+	 */
+	String sign(String data) throws EncryptionException;
+
+	/**
+	 * Verifies a digital signature (created with the sign method) and returns
+	 * the boolean result.
+     * <p>
+     * <b>Limitations:</b> A new public/private key pair used for ESAPI 2.0 digital
+     * signatures with this method and {@link #sign(String)}
+     * are dynamically created when the default reference implementation class,
+     * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created.
+     * Because this key pair is not persisted nor is the public key shared,
+     * this method and the corresponding {@link #sign(String)}
+     * can not be used with expected results across JVM instances. This limitation
+     * will be addressed in ESAPI 2.1.
+     * </p>
+	 * @param signature
+	 *      the signature to verify against 'data'
+	 * @param data
+	 *      the data to verify against 'signature'
+	 * 
+	 * @return 
+	 * 		true, if the signature is verified, false otherwise
+	 * 
+	 */
+	boolean verifySignature(String signature, String data);
+
+	/**
+	 * Creates a seal that binds a set of data and includes an expiration timestamp.
+	 * 
+	 * @param data
+	 *      the data to seal
+	 * @param timestamp
+	 *      the absolute expiration date of the data, expressed as seconds since the epoch
+	 * 
+	 * @return 
+     * 		the seal
+     * @throws IntegrityException
+	 * 
+	 */
+	String seal(String data, long timestamp) throws IntegrityException;
+
+	/**
+	 * Unseals data (created with the seal method) and throws an exception
+	 * describing any of the various problems that could exist with a seal, such
+	 * as an invalid seal format, expired timestamp, or decryption error.
+	 * 
+	 * @param seal
+	 *      the sealed data
+	 * 
+	 * @return 
+	 * 		the original (unsealed) data
+	 * 
+	 * @throws EncryptionException 
+	 * 		if the unsealed data cannot be retrieved for any reason
+	 */
+	String unseal( String seal ) throws EncryptionException;
+	
+	/**
+	 * Verifies a seal (created with the seal method) and throws an exception
+	 * describing any of the various problems that could exist with a seal, such
+	 * as an invalid seal format, expired timestamp, or data mismatch.
+	 * 
+	 * @param seal
+	 *      the seal to verify
+	 * 
+	 * @return 
+	 * 		true, if the seal is valid.  False otherwise
+	 */
+	boolean verifySeal(String seal);
+	
+	/**
+	 * Gets an absolute timestamp representing an offset from the current time to be used by
+	 * other functions in the library.
+	 * 
+	 * @param offset 
+	 * 		the offset to add to the current time
+	 * 
+	 * @return 
+	 * 		the absolute timestamp
+	 */
+	public long getRelativeTimeStamp( long offset );
+	
+	
+	/**
+	 * Gets a timestamp representing the current date and time to be used by
+	 * other functions in the library.
+	 * 
+	 * @return 
+	 * 		a timestamp representing the current time
+	 */
+	long getTimeStamp();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/ExecuteResult.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/ExecuteResult.java.svn-base
new file mode 100644
index 0000000..e9835e3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/ExecuteResult.java.svn-base
@@ -0,0 +1,75 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2010 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Patrick Higgins
+ * @created 2010
+ */
+package org.owasp.esapi;
+
+/**
+ * The ExecuteResult class encapsulates the pieces of data that can be returned
+ * from a process executed by the Executor interface.
+ *
+ * This class is immutable for thread-safety.
+ *
+ * @author Patrick Higgins
+ * @since Aug 25, 2010
+ */
+public class ExecuteResult {
+    
+	private final int exitValue;
+	private final String output;
+	private final String errors;
+
+	/**
+	 * Constructs an ExecuteResult from the given values.
+	 *
+	 * @param exitValue
+	 *            the code from java.lang.Process.exitValue()
+	 * @param output
+	 *            the contents read from java.lang.Process.getInputStream()
+	 * @param errors
+	 *            the contents read from java.lang.Process.getErrorStream()
+	 */
+	public ExecuteResult(int exitValue, String output, String errors) {
+		this.exitValue = exitValue;
+		this.output = output;
+		this.errors = errors;
+	}
+
+	/**
+	 * @return the code from java.lang.Process.exitValue()
+	 */
+	public int getExitValue() {
+		return exitValue;
+	}
+
+	/**
+	 * @return the contents read from java.lang.Process.getInputStream()
+	 */
+	public String getOutput() {
+		return output;
+	}
+
+	/**
+	 * @return the contents read from java.lang.Process.getErrorStream()
+	 */
+	public String getErrors() {
+		return errors;
+	}
+	
+	@Override
+	public String toString() {
+		return "ExecuteResult[exitValue="+exitValue+",output="+output+",errors="+errors+"]";
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/Executor.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/Executor.java.svn-base
new file mode 100644
index 0000000..6286741
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/Executor.java.svn-base
@@ -0,0 +1,78 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.util.List;
+
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.errors.ExecutorException;
+
+/**
+ * The Executor interface is used to run an OS command with reduced security risk.
+ * 
+ * <p>Implementations should do as much as possible to minimize the risk of
+ * injection into either the command or parameters. In addition, implementations
+ * should timeout after a specified time period in order to help prevent denial
+ * of service attacks.</p> 
+ * 
+ * <p>The class should perform logging and error handling as
+ * well. Finally, implementation should handle errors and generate an
+ * ExecutorException with all the necessary information.</p>
+ *
+ * <p>The reference implementation does all of the above.</p>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Executor {
+
+	/**
+	 * Invokes the specified executable with default workdir and codec and not logging parameters.
+	 * 
+	 * @param executable
+	 *            the command to execute
+	 * @param params
+	 *            the parameters of the command being executed
+	 */
+	ExecuteResult executeSystemCommand(File executable, List params) throws ExecutorException;
+
+	/**
+	 * Executes a system command after checking that the executable exists and
+	 * escaping all the parameters to ensure that injection is impossible.
+	 * Implementations must change to the specified working
+	 * directory before invoking the command.
+	 *
+	 * @param executable
+	 *            the command to execute
+	 * @param params
+	 *            the parameters of the command being executed
+	 * @param workdir
+	 *            the working directory
+	 * @param codec
+	 *            the codec to use to encode for the particular OS in use
+	 * @param logParams
+	 *            use false if any parameters contains sensitive or confidential information
+	 *
+	 * @return the output of the command being run
+	 *
+	 * @throws ExecutorException
+	 *             the service exception
+	 */
+	ExecuteResult executeSystemCommand(File executable, List params, File workdir, Codec codec, boolean logParams, boolean redirectErrorStream) throws ExecutorException;
+
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/HTTPUtilities.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/HTTPUtilities.java.svn-base
new file mode 100644
index 0000000..f3eb169
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/HTTPUtilities.java.svn-base
@@ -0,0 +1,649 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.*;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * The HTTPUtilities interface is a collection of methods that provide additional security related to HTTP requests,
+ * responses, sessions, cookies, headers, and logging.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface HTTPUtilities
+{
+
+    final static String REMEMBER_TOKEN_COOKIE_NAME = "rtoken";
+    final static int MAX_COOKIE_LEN = 4096;            // From RFC 2109
+	final static int MAX_COOKIE_PAIRS = 20;			// From RFC 2109
+	final static String CSRF_TOKEN_NAME = "ctoken";
+	final static String ESAPI_STATE = "estate";
+
+	final static int PARAMETER = 0;
+	final static int HEADER = 1;
+	final static int COOKIE = 2;
+
+
+	/**
+     * Calls addCookie with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+    void addCookie(Cookie cookie);
+
+	/**
+     * Add a cookie to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This method also sets
+     * the secure and HttpOnly flags on the cookie.
+     *
+     * @param cookie
+     */
+    void addCookie(HttpServletResponse response, Cookie cookie);
+
+	/**
+     * Adds the current user's CSRF token (see User.getCSRFToken()) to the URL for purposes of preventing CSRF attacks.
+     * This method should be used on all URLs to be put into all links and forms the application generates.
+     *
+     * @param href the URL to which the CSRF token will be appended
+     * @return the updated URL with the CSRF token parameter added
+     */
+    String addCSRFToken(String href);
+
+    /**
+     * Calls addHeader with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+    void addHeader(String name, String value);
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This implementation
+     * follows the following recommendation: "A recipient MAY replace any linear
+     * white space with a single SP before interpreting the field value or
+     * forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     *
+     * @param name
+     * @param value
+     */
+    void addHeader(HttpServletResponse response, String name, String value);
+
+	/**
+     * Calls assertSecureRequest with the *current* request.
+	 * @see {@link HTTPUtilities#assertSecureRequest(HttpServletRequest)}
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void assertSecureRequest() throws AccessControlException;
+
+	/**
+     * Calls assertSecureChannel with the *current* request.
+	 * @see {@link HTTPUtilities#assertSecureChannel(HttpServletRequest)}
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void assertSecureChannel() throws AccessControlException;
+
+	/**
+	 * Ensures that the request uses both SSL and POST to protect any sensitive parameters
+	 * in the querystring from being sniffed, logged, bookmarked, included in referer header, etc...
+	 * This method should be called for any request that contains sensitive data from a web form.
+     *
+     * @param request
+     * @throws AccessControlException if security constraints are not met
+	 */
+    void assertSecureRequest(HttpServletRequest request) throws AccessControlException;
+
+	/**
+	 * Ensures the use of SSL to protect any sensitive parameters in the request and
+	 * any sensitive data in the response. This method should be called for any request
+	 * that contains sensitive data from a web form or will result in sensitive data in the
+	 * response page.
+     *
+     * @param request
+     * @throws AccessControlException if security constraints are not met
+	 */
+    void assertSecureChannel(HttpServletRequest request) throws AccessControlException;
+
+	/**
+     * Calls changeSessionIdentifier with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+	HttpSession changeSessionIdentifier() throws AuthenticationException;
+
+	/**
+     * Invalidate the existing session after copying all of its contents to a newly created session with a new session id.
+     * Note that this is different from logging out and creating a new session identifier that does not contain the
+     * existing session contents. Care should be taken to use this only when the existing session does not contain
+     * hazardous contents.
+     *
+     * @param request
+     * @return the new HttpSession with a changed id
+     * @throws AuthenticationException the exception
+     */
+    HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException;
+
+    /**
+	 * Clears the current HttpRequest and HttpResponse associated with the current thread.
+     *
+	 * @see ESAPI#clearCurrent()
+	 */
+    void clearCurrent();
+
+    /**
+	 * Decrypts an encrypted hidden field value and returns the cleartext. If the field does not decrypt properly,
+	 * an IntrusionException is thrown to indicate tampering.
+     *
+	 * @param encrypted hidden field value to decrypt
+	 * @return decrypted hidden field value stored as a String
+	 */
+	String decryptHiddenField(String encrypted);
+
+    /**
+	 * Takes an encrypted querystring and returns a Map containing the original parameters.
+     *
+	 * @param encrypted the encrypted querystring to decrypt
+	 * @return a Map object containing the decrypted querystring
+	 * @throws EncryptionException
+	 */
+    Map<String, String> decryptQueryString(String encrypted) throws EncryptionException;
+
+    /**
+     * Calls decryptStateFromCookie with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+    Map<String, String> decryptStateFromCookie() throws EncryptionException;
+
+    /**
+     * Retrieves a map of data from a cookie encrypted with encryptStateInCookie().
+     *
+     * @param request
+     * @return a map containing the decrypted cookie state value
+	 * @throws EncryptionException
+     */
+    Map<String, String> decryptStateFromCookie(HttpServletRequest request) throws EncryptionException;
+
+    /**
+     * Encrypts a hidden field value for use in HTML.
+     *
+     * @param value the cleartext value of the hidden field
+     * @return the encrypted value of the hidden field
+     * @throws EncryptionException
+     */
+	String encryptHiddenField(String value) throws EncryptionException;
+
+	/**
+	 * Takes a querystring (everything after the question mark in the URL) and returns an encrypted string containing the parameters.
+     *
+	 * @param query the querystring to encrypt
+	 * @return encrypted querystring stored as a String
+	 * @throws EncryptionException
+	 */
+	String encryptQueryString(String query) throws EncryptionException;
+
+	/**
+	 * Calls encryptStateInCookie with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void encryptStateInCookie(Map<String, String> cleartext) throws EncryptionException;
+
+    /**
+     * Stores a Map of data in an encrypted cookie. Generally the session is a better
+     * place to store state information, as it does not expose it to the user at all.
+     * If there is a requirement not to use sessions, or the data should be stored
+     * across sessions (for a long time), the use of encrypted cookies is an effective
+     * way to prevent the exposure.
+     *
+     * @param response
+     * @param cleartext
+     * @throws EncryptionException
+     */
+    void encryptStateInCookie(HttpServletResponse response, Map<String, String> cleartext) throws EncryptionException;
+
+    /**
+	 * Calls getCookie with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	String getCookie(String name) throws ValidationException;
+
+	/**
+     * A safer replacement for getCookies() in HttpServletRequest that returns the canonicalized
+     * value of the named cookie after "global" validation against the
+     * general type defined in ESAPI.properties. This should not be considered a replacement for
+     * more specific validation.
+     *
+     * @param request
+     * @param name
+     * @return the requested cookie value
+     */
+	String getCookie(HttpServletRequest request, String name) throws ValidationException;
+
+    /**
+     * Returns the current user's CSRF token. If there is no current user then return null.
+     *
+     * @return the current users CSRF token
+     */
+    String getCSRFToken();
+
+	/**
+     * Retrieves the current HttpServletRequest
+     *
+     * @return the current request
+     */
+    HttpServletRequest getCurrentRequest();
+
+	/**
+     * Retrieves the current HttpServletResponse
+     *
+     * @return the current response
+     */
+    HttpServletResponse getCurrentResponse();
+
+	/**
+	 * Calls getFileUploads with the *current* request, default upload directory, and default allowed file extensions
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	List getFileUploads() throws ValidationException;
+
+    /**
+	 * Call getFileUploads with the specified request, default upload directory, and default allowed file extensions
+	 */
+	List getFileUploads(HttpServletRequest request) throws ValidationException;
+
+    /**
+	 * Call getFileUploads with the specified request, specified upload directory, and default allowed file extensions
+	 */
+    List getFileUploads(HttpServletRequest request, File finalDir) throws ValidationException;
+
+
+    /**
+     * Extract uploaded files from a multipart HTTP requests. Implementations must check the content to ensure that it
+     * is safe before making a permanent copy on the local filesystem. Checks should include length and content checks,
+     * possibly virus checking, and path and name checks. Refer to the file checking methods in Validator for more
+     * information.
+     * <p/>
+	 * This method uses {@link HTTPUtilities#getCurrentRequest()} to obtain the {@link HttpServletRequest} object
+     *
+     * @param request
+     * @return List of new File objects from upload
+     * @throws ValidationException if the file fails validation
+     */
+    List getFileUploads(HttpServletRequest request, File destinationDir, List allowedExtensions) throws ValidationException;
+
+
+	/**
+	 * Calls getHeader with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	String getHeader(String name) throws ValidationException;
+
+    /**
+     * A safer replacement for getHeader() in HttpServletRequest that returns the canonicalized
+     * value of the named header after "global" validation against the
+     * general type defined in ESAPI.properties. This should not be considered a replacement for
+     * more specific validation.
+     *
+     * @param request
+     * @param name
+     * @return the requested header value
+     */
+	String getHeader(HttpServletRequest request, String name) throws ValidationException;
+
+	/**
+	 * Calls getParameter with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	String getParameter(String name) throws ValidationException;
+
+    /**
+     * A safer replacement for getParameter() in HttpServletRequest that returns the canonicalized
+     * value of the named parameter after "global" validation against the
+     * general type defined in ESAPI.properties. This should not be considered a replacement for
+     * more specific validation.
+     *
+     * @param request
+     * @param name
+     * @return the requested parameter value
+     */
+    String getParameter(HttpServletRequest request, String name) throws ValidationException;
+
+	/**
+	 * Calls killAllCookies with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void killAllCookies();
+
+    /**
+     * Kill all cookies received in the last request from the browser. Note that new cookies set by the application in
+     * this response may not be killed by this method.
+     *
+     * @param request
+     * @param response
+     */
+    void killAllCookies(HttpServletRequest request, HttpServletResponse response);
+
+	/**
+	 * Calls killCookie with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void killCookie(String name);
+
+    /**
+     * Kills the specified cookie by setting a new cookie that expires immediately. Note that this
+     * method does not delete new cookies that are being set by the application for this response.
+     *
+     * @param request
+     * @param name
+     * @param response
+     */
+    void killCookie(HttpServletRequest request, HttpServletResponse response, String name);
+
+	/**
+	 * Calls logHTTPRequest with the *current* request and logger.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void logHTTPRequest();
+
+    /**
+     * Format the Source IP address, URL, URL parameters, and all form
+     * parameters into a string suitable for the log file. Be careful not
+     * to log sensitive information, and consider masking with the
+     * logHTTPRequest( List parameterNamesToObfuscate ) method.
+     *
+     * @param request
+     * @param logger the logger to write the request to
+     */
+    void logHTTPRequest(HttpServletRequest request, Logger logger);
+
+    /**
+     * Format the Source IP address, URL, URL parameters, and all form
+     * parameters into a string suitable for the log file. The list of parameters to
+     * obfuscate should be specified in order to prevent sensitive information
+     * from being logged. If a null list is provided, then all parameters will
+     * be logged. If HTTP request logging is done in a central place, the
+     * parameterNamesToObfuscate could be made a configuration parameter. We
+     * include it here in case different parts of the application need to obfuscate
+     * different parameters.
+     *
+     * @param request
+     * @param logger the logger to write the request to
+     * @param parameterNamesToObfuscate the sensitive parameters
+     */
+    void logHTTPRequest(HttpServletRequest request, Logger logger, List parameterNamesToObfuscate);
+
+	/**
+	 * Calls sendForward with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void sendForward(String location) throws AccessControlException, ServletException, IOException;
+
+    /**
+     * This method performs a forward to any resource located inside the WEB-INF directory. Forwarding to
+     * publicly accessible resources can be dangerous, as the request will have already passed the URL
+     * based access control check. This method ensures that you can only forward to non-publicly
+     * accessible resources.
+     *
+     * @param request
+     * @param response
+     * @param location the URL to forward to, including parameters
+     * @throws AccessControlException
+     * @throws ServletException
+     * @throws IOException
+     */
+    void sendForward(HttpServletRequest request, HttpServletResponse response, String location) throws AccessControlException, ServletException, IOException;
+
+
+	/**
+	 * Calls sendRedirect with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void sendRedirect(String location) throws AccessControlException, IOException;
+
+
+    /**
+     * This method performs a forward to any resource located inside the WEB-INF directory. Forwarding to
+     * publicly accessible resources can be dangerous, as the request will have already passed the URL
+     * based access control check. This method ensures that you can only forward to non-publicly
+     * accessible resources.
+     *
+     * @param response
+     * @param location the URL to forward to, including parameters
+     * @throws AccessControlException
+     * @throws ServletException
+     * @throws IOException
+     */
+    void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException;
+
+	/**
+	 * Calls setContentType with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void setContentType();
+
+     /**
+	 * Set the content type character encoding header on every HttpServletResponse in order to limit
+	 * the ways in which the input data can be represented. This prevents
+	 * malicious users from using encoding and multi-byte escape sequences to
+	 * bypass input validation routines.
+     * <p/>
+	 * Implementations of this method should set the content type header to a safe value for your environment.
+     * The default is text/html; charset=UTF-8 character encoding, which is the default in early
+	 * versions of HTML and HTTP. See RFC 2047 (http://ds.internic.net/rfc/rfc2045.txt) for more
+	 * information about character encoding and MIME.
+     * <p/>
+	 * The DefaultHTTPUtilities reference implementation sets the content type as specified.
+     *
+     * @param response The servlet response to set the content type for.
+     */
+    void setContentType(HttpServletResponse response);
+
+    /**
+     * Stores the current HttpRequest and HttpResponse so that they may be readily accessed throughout
+     * ESAPI (and elsewhere)
+     *
+     * @param request  the current request
+     * @param response the current response
+     */
+    void setCurrentHTTP(HttpServletRequest request, HttpServletResponse response);
+
+
+	/**
+	 * Calls setHeader with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void setHeader(String name, String value);
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and value. "A recipient MAY replace any
+     * linear white space with a single SP before interpreting the field value
+     * or forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     *
+     * @param name
+     * @param value
+     */
+    void setHeader(HttpServletResponse response, String name, String value);
+
+
+	/**
+	 * Calls setNoCacheHeaders with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void setNoCacheHeaders();
+
+
+    /**
+     * Set headers to protect sensitive information against being cached in the browser. Developers should make this
+     * call for any HTTP responses that contain any sensitive data that should not be cached within the browser or any
+     * intermediate proxies or caches. Implementations should set headers for the expected browsers. The safest approach
+     * is to set all relevant headers to their most restrictive setting. These include:
+     * <p/>
+     * <PRE>
+     * Cache-Control: no-store<BR>
+     * Cache-Control: no-cache<BR>
+     * Cache-Control: must-revalidate<BR>
+     * Expires: -1<BR>
+     * </PRE>
+     * <p/>
+     * Note that the header "pragma: no-cache" is intended only for use in HTTP requests, not HTTP responses. However, Microsoft has chosen to
+     * directly violate the standards, so we need to include that header here. For more information, please refer to the relevant standards:
+     * <UL>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">HTTP/1.1 Cache-Control "no-cache"</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1">HTTP/1.1 Cache-Control "no-store"</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2">HTTP/1.0 Pragma "no-cache"</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32">HTTP/1.0 Expires</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">IE6 Caching Issues</a>
+     * <LI><a href="http://support.microsoft.com/kb/937479">Firefox browser.cache.disk_cache_ssl</a>
+     * <LI><a href="http://support.microsoft.com/kb/234067">Microsoft directly violates specification for pragma: no-cache</a>
+     * <LI><a href="http://www.mozilla.org/quality/networking/docs/netprefs.html">Mozilla</a>
+     * </UL>
+     *
+     * @param response
+     */
+    void setNoCacheHeaders(HttpServletResponse response);
+
+	/**
+	 * Calls setNoCacheHeaders with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    String setRememberToken(String password, int maxAge, String domain, String path);
+
+
+    /**
+	 * Set a cookie containing the current User's remember me token for automatic authentication. The use of remember me tokens
+	 * is generally not recommended, but this method will help do it as safely as possible. The user interface should strongly warn
+     * the user that this should only be enabled on computers where no other users will have access.
+     * <p/>
+     * Implementations should save the user's remember me data in an encrypted cookie and send it to the user.
+     * Any old remember me cookie should be destroyed first. Setting this cookie should keep the user
+	 * logged in until the maxAge passes, the password is changed, or the cookie is deleted.
+	 * If the cookie exists for the current user, it should automatically be used by ESAPI to
+     * log the user in, if the data is valid and not expired.
+     * <p/>
+	 * The ESAPI reference implementation, DefaultHTTPUtilities.setRememberToken() implements all these suggestions.
+     * <p/>
+     * The username can be retrieved with: User username = ESAPI.authenticator().getCurrentUser();
+     *
+     * @param request
+     * @param password the user's password
+     * @param response
+     * @param maxAge the length of time that the token should be valid for in relative seconds
+	 * @param domain the domain to restrict the token to or null
+	 * @param path the path to restrict the token to or null
+	 * @return encrypted "Remember Me" token stored as a String
+	 */
+    String setRememberToken(HttpServletRequest request, HttpServletResponse response, String password, int maxAge, String domain, String path);
+
+
+	/**
+	 * Calls verifyCSRFToken with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void verifyCSRFToken();
+
+    /**
+     * Checks the CSRF token in the URL (see User.getCSRFToken()) against the user's CSRF token and
+	 * throws an IntrusionException if it is missing.
+     *
+     * @param request
+     * @throws IntrusionException if CSRF token is missing or incorrect
+	 */
+    void verifyCSRFToken(HttpServletRequest request) throws IntrusionException;
+
+   /**
+    * Gets a typed attribute from the session associated with the calling thread. If the
+    * object referenced by the passed in key is not of the implied type, a ClassCastException
+    * will be thrown to the calling code.
+    *
+    * @param    key
+    *           The key that references the session attribute
+    * @param    <T>
+    *           The implied type of object expected.
+    * @return
+    *           The requested object.
+    * @see      #getSessionAttribute(javax.servlet.http.HttpSession, String)
+    */
+    <T> T getSessionAttribute( String key );
+
+    /**
+     * Gets a typed attribute from the passed in session. This method has the same
+     * responsibility as {link #getSessionAttribute(String} however only it references
+     * the passed in session and thus performs slightly better since it does not need
+     * to return to the Thread to get the {@link HttpSession} associated with the current
+     * thread.
+     *
+     * @param session
+     *          The session to retrieve the attribute from
+     * @param key
+     *          The key that references the requested object
+     * @param <T>
+     *          The implied type of object expected
+     * @return  The requested object
+     */
+    <T> T getSessionAttribute( HttpSession session, String key );
+
+    /**
+     * Gets a typed attribute from the {@link HttpServletRequest} associated
+     * with the caller thread. If the attribute on the request is not of the implied
+     * type, a ClassCastException will be thrown back to the caller.
+     *
+     * @param key The key that references the request attribute.
+     * @param <T> The implied type of the object expected
+     * @return The requested object
+     */
+    <T> T getRequestAttribute( String key );
+
+    /**
+     * Gets a typed attribute from the {@link HttpServletRequest} associated
+     * with the passed in request. If the attribute on the request is not of the implied
+     * type, a ClassCastException will be thrown back to the caller.
+     *
+     * @param request The request to retrieve the attribute from
+     * @param key The key that references the request attribute.
+     * @param <T> The implied type of the object expected
+     * @return The requested object
+     */
+    <T> T getRequestAttribute( HttpServletRequest request, String key );
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/IntrusionDetector.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/IntrusionDetector.java.svn-base
new file mode 100644
index 0000000..57ea32b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/IntrusionDetector.java.svn-base
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.IntrusionException;
+
+
+/**
+ * The IntrusionDetector interface is intended to track security relevant events and identify attack behavior. The
+ * implementation can use as much state as necessary to detect attacks, but note that storing too much state will burden
+ * your system.
+ * <P>
+ * The interface is currently designed to accept exceptions as well as custom events. Implementations can use this
+ * stream of information to detect both normal and abnormal behavior.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface IntrusionDetector {
+
+    /**
+     * Adds the exception to the IntrusionDetector.  This method should immediately log the exception so that developers throwing an 
+     * IntrusionException do not have to remember to log every error.  The implementation should store the exception somewhere for the current user
+     * in order to check if the User has reached the threshold for any Enterprise Security Exceptions.  The User object is the recommended location for storing
+     * the current user's security exceptions.  If the User has reached any security thresholds, the appropriate security action can be taken and logged.
+     * 
+     * @param exception 
+     * 		the exception thrown
+     * 
+     * @throws IntrusionException 
+     * 		the intrusion exception
+     */
+    void addException(Exception exception) throws IntrusionException;
+
+    /**
+     * Adds the event to the IntrusionDetector.  This method should immediately log the event.  The implementation should store the event somewhere for the current user
+     * in order to check if the User has reached the threshold for any Enterprise Security Exceptions.  The User object is the recommended location for storing
+     * the current user's security event.  If the User has reached any security thresholds, the appropriate security action can be taken and logged.
+     * 
+     * @param eventName 
+     * 		the event to add
+     * @param logMessage 
+     * 		the message to log with the event
+     * 
+     * @throws IntrusionException 
+     * 		the intrusion exception
+     */
+    void addEvent(String eventName, String logMessage) throws IntrusionException;
+
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/LogFactory.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/LogFactory.java.svn-base
new file mode 100644
index 0000000..e373698
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/LogFactory.java.svn-base
@@ -0,0 +1,60 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Rogan Dawes<a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2008
+ */
+package org.owasp.esapi;
+
+/**
+ * The LogFactory interface is intended to allow substitution of various logging packages, while providing
+ * a common interface to access them.
+ * 
+ * In the reference implementation, JavaLogFactory.java implements this interface.  JavaLogFactory.java also contains an 
+ * inner class called JavaLogger which implements Logger.java and uses the Java logging package to log events. 
+ * 
+ * @see org.owasp.esapi.ESAPI
+ * 
+ * @author rdawes
+ *
+ */
+public interface LogFactory {
+	
+	/**
+	 * Gets the logger associated with the specified module name. The module name is used by the logger to log which 
+	 * module is generating the log events. The implementation of this method should return any preexisting Logger 
+	 * associated with this module name, rather than creating a new Logger.
+	 * <br><br>
+	 * The JavaLogFactory reference implementation meets these requirements.
+	 * 
+	 * @param moduleName
+	 * 			The name of the module requesting the logger.
+	 * @return
+	 * 			The Logger associated with this module.
+	 */
+	Logger getLogger(String moduleName);
+	
+	/**
+	 * Gets the logger associated with the specified class. The class is used by the logger to log which 
+	 * class is generating the log events. The implementation of this method should return any preexisting Logger 
+	 * associated with this class name, rather than creating a new Logger.
+	 * <br><br>
+	 * The JavaLogFactory reference implementation meets these requirements.
+	 * 
+	 * @param clazz
+	 * 			The name of the class requesting the logger.
+	 * @return
+	 * 			The Logger associated with this class.
+	 */
+	Logger getLogger(Class clazz);
+	
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/Logger.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/Logger.java.svn-base
new file mode 100644
index 0000000..0bd9517
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/Logger.java.svn-base
@@ -0,0 +1,423 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+
+/**
+ * The Logger interface defines a set of methods that can be used to log
+ * security events. It supports a hierarchy of logging levels which can be configured at runtime to determine
+ * the severity of events that are logged, and those below the current threshold that are discarded.
+ * Implementors should use a well established logging library
+ * as it is quite difficult to create a high-performance logger.
+ * <P>
+ * The logging levels defined by this interface (in descending order) are:
+ * <ul>
+ * <li>fatal (highest value)</li>
+ * <li>error</li>
+ * <li>warning</li>
+ * <li>info</li>
+ * <li>debug</li>
+ * <li>trace (lowest value)</li>
+ * </ul>
+ * There are also several variations of {@code always()} methods that will <i>always</i>
+ * log a message regardless of the log level.
+ * <p>
+ * ESAPI also allows for the definition of the type of log event that is being generated.
+ * The Logger interface predefines 6 types of Log events:
+ * <ul>
+ * <li>SECURITY_SUCCESS</li>
+ * <li>SECURITY_FAILURE</li>
+ * <li>SECURITY_AUDIT</li>
+ * <li>EVENT_SUCCESS</li>
+ * <li>EVENT_FAILURE</li>
+ * <li>EVENT_UNSPECIFIED</li>
+ * </ul>
+ * <p>
+ * Your implementation can extend or change this list if desired. 
+ * <p>
+ * This Logger allows callers to determine which logging levels are enabled, and to submit events 
+ * at different severity levels.<br>
+ * <br>Implementors of this interface should:
+ * 
+ * <ol>
+ * <li>provide a mechanism for setting the logging level threshold that is currently enabled. This usually works by logging all 
+ * events at and above that severity level, and discarding all events below that level.
+ * This is usually done via configuration, but can also be made accessible programmatically.</li>
+ * <li>ensure that dangerous HTML characters are encoded before they are logged to defend against malicious injection into logs 
+ * that might be viewed in an HTML based log viewer.</li>
+ * <li>encode any CRLF characters included in log data in order to prevent log injection attacks.</li>
+ * <li>avoid logging the user's session ID. Rather, they should log something equivalent like a 
+ * generated logging session ID, or a hashed value of the session ID so they can track session specific 
+ * events without risking the exposure of a live session's ID.</li> 
+ * <li>record the following information with each event:</li>
+ *   <ol type="a">
+ *   <li>identity of the user that caused the event,</li>
+ *   <li>a description of the event (supplied by the caller),</li>
+ *   <li>whether the event succeeded or failed (indicated by the caller),</li>
+ *   <li>severity level of the event (indicated by the caller),</li>
+ *   <li>that this is a security relevant event (indicated by the caller),</li>
+ *   <li>hostname or IP where the event occurred (and ideally the user's source IP as well),</li>
+ *   <li>a time stamp</li>
+ *   </ol>
+ * </ol>
+ *  
+ * Custom logger implementations might also:
+ * <ol start="6">
+ * <li>filter out any sensitive data specific to the current application or organization, such as credit cards, 
+ * social security numbers, etc.</li>
+ * </ol>
+ * 
+ * There are both Log4j and native Java Logging default implementations. JavaLogger uses the java.util.logging package as the basis for its logging 
+ * implementation. Both default implementations implements requirements #1 thru #5 above.<br>
+ * <br>
+ * Customization: It is expected that most organizations will implement their own custom Logger class in 
+ * order to integrate ESAPI logging with their logging infrastructure. The ESAPI Reference Implementation 
+ * is intended to provide a simple functional example of an implementation.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ * href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Logger {
+
+	/**
+     * A security type of log event that has succeeded. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType SECURITY_SUCCESS = new EventType( "SECURITY SUCCESS", true);
+
+	/**
+     * A security type of log event that has failed. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType SECURITY_FAILURE = new EventType( "SECURITY FAILURE", false);
+
+	/**
+	 * A security type of log event that is associated with an audit trail of some type,
+	 * but the log event is not specifically something that has either succeeded or failed
+	 * or that is irrelevant in the case of this logged message.
+	 */
+	// CHECKME: Should the Boolean for this be 'null' or 'true'? See EVENT_UNSPECIFIED.
+	public static final EventType SECURITY_AUDIT = new EventType( "SECURITY AUDIT", null);
+
+	/**
+     * A non-security type of log event that has succeeded. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType EVENT_SUCCESS = new EventType( "EVENT SUCCESS", true);
+	
+	/**
+     * A non-security type of log event that has failed. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType EVENT_FAILURE = new EventType( "EVENT FAILURE", false);
+
+	/**
+     * A non-security type of log event that is unspecified. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType EVENT_UNSPECIFIED = new EventType( "EVENT UNSPECIFIED", null);
+
+	/**
+	 * Defines the type of log event that is being generated. The Logger interface defines 6 types of Log events:
+	 * SECURITY_SUCCESS, SECURITY_FAILURE, EVENT_SUCCESS, EVENT_FAILURE, EVENT_UNSPECIFIED.
+     * Your implementation can extend or change this list if desired. 
+	 */
+	public class EventType {
+		
+		private String type;
+		private Boolean success = null;
+		
+		public EventType (String name, Boolean newSuccess) {
+			this.type = name;
+			this.success = newSuccess;
+		}
+		
+		public Boolean isSuccess() {
+			return success;
+		}
+		
+        /**
+         * Convert the {@code EventType} to a string.
+         * @return The event type name.
+         */
+		@Override
+        public String toString() {
+			return this.type;
+		}
+	}
+	
+	/*
+     * The Logger interface defines 6 logging levels: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE. It also 
+     * supports ALL, which logs all events, and OFF, which disables all logging.
+     * Your implementation can extend or change this list if desired. 
+     */
+	
+	/** OFF indicates that no messages should be logged. This level is initialized to Integer.MAX_VALUE. */
+	public static final int OFF = Integer.MAX_VALUE;
+
+	/** FATAL indicates that only FATAL messages should be logged. This level is initialized to 1000. */
+	public static final int FATAL = 1000;
+
+	/** ERROR indicates that ERROR messages and above should be logged. 
+	 * This level is initialized to 800. */
+    public static final int ERROR = 800;
+
+    /** WARNING indicates that WARNING messages and above should be logged. 
+     * This level is initialized to 600. */
+    public static final int WARNING = 600;
+
+    /** INFO indicates that INFO messages and above should be logged. 
+     * This level is initialized to 400. */
+    public static final int INFO = 400;
+
+    /** DEBUG indicates that DEBUG messages and above should be logged. 
+     * This level is initialized to 200. */
+    public static final int DEBUG = 200;
+
+    /** TRACE indicates that TRACE messages and above should be logged. 
+     * This level is initialized to 100. */
+    public static final int TRACE = 100;
+
+    /** ALL indicates that all messages should be logged. This level is initialized to Integer.MIN_VALUE. */
+    public static final int ALL = Integer.MIN_VALUE;
+    
+
+    /**
+     * Dynamically set the ESAPI logging severity level. All events of this level and higher will be logged from 
+     * this point forward for all logs. All events below this level will be discarded.
+     * 
+     * @param level The level to set the logging level to. 
+     */
+    void setLevel(int level);
+    
+    /** Retrieve the current ESAPI logging level for this logger. See
+     * {@link org.owasp.esapi.reference.Log4JLogger} for an explanation of
+     * why this method is not simply called {@code getLevel()}.
+     * 
+     * @return The current logging level.
+     */
+    int getESAPILevel();
+    
+	/**
+     * Log a fatal event if 'fatal' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void fatal(EventType type, String message);
+	
+	/**
+     * Log a fatal level security event if 'fatal' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void fatal(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if fatal level messages will be output to the log
+	 */
+	boolean isFatalEnabled();
+
+	/**
+     * Log an error level security event if 'error' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     */
+	void error(EventType type, String message);
+	
+	/**
+     * Log an error level security event if 'error' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void error(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if error level messages will be output to the log
+	 */
+	boolean isErrorEnabled();
+
+	/**
+     * Log a warning level security event if 'warning' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     */
+	void warning(EventType type, String message);
+	
+	/**
+     * Log a warning level security event if 'warning' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void warning(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if warning level messages will be output to the log
+	 */
+	boolean isWarningEnabled();
+
+	/**
+     * Log an info level security event if 'info' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     */
+	void info(EventType type, String message);
+	
+	/**
+     * Log an info level security event if 'info' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void info(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if info level messages will be output to the log
+	 */
+	boolean isInfoEnabled();
+
+	/**
+     * Log a debug level security event if 'debug' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void debug(EventType type, String message);
+	
+	/**
+     * Log a debug level security event if 'debug' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void debug(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if debug level messages will be output to the log
+	 */
+	boolean isDebugEnabled();
+
+	/**
+     * Log a trace level security event if 'trace' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void trace(EventType type, String message);
+	
+	/**
+     * Log a trace level security event if 'trace' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void trace(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if trace level messages will be output to the log
+	 */
+	boolean isTraceEnabled();
+
+	/**
+     * Log an event regardless of what logging level is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void always(EventType type, String message);
+	
+	/**
+     * Log an event regardless of what logging level is enabled
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void always(EventType type, String message, Throwable throwable);
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/PreparedString.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/PreparedString.java.svn-base
new file mode 100644
index 0000000..4e300be
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/PreparedString.java.svn-base
@@ -0,0 +1,139 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi;
+
+import java.util.ArrayList;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.HTMLEntityCodec;
+
+
+/**
+ * A parameterized string that uses escaping to make untrusted data safe before combining it with
+ * a command or query intended for use in an interpreter.
+ * <pre> 
+ * PreparedString div = new PreparedString( "<a href=\"http:\\\\example.com?id=?\" onmouseover=\"alert('?')\">test</a>", new HTMLEntityCodec() );
+ * div.setURL( 1, request.getParameter( "url" ), new PercentCodec() );
+ * div.set( 2, request.getParameter( "message" ), new JavaScriptCodec() );
+ * out.println( div.toString() );
+ * 
+ * // escaping for SQL
+ * PreparedString query = new PreparedString( "SELECT * FROM users WHERE name='?' AND password='?'", new OracleCodec() );
+ * query.set( 1, request.getParameter( "name" ) );
+ * query.set( 2, request.getParameter( "pass" ) );
+ * stmt.execute( query.toString() );
+ * </pre>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public class PreparedString {
+	char parameterCharacter = '?';
+	Codec codec = null;
+	String[] parameters = null;
+	ArrayList parts = new ArrayList();
+	private final static char[] IMMUNE = {};
+
+	/**
+	 * Create a PreparedString with the supplied template and Codec. The template should use the 
+	 * default parameter placeholder character (?) in the place where actual parameters are to be inserted.
+	 * The supplied Codec will be used to escape characters in calls to set, unless a specific Codec is
+	 * provided to override it.
+	 * @param template
+	 * @param codec
+	 */
+	public PreparedString( String template, Codec codec ) {
+		this.codec = codec;
+		split( template, parameterCharacter );
+	}
+
+	/**
+	 * Create a PreparedString with the supplied template, parameter placeholder character, and Codec. The parameter character
+	 * can be any character, but should not be one that will be used in the template. The parameter character can safely
+	 * be used in a parameter passed into the set methods.
+	 * @param template
+	 * @param parameterCharacter
+	 * @param codec
+	 */
+	public PreparedString( String template, char parameterCharacter, Codec codec ) {
+		this.codec = codec;
+		this.parameterCharacter = parameterCharacter;
+		split( template, parameterCharacter );
+	}
+
+	/**
+	 * Split a string with a particular character.
+	 * @param str
+	 * @param c
+	 */
+	private void split( String str, char c ) {
+		int index = 0;
+		int pcount = 0;
+		for ( int i = 0; i < str.length(); i++ ) {
+			if ( str.charAt(i) == c ) {
+				pcount++;
+				parts.add( str.substring(index,i) );
+				index = i + 1;
+			}
+		}
+		parts.add( str.substring(index) );
+		parameters = new String[pcount];
+	}
+	
+	/**
+	 * Set the parameter at index with supplied value using the default Codec to escape. 
+	 * @param index
+	 * @param value
+	 */
+	public void set( int index, String value ) {
+		if ( index < 1 || index > parameters.length ) {
+			throw new IllegalArgumentException( "Attempt to set parameter " + index + " on a PreparedString with only " + parameters.length + " placeholders" );
+		}
+		String encoded = codec.encode( IMMUNE, value );
+		parameters[index-1] = encoded;
+	}
+	
+	/**
+	 * Set the parameter at index with supplied value using the supplied Codec to escape. 
+	 * @param index
+	 * @param value
+	 * @param codec
+	 */
+	public void set( int index, String value, Codec codec ) {
+		if ( index < 1 || index > parameters.length ) {
+			throw new IllegalArgumentException( "Attempt to set parameter " + index + " on a PreparedString with only " + parameters.length + " placeholders" );
+		}
+		String encoded = codec.encode( IMMUNE, value );
+		parameters[index-1] = encoded;
+	}
+	
+	/**
+	 * Render the PreparedString by combining the template with properly escaped parameters.
+	 */
+	public String toString() {
+		for ( int ix = 0; ix < parameters.length; ix++ ) {
+			if ( parameters[ix] == null ) {
+				throw new RuntimeException( "Attempt to render PreparedString without setting parameter " + ( ix + 1 ));
+			}
+		}
+		StringBuilder sb = new StringBuilder();
+		int i = 0;
+		for ( int p=0; p < parts.size(); p++ ) {
+			sb.append( parts.get( p ) );
+			if ( i < parameters.length ) sb.append( parameters[i++] );
+		}
+		return sb.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/Randomizer.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/Randomizer.java.svn-base
new file mode 100644
index 0000000..17532c3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/Randomizer.java.svn-base
@@ -0,0 +1,148 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The Randomizer interface defines a set of methods for creating
+ * cryptographically random numbers and strings. Implementers should be sure to
+ * use a strong cryptographic implementation, such as the JCE or BouncyCastle.
+ * Weak sources of randomness can undermine a wide variety of security
+ * mechanisms. The specific algorithm used is configurable in ESAPI.properties.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Randomizer {
+
+	/**
+	 * Gets a random string of a desired length and character set.  The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @param length 
+	 * 		the length of the string
+	 * @param characterSet 
+	 * 		the set of characters to include in the created random string
+	 * 
+	 * @return 
+	 * 		the random string of the desired length and character set
+	 */
+	String getRandomString(int length, char[] characterSet);
+
+	/**
+	 * Returns a random boolean.  The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @return 
+	 * 		true or false, randomly
+	 */
+	boolean getRandomBoolean();
+	
+	/**
+	 * Gets the random integer. The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @param min 
+	 * 		the minimum integer that will be returned
+	 * @param max 
+	 * 		the maximum integer that will be returned
+	 * 
+	 * @return 
+	 * 		the random integer
+	 */
+	int getRandomInteger(int min, int max);
+
+	
+	/**
+	 * Gets the random long. The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @return 
+	 * 		the random long
+	 */
+    public long getRandomLong();
+	
+	
+    /**
+     * Returns an unguessable random filename with the specified extension.  This method could call
+     * getRandomString(length, charset) from this Class with the desired length and alphanumerics as the charset 
+     * then merely append "." + extension.
+     * 
+     * @param extension 
+     * 		extension to add to the random filename
+     * 
+     * @return 
+     * 		a random unguessable filename ending with the specified extension
+     */
+    public String getRandomFilename( String extension );
+    
+    
+	/**
+	 * Gets the random real.  The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @param min 
+	 * 		the minimum real number that will be returned
+	 * @param max 
+	 * 		the maximum real number that will be returned
+	 * 
+	 * @return 
+	 * 		the random real
+	 */
+	float getRandomReal(float min, float max);
+
+    /**
+     * Generates a random GUID.  This method could use a hash of random Strings, the current time,
+     * and any other random data available.  The format is a well-defined sequence of 32 hex digits 
+     * grouped into chunks of 8-4-4-4-12.
+     * <p>
+     * For more information including algorithms used to create <tt>UUID</tt>s,
+     * see the Internet-Draft <a href="http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-03.txt">UUIDs and GUIDs</a>
+     * or the standards body definition at <a href="http://www.iso.ch/cate/d2229.html">ISO/IEC 11578:1996</a>.
+     * @return 
+     * 		the GUID
+     * 
+     * @throws 
+     * 		EncryptionException if hashing or encryption fails 
+     */
+    String getRandomGUID() throws EncryptionException;
+           
+    /**
+     * Generates a specified number of random bytes.
+     * @param n	The requested number of random bytes.
+     * @return The {@code n} random bytes are returned.
+     */
+    public byte[] getRandomBytes(int n);
+           
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/SafeFile.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/SafeFile.java.svn-base
new file mode 100644
index 0000000..73643c0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/SafeFile.java.svn-base
@@ -0,0 +1,107 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2008 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2008
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * Extension to java.io.File to prevent against null byte injections and
+ * other unforeseen problems resulting from unprintable characters
+ * causing problems in path lookups. This does _not_ prevent against
+ * directory traversal attacks.
+ */
+public class SafeFile extends File {
+
+	private static final long serialVersionUID = 1L;
+	private static final Pattern PERCENTS_PAT = Pattern.compile("(%)([0-9a-fA-F])([0-9a-fA-F])");	
+	private static final Pattern FILE_BLACKLIST_PAT = Pattern.compile("([\\\\/:*?<>|])");	
+	private static final Pattern DIR_BLACKLIST_PAT = Pattern.compile("([*?<>|])");
+
+	public SafeFile(String path) throws ValidationException {
+		super(path);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	public SafeFile(String parent, String child) throws ValidationException {
+		super(parent, child);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	public SafeFile(File parent, String child) throws ValidationException {
+		super(parent, child);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	public SafeFile(URI uri) throws ValidationException {
+		super(uri);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	
+	private void doDirCheck(String path) throws ValidationException {
+		Matcher m1 = DIR_BLACKLIST_PAT.matcher( path );
+		if ( m1.find() ) {
+			throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains illegal character: " + m1.group() );
+		}
+
+		Matcher m2 = PERCENTS_PAT.matcher( path );
+		if ( m2.find() ) {
+			throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains encoded characters: " + m2.group() );
+		}
+		
+		int ch = containsUnprintableCharacters(path);
+		if (ch != -1) {
+			throw new ValidationException("Invalid directory", "Directory path (" + path + ") contains unprintable character: " + ch);
+		}
+	}
+	
+	private void doFileCheck(String path) throws ValidationException {
+		Matcher m1 = FILE_BLACKLIST_PAT.matcher( path );
+		if ( m1.find() ) {
+			throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains illegal character: " + m1.group() );
+		}
+
+		Matcher m2 = PERCENTS_PAT.matcher( path );
+		if ( m2.find() ) {
+			throw new ValidationException( "Invalid file", "File path (" + path + ") contains encoded characters: " + m2.group() );
+		}
+		
+		int ch = containsUnprintableCharacters(path);
+		if (ch != -1) {
+			throw new ValidationException("Invalid file", "File path (" + path + ") contains unprintable character: " + ch);
+		}
+	}
+
+	private int containsUnprintableCharacters(String s) {
+		for (int i = 0; i < s.length(); i++) {
+			char ch = s.charAt(i);
+			if (((int) ch) < 32 || ((int) ch) > 126) {
+				return (int) ch;
+			}
+		}
+		return -1;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/SecurityConfiguration.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/SecurityConfiguration.java.svn-base
new file mode 100644
index 0000000..9c54acb
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/SecurityConfiguration.java.svn-base
@@ -0,0 +1,682 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Mike Fauzy <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * The {@code SecurityConfiguration} interface stores all configuration information
+ * that directs the behavior of the ESAPI implementation.
+ * <br><br>
+ * Protection of this configuration information is critical to the secure
+ * operation of the application using the ESAPI. You should use operating system
+ * access controls to limit access to wherever the configuration information is
+ * stored.
+ * <br><br>
+ * Please note that adding another layer of encryption does not make the
+ * attackers job much more difficult. Somewhere there must be a master "secret"
+ * that is stored unencrypted on the application platform (unless you are
+ * willing to prompt for some passphrase when you application starts or insert
+ * a USB thumb drive or an HSM card, etc., in which case this master "secret"
+ * it would only be in memory). Creating another layer of indirection provides
+ * additional obfuscation, but doesn't provide any real additional security.
+ * It's up to the reference implementation to decide whether this file should
+ * be encrypted or not.
+ * <br><br>
+ * The ESAPI reference implementation (DefaultSecurityConfiguration.java) does
+ * <i>not</i> encrypt its properties file.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface SecurityConfiguration {
+
+	/**
+	 * Gets the application name, used for logging
+	 * 
+	 * @return the name of the current application
+	 */
+	public String getApplicationName();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Logging implementation.
+	 */
+	public String getLogImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Authentication implementation.
+	 */
+	public String getAuthenticationImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Encoder implementation.
+	 */
+	public String getEncoderImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Access Control implementation.
+	 */
+	public String getAccessControlImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Intrusion Detection implementation.
+	 */
+	public String getIntrusionDetectionImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Randomizer implementation.
+	 */
+	public String getRandomizerImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Encryption implementation.
+	 */
+	public String getEncryptionImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Validation implementation.
+	 */
+	public String getValidationImplementation();
+	
+	/**
+	 * Returns the validation pattern for a particular type
+	 * @param typeName
+	 * @return the validation pattern
+	 */
+    public Pattern getValidationPattern( String typeName );
+    
+    /**
+     * Determines whether ESAPI will accept "lenient" dates when attempt
+     * to parse dates. Controlled by ESAPI property
+     * {@code Validator.AcceptLenientDates}, which defaults to {@code false}
+     * if unset.
+     * 
+     * @return True if lenient dates are accepted; false otherwise.
+     * @see java.text.DateFormat#setLenient(boolean)
+     */
+    public boolean getLenientDatesAccepted();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI OS Execution implementation.
+	 */
+	public String getExecutorImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI HTTPUtilities implementation.
+	 */
+	public String getHTTPUtilitiesImplementation();
+	
+	/**
+	 * Gets the master key. This password is used to encrypt/decrypt other files or types
+	 * of data that need to be protected by your application.
+	 * 
+	 * @return the current master key
+	 */
+	public byte[] getMasterKey();
+
+	/**
+     * Retrieves the upload directory as specified in the ESAPI.properties file.
+     * @return the upload directory
+     */
+    public File getUploadDirectory();
+	
+    /**
+     * Retrieves the temp directory to use when uploading files, as specified in ESAPI.properties.
+     * @return the temp directory
+     */
+    public File getUploadTempDirectory();
+
+	/**
+	 * Gets the key length to use in cryptographic operations declared in the ESAPI properties file.
+	 * 
+	 * @return the key length.
+	 */
+    public int getEncryptionKeyLength();
+    
+	/**
+	 * Gets the master salt that is used to salt stored password hashes and any other location 
+	 * where a salt is needed.
+	 * 
+	 * @return the current master salt
+	 */
+	public byte[] getMasterSalt();
+
+	/**
+	 * Gets the allowed executables to run with the Executor.
+	 * 
+	 * @return a list of the current allowed file extensions
+	 */
+	public List<String> getAllowedExecutables();
+
+	/**
+	 * Gets the allowed file extensions for files that are uploaded to this application.
+	 * 
+	 * @return a list of the current allowed file extensions
+	 */
+	public List<String> getAllowedFileExtensions();
+
+	/**
+	 * Gets the maximum allowed file upload size.
+	 * 
+	 * @return the current allowed file upload size
+	 */
+	public int getAllowedFileUploadSize();
+
+	/**
+	 * Gets the name of the password parameter used during user authentication.
+	 * 
+	 * @return the name of the password parameter
+	 */
+	public String getPasswordParameterName();
+
+	/**
+	 * Gets the name of the username parameter used during user authentication.
+	 * 
+	 * @return the name of the username parameter
+	 */
+	public String getUsernameParameterName();
+
+	/**
+	 * Gets the encryption algorithm used by ESAPI to protect data. This is
+	 * mostly used for compatibility with ESAPI 1.4; ESAPI 2.0 prefers to
+	 * use "cipher transformation" since it supports multiple cipher modes
+	 * and padding schemes.
+	 * 
+	 * @return the current encryption algorithm
+	 */
+	public String getEncryptionAlgorithm();
+
+	/**
+	 * Retrieve the <i>cipher transformation</i>. In general, the cipher transformation
+	 * is a specification of cipher algorithm, cipher mode, and padding scheme
+	 * and in general, is a {@code String} that takes the following form:
+	 * <pre>
+	 * 		<i>cipher_alg</i>/<i>cipher_mode[bits]</i>/<i>padding_scheme</i>
+	 * </pre>
+	 * where <i>cipher_alg</i> is the JCE cipher algorithm (e.g., "DESede"),
+	 * <i>cipher_mode</i> is the cipher mode (e.g., "CBC", "CFB", "CTR", etc.),
+	 * and <i>padding_scheme</i> is the cipher padding scheme (e.g., "NONE" for
+	 * no padding, "PKCS5Padding" for PKCS#5 padding, etc.) and where
+	 * <i>[bits]</i> is an optional bit size that applies to certain cipher
+	 * modes such as {@code CFB} and {@code OFB}. Using modes such as CFB and
+	 * OFB, block ciphers can encrypt data in units smaller than the cipher's
+	 * actual block size. When requesting such a mode, you may optionally
+	 * specify the number of bits to be processed at a time. This generally must
+	 * be an integral multiple of 8-bits so that it can specify a whole number
+	 * of octets. 
+	 * </p><p>
+	 * Examples are:
+	 * <pre>
+	 * 		"AES/ECB/NoPadding"		// Default for ESAPI Java 1.4 (insecure)
+	 * 		"AES/CBC/PKCS5Padding"	// Default for ESAPI Java 2.0
+	 * 		"DESede/OFB32/PKCS5Padding"
+	 * </pre>
+	 * <b>NOTE:</b> Occasionally, in cryptographic literature, you may also
+	 * see the key size (in bits) specified after the cipher algorithm in the
+	 * cipher transformation. Generally, this is done to account for cipher
+	 * algorithms that have variable key sizes. The Blowfish cipher for example
+	 * supports key sizes from 32 to 448 bits. So for Blowfish, you might see
+	 * a cipher transformation something like this:
+	 * <pre>
+	 * 		"Blowfish-192/CFB8/PKCS5Padding"
+	 * </pre>
+	 * in the cryptographic literature. It should be noted that the Java
+	 * Cryptography Extensions (JCE) do not generally support this (at least
+	 * not the reference JCE implementation of "SunJCE"), and therefore it
+	 * should be avoided.
+	 * @return	The cipher transformation.
+	 */
+    public String getCipherTransformation();
+    
+    /**
+     * Set the cipher transformation. This allows a different cipher transformation
+     * to be used without changing the {@code ESAPI.properties} file. For instance
+     * you may normally want to use AES/CBC/PKCS5Padding, but have some legacy
+     * encryption where you have ciphertext that was encrypted using 3DES.
+     * 
+     * @param cipherXform	The new cipher transformation. See
+     * 						{@link #getCipherTransformation} for format. If
+     * 						{@code null} is passed as the parameter, the cipher
+     * 						transformation will be set to the the default taken
+     * 						from the property {@code Encryptor.CipherTransformation}
+     * 						in the {@code ESAPI.properties} file. <b>BEWARE:</b>
+     * 						there is <b>NO</b> sanity checking here (other than
+     * 						the empty string, and then, only if Java assertions are
+     * 						enabled), so if you set this wrong, you will not get
+     * 						any errors until you later try to use it to encrypt
+     * 						or decrypt data.
+     * @return	The previous cipher transformation is returned for convenience,
+     * 			with the assumption that you may wish to restore it once you have
+     * 			completed the encryption / decryption with the new cipher
+     * 			transformation.
+     * @deprecated To be replaced by new class in ESAPI 2.1, but here if you need it
+     *          until then. Details of replacement forthcoming to ESAPI-Dev list.
+     */
+    @Deprecated
+    public String setCipherTransformation(String cipherXform);
+
+    /**
+     * Retrieve the <i>preferred</i> JCE provider for ESAPI and your application.
+     * ESAPI 2.0 now allows setting the property
+     * {@code Encryptor.PreferredJCEProvider} in the
+     * {@code ESAPI.properties} file, which will cause the specified JCE
+     * provider to be automatically and dynamically loaded (assuming that
+     * {@code SecurityManager} permissions allow) as the Ii>preferred</i>
+     * JCE provider. (Note this only happens if the JCE provider is not already
+     * loaded.) This method returns the property {@code Encryptor.PreferredJCEProvider}.
+     * </p<p>
+     * By default, this {@code Encryptor.PreferredJCEProvider} property is set
+     * to an empty string, which means that the preferred JCE provider is not
+     * changed.
+     * @return The property {@code Encryptor.PreferredJCEProvider} is returned.
+     * @see org.owasp.esapi.crypto.SecurityProviderLoader
+     */
+    public String getPreferredJCEProvider();
+    
+// TODO - DISCUSS: Where should this web page (below) go? Maybe with the Javadoc? But where?
+//				   Think it makes more sense as part of the release notes, but OTOH, I
+//				   really don't want to rewrite this as a Wiki page either.
+    /**
+     * Determines whether the {@code CipherText} should be used with a Message
+     * Authentication Code (MAC). Generally this makes for a more robust cryptographic
+     * scheme, but there are some minor performance implications. Controlled by
+     * the ESAPI property <i>Encryptor.CipherText.useMAC</i>.
+     * </p><p>
+     * For further details, see the "Advanced Usage" section of
+     * <a href="http://www.owasp.org/ESAPI_2.0_ReleaseNotes_CryptoChanges.html">
+     * "Why Is OWASP Changing ESAPI Encryption?"</a>.
+     * </p>
+     * @return	{@code true} if a you want a MAC to be used, otherwise {@code false}.
+     */
+    public boolean useMACforCipherText();
+
+    /**
+     * Indicates whether the {@code PlainText} objects may be overwritten after
+     * they have been encrypted. Generally this is a good idea, especially if
+     * your VM is shared by multiple applications (e.g., multiple applications
+     * running in the same J2EE container) or if there is a possibility that
+     * your VM may leave a core dump (say because it is running non-native
+     * Java code.
+     * <p>
+     * Controlled by the property {@code Encryptor.PlainText.overwrite} in
+     * the {@code ESAPI.properties} file.
+     * </p>
+     * @return	True if it is OK to overwrite the {@code PlainText} objects
+     *			after encrypting, false otherwise.
+     */
+    public boolean overwritePlainText();
+    
+    /**
+     * Get a string indicating how to compute an Initialization Vector (IV).
+     * Currently supported modes are "random" to generate a random IV or
+     * "fixed" to use a fixed (static) IV. If a "fixed" IV is chosen, then the
+     * the value of this fixed IV must be specified as the property
+     * {@code Encryptor.fixedIV} and be of the appropriate length.
+     * 
+     * @return A string specifying the IV type. Should be "random" or "fixed".
+     * 
+     * @see #getFixedIV()
+     */
+    public String getIVType();
+    
+    /**
+     * If a "fixed" (i.e., static) Initialization Vector (IV) is to be used,
+     * this will return the IV value as a hex-encoded string.
+     * @return The fixed IV as a hex-encoded string.
+     */
+    public String getFixedIV();
+    
+    /**
+     * Return a {@code List} of strings of combined cipher modes that support
+     * <b>both</b> confidentiality and authenticity. These would be preferred
+     * cipher modes to use if your JCE provider supports them. If such a
+     * cipher mode is used, no explicit <i>separate</i> MAC is calculated as part of
+     * the {@code CipherText} object upon encryption nor is any attempt made
+     * to verify the same on decryption.
+     * </p><p>
+     * The list is taken from the comma-separated list of cipher modes specified
+     * by the ESAPI property
+     * {@code Encryptor.cipher_modes.combined_modes}.
+     * 
+     * @return The parsed list of comma-separated cipher modes if the property
+     * was specified in {@code ESAPI.properties}; otherwise the empty list is
+     * returned.
+     */
+    public List<String> getCombinedCipherModes();
+
+    /**
+     * Return {@code List} of strings of additional cipher modes that are
+     * permitted (i.e., in <i>addition<i> to those returned by
+     * {@link #getPreferredCipherModes()}) to be used for encryption and
+     * decryption operations.
+     * </p><p>
+     * The list is taken from the comma-separated list of cipher modes specified
+     * by the ESAPI property
+     * {@code Encryptor.cipher_modes.additional_allowed}.
+     * 
+     * @return The parsed list of comma-separated cipher modes if the property
+     * was specified in {@code ESAPI.properties}; otherwise the empty list is
+     * returned.
+     *
+     * @see #getPreferredCipherModes() 
+     */
+    public List<String> getAdditionalAllowedCipherModes();
+
+	/**
+	 * Gets the hashing algorithm used by ESAPI to hash data.
+	 * 
+	 * @return the current hashing algorithm
+	 */
+	public String getHashAlgorithm();
+
+	/**
+	 * Gets the hash iterations used by ESAPI to hash data.
+	 * 
+	 * @return the current hashing algorithm
+	 */
+	public int getHashIterations();
+
+	/**
+	 * Retrieve the Pseudo Random Function (PRF) used by the ESAPI
+	 * Key Derivation Function (KDF).
+	 * 
+	 * @return	The KDF PRF algorithm name.
+	 */
+	public String getKDFPseudoRandomFunction();
+	
+	/**
+	 * Gets the character encoding scheme supported by this application. This is used to set the
+	 * character encoding scheme on requests and responses when setCharacterEncoding() is called
+	 * on SafeRequests and SafeResponses. This scheme is also used for encoding/decoding URLs 
+	 * and any other place where the current encoding scheme needs to be known.
+	 * <br><br>
+	 * Note: This does not get the configured response content type. That is accessed by calling 
+	 * getResponseContentType().
+	 * 
+	 * @return the current character encoding scheme
+	 */
+	public String getCharacterEncoding();
+
+	/**
+	 * Return true if multiple encoding is allowed
+	 *
+	 * @return whether multiple encoding is allowed when canonicalizing data
+	 */
+	public boolean getAllowMultipleEncoding();
+
+	/**
+	 * Return true if mixed encoding is allowed
+	 *
+	 * @return whether mixed encoding is allowed when canonicalizing data
+	 */
+	public boolean getAllowMixedEncoding();
+
+	/**
+	 * Returns the List of Codecs to use when canonicalizing data
+	 * 
+	 * @return the codec list
+	 */
+	public List<String> getDefaultCanonicalizationCodecs();
+
+	/**
+	 * Gets the digital signature algorithm used by ESAPI to generate and verify signatures.
+	 * 
+	 * @return the current digital signature algorithm
+	 */
+	public String getDigitalSignatureAlgorithm();
+
+	/**
+	 * Gets the digital signature key length used by ESAPI to generate and verify signatures.
+	 * 
+	 * @return the current digital signature key length
+	 */
+	public int getDigitalSignatureKeyLength();
+		   
+	/**
+	 * Gets the random number generation algorithm used to generate random numbers where needed.
+	 * 
+	 * @return the current random number generation algorithm
+	 */
+	public String getRandomAlgorithm();
+
+	/**
+	 * Gets the number of login attempts allowed before the user's account is locked. If this 
+	 * many failures are detected within the alloted time period, the user's account will be locked.
+	 * 
+	 * @return the number of failed login attempts that cause an account to be locked
+	 */
+	public int getAllowedLoginAttempts();
+
+	/**
+	 * Gets the maximum number of old password hashes that should be retained. These hashes can 
+	 * be used to ensure that the user doesn't reuse the specified number of previous passwords
+	 * when they change their password.
+	 * 
+	 * @return the number of old hashed passwords to retain
+	 */
+	public int getMaxOldPasswordHashes();
+
+	/**
+	 * Allows for complete disabling of all intrusion detection mechanisms
+	 * 
+	 * @return true if intrusion detection should be disabled
+	 */
+	public boolean getDisableIntrusionDetection();
+	
+	/**
+	 * Gets the intrusion detection quota for the specified event.
+	 * 
+	 * @param eventName the name of the event whose quota is desired
+	 * 
+	 * @return the Quota that has been configured for the specified type of event
+	 */
+	public Threshold getQuota(String eventName);
+
+	/**
+	 * Gets a file from the resource directory
+     *
+     * @param filename The file name resource.
+     * @return A {@code File} object representing the specified file name or null if not found.
+     */
+    public File getResourceFile( String filename );
+    
+	/**
+	 * Forces new cookies to have HttpOnly flag set.
+     */
+    public boolean getForceHttpOnlySession() ;
+
+	/**
+	 * Forces session cookies to have Secure flag set.
+     */
+    public boolean getForceSecureSession() ;
+
+	/**
+	 * Forces new cookies to have HttpOnly flag set.
+     */
+    public boolean getForceHttpOnlyCookies() ;
+
+	/**
+	 * Forces new cookies to have Secure flag set.
+     */
+    public boolean getForceSecureCookies() ;
+
+	/**
+	 * Returns the maximum allowable HTTP header size.
+	 */
+	public int getMaxHttpHeaderSize() ;
+
+	/**
+	 * Gets an InputStream to a file in the resource directory
+     *
+     * @param filename A file name in the resource directory.
+     * @return An {@code InputStream} to the specified file name in the resource directory.
+     * @throws IOException If the specified file name cannot be found or opened for reading.
+     */
+    public InputStream getResourceStream( String filename ) throws IOException;
+
+    	
+	/**
+	 * Sets the ESAPI resource directory.
+	 * 
+	 * @param dir The location of the resource directory.
+	 */
+	public void setResourceDirectory(String dir);
+	
+	/**
+	 * Gets the content type for responses used when setSafeContentType() is called.
+	 * <br><br>
+	 * Note: This does not get the configured character encoding scheme. That is accessed by calling 
+	 * getCharacterEncoding().
+	 * 
+	 * @return The current content-type set for responses.
+	 */
+	public String getResponseContentType();
+
+	/**
+	 * This method returns the configured name of the session identifier, 
+	 * likely "JSESSIONID" though this can be overridden.
+	 * 
+	 * @return The name of the session identifier, like "JSESSIONID"
+	 */
+	public String getHttpSessionIdName();
+	
+	/**
+	 * Gets the length of the time to live window for remember me tokens (in milliseconds).
+	 * 
+	 * @return The time to live length for generated remember me tokens.
+	 */
+	public long getRememberTokenDuration();
+
+	
+	/**
+	 * Gets the idle timeout length for sessions (in milliseconds). This is the amount of time that a session
+	 * can live before it expires due to lack of activity. Applications or frameworks could provide a reauthenticate
+	 * function that enables a session to continue after reauthentication.
+	 * 
+	 * @return The session idle timeout length.
+	 */
+	public int getSessionIdleTimeoutLength();
+	
+	/**
+	 * Gets the absolute timeout length for sessions (in milliseconds). This is the amount of time that a session
+	 * can live before it expires regardless of the amount of user activity. Applications or frameworks could 
+	 * provide a reauthenticate function that enables a session to continue after reauthentication.
+	 * 
+	 * @return The session absolute timeout length.
+	 */
+	public int getSessionAbsoluteTimeoutLength();
+	
+	
+	/**
+	 * Returns whether HTML entity encoding should be applied to log entries.
+	 * 
+	 * @return True if log entries are to be HTML Entity encoded. False otherwise.
+	 */
+	public boolean getLogEncodingRequired();
+	
+	/**
+	 * Returns whether ESAPI should log the application name. This might be clutter in some
+	 * single-server/single-app environments.
+	 * 
+	 * @return True if ESAPI should log the application name, False otherwise
+	 */
+	public boolean getLogApplicationName();
+
+	/**
+	 * Returns whether ESAPI should log the server IP. This might be clutter in some
+	 * single-server environments.
+	 * 
+	 * @return True if ESAPI should log the server IP and port, False otherwise
+	 */
+	public boolean getLogServerIP();
+
+	/**
+	 * Returns the current log level.
+	 * @return	An integer representing the current log level.
+	 */
+    public int getLogLevel();
+	
+    /**
+     * Get the name of the log file specified in the ESAPI configuration properties file. Return a default value 
+     * if it is not specified.
+     * 
+     * @return the log file name defined in the properties file.
+     */
+    public String getLogFileName();
+
+    /**
+     * Get the maximum size of a single log file from the ESAPI configuration properties file. Return a default value 
+     * if it is not specified. Once the log hits this file size, it will roll over into a new log.
+     * 
+     * @return the maximum size of a single log file (in bytes).
+     */
+    public int getMaxLogFileSize();
+
+	/**
+	 * Models a simple threshold as a count and an interval, along with a set of actions to take if 
+	 * the threshold is exceeded. These thresholds are used to define when the accumulation of a particular event
+	 * has met a set number within the specified time period. Once a threshold value has been met, various
+	 * actions can be taken at that point.
+	 */
+	public static class Threshold {
+		
+		/** The name of this threshold. */
+		public String name = null;
+		
+		/** The count at which this threshold is triggered. */
+		public int count = 0;
+		
+		/** 
+		 * The time frame within which 'count' number of actions has to be detected in order to
+		 * trigger this threshold.
+		 */
+		public long interval = 0;
+		
+		/**
+		 * The list of actions to take if the threshold is met. It is expected that this is a list of Strings, but 
+		 * your implementation could have this be a list of any type of 'actions' you wish to define. 
+		 */
+		public List<String> actions = null;
+
+		/**
+		 * Constructs a threshold that is composed of its name, its threshold count, the time window for
+		 * the threshold, and the actions to take if the threshold is triggered.
+		 * 
+		 * @param name The name of this threshold.
+		 * @param count The count at which this threshold is triggered.
+		 * @param interval The time frame within which 'count' number of actions has to be detected in order to
+		 * trigger this threshold.
+		 * @param actions The list of actions to take if the threshold is met.
+		 */
+		public Threshold(String name, int count, long interval, List<String> actions) {
+			this.name = name;
+			this.count = count;
+			this.interval = interval;
+			this.actions = actions;
+		}
+	}
+
+	/**
+	 * Returns the default working directory for executing native processes with Runtime.exec().
+	 */
+	public File getWorkingDirectory();
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/StringUtilities.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/StringUtilities.java.svn-base
new file mode 100644
index 0000000..e41dd03
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/StringUtilities.java.svn-base
@@ -0,0 +1,199 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+/**
+ * String utilities used in various filters.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ * href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public class StringUtilities {
+
+	private static final Pattern p = Pattern.compile( "\\s");
+	public static String replaceLinearWhiteSpace( String input ) {
+		return p.matcher(input).replaceAll( " " );
+	}
+	
+	/**
+	 * Removes all unprintable characters from a string 
+	 * and replaces with a space.
+	 * @param input
+	 * @return the stripped value
+	 */
+	public static String stripControls( String input ) {
+		StringBuilder sb = new StringBuilder();
+		for ( int i=0; i<input.length(); i++ ) {
+			char c = input.charAt( i );
+			if ( c > 0x20 && c < 0x7f ) {
+				sb.append( c );
+			} else {
+				sb.append( ' ' );
+			}
+		}
+		return sb.toString();
+	}
+
+	
+    /**
+     * Union multiple character arrays.
+     * 
+     * @param list the char[]s to union
+     * @return the union of the char[]s
+     */
+    public static char[] union(char[]... list) {
+    	StringBuilder sb = new StringBuilder();
+    	
+    	for (char[] characters : list) {
+	        for (int i = 0; i < list.length; i++) {
+	            if (!contains(sb, characters[i]))
+	                sb.append(list[i]);
+	        }
+    	}
+
+        char[] toReturn = new char[sb.length()];
+        sb.getChars(0, sb.length(), toReturn, 0);
+        Arrays.sort(toReturn);
+        return toReturn;
+    }
+
+
+	/**
+     * Returns true if the character is contained in the provided StringBuilder.
+     * @param input 	The input
+     * @param c 		The character to check for to see if {@code input} contains.
+     * @return			True if the specified character is contained; false otherwise.
+     */
+    public static boolean contains(StringBuilder input, char c) {
+        for (int i = 0; i < input.length(); i++) {
+            if (input.charAt(i) == c)
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the replace value if the value of test is null, "null", or ""
+     *
+     * @param test The value to test
+     * @param replace The replacement value
+     * @return The correct value
+     */
+    public static String replaceNull( String test, String replace ) {
+        return ( test == null || "null".equalsIgnoreCase( test.trim() ) || "".equals( test.trim() ) ) ? replace : test;
+    }
+
+    /**
+     * Calculate the Edit Distance between 2 Strings as a measure of similarity.
+     *
+     * For example, if the strings GUMBO and GAMBOL are passed in, the edit distance
+     * is 2, since GUMBO transforms into GAMBOL by replacing the 'U' with an 'A' and
+     * adding an 'L'.
+     *
+     * Original Implementation of this algorithm by Michael Gilleland, adapted by
+     * Chas Emerick for the Apache-Commons project
+     * http://www.merriampark.com/ldjava.htm
+     *
+     * @param s The source string
+     * @param t The target String
+     * @return The edit distance between the 2 strings
+     */
+    public static int getLevenshteinDistance (String s, String t) {
+      if (s == null || t == null) {
+        throw new IllegalArgumentException("Strings must not be null");
+      }
+
+      int n = s.length(); // length of s
+      int m = t.length(); // length of t
+
+      if (n == 0) {
+        return m;
+      } else if (m == 0) {
+        return n;
+      }
+
+      int p[] = new int[n+1]; //'previous' cost array, horizontally
+      int d[] = new int[n+1]; // cost array, horizontally
+      int _d[]; //placeholder to assist in swapping p and d
+
+      // indexes into strings s and t
+      int i; // iterates through s
+      int j; // iterates through t
+
+      char t_j; // jth character of t
+
+      int cost; // cost
+
+      for (i = 0; i<=n; i++) {
+         p[i] = i;
+      }
+
+      for (j = 1; j<=m; j++) {
+         t_j = t.charAt(j-1);
+         d[0] = j;
+
+         for (i=1; i<=n; i++) {
+            cost = s.charAt(i-1)==t_j ? 0 : 1;
+            // minimum of cell to the left+1, to the top+1, diagonally left and up +cost
+            d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1),  p[i-1]+cost);
+         }
+
+         // copy current distance counts to 'previous row' distance counts
+         _d = p;
+         p = d;
+         d = _d;
+      }
+
+      // our last action in the above loop was to switch d and p, so p now
+      // actually has the most recent cost counts
+      return p[n];
+    }
+
+    /**
+     * Check to ensure that a {@code String} is not null or empty (after optional
+     * trimming of leading and trailing whitespace). Usually used with
+     * assertions, as in
+     * <pre>
+     * 		assert StringUtils.notNullOrEmpty(cipherXform, true) :
+     * 								"Cipher transformation may not be null or empty!";
+     * </pre>
+     *
+     * @param str	The {@code String} to be checked.
+     * @param trim	If {@code true}, the string is first trimmed before checking
+     * 				to see if it is empty, otherwise it is not.
+     * @return		True if the string is null or empty (after possible
+     * 				trimming); otherwise false.
+     * @since 2.0
+     */
+    public static boolean notNullOrEmpty(String str, boolean trim) {
+    	if ( trim ) {
+    		return !( str == null || str.trim().equals("") );
+    	} else {
+    		return !( str == null || str.equals("") );
+    	}
+    }
+
+    /**
+     * Returns true if String is empty ("") or null.
+     */
+    public static boolean isEmpty(String str) {
+        return str == null || str.length() == 0;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/User.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/User.java.svn-base
new file mode 100644
index 0000000..f6b9fa2
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/User.java.svn-base
@@ -0,0 +1,760 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.AuthenticationHostException;
+import org.owasp.esapi.errors.EncryptionException;
+
+import javax.servlet.http.HttpSession;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.*;
+
+/**
+ * The User interface represents an application user or user account. There is quite a lot of information that an
+ * application must store for each user in order to enforce security properly. There are also many rules that govern
+ * authentication and identity management.
+ * <P>
+ * A user account can be in one of several states. When first created, a User should be disabled, not expired, and
+ * unlocked. To start using the account, an administrator should enable the account. The account can be locked for a
+ * number of reasons, most commonly because they have failed login for too many times. Finally, the account can expire
+ * after the expiration date has been reached. The User must be enabled, not expired, and unlocked in order to pass
+ * authentication.
+ * 
+ * @author <a href="mailto:jeff.williams at aspectsecurity.com?subject=ESAPI question">Jeff Williams</a> at <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com) <a href="http://www.digital-ritual.com">Digital Ritual Software</a>
+ * @since June 1, 2007
+ */
+
+public interface User extends Principal, Serializable {
+	/**
+	 * @return the locale
+	 */
+	public Locale getLocale();
+
+	/**
+	 * @param locale the locale to set
+	 */
+	public void setLocale(Locale locale);
+
+    /**
+     * Adds a role to this user's account.
+     * 
+     * @param role 
+     * 		the role to add
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void addRole(String role) throws AuthenticationException;
+
+    /**
+     * Adds a set of roles to this user's account.
+     * 
+     * @param newRoles 
+     * 		the new roles to add
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void addRoles(Set<String> newRoles) throws AuthenticationException;
+
+    /**
+     * Sets the user's password, performing a verification of the user's old password, the equality of the two new
+     * passwords, and the strength of the new password.
+     * 
+     * @param oldPassword 
+     * 		the old password
+     * @param newPassword1 
+     * 		the new password
+     * @param newPassword2 
+     * 		the new password - used to verify that the new password was typed correctly
+     * 
+     * @throws AuthenticationException 
+     * 		if newPassword1 does not match newPassword2, if oldPassword does not match the stored old password, or if the new password does not meet complexity requirements 
+     * @throws EncryptionException 
+     */
+    void changePassword(String oldPassword, String newPassword1, String newPassword2) throws AuthenticationException, EncryptionException;
+
+    /**
+     * Disable this user's account.
+     */
+    void disable();
+
+    /**
+     * Enable this user's account.
+     */
+    void enable();
+
+    /**
+     * Gets this user's account id number.
+     * 
+     * @return the account id
+     */
+    long getAccountId();
+    
+    /**
+     * Gets this user's account name.
+     * 
+     * @return the account name
+     */
+    String getAccountName();
+
+    /**
+     * Gets the CSRF token for this user's current sessions.
+     * 
+     * @return the CSRF token
+     */
+    String getCSRFToken();
+
+    /**
+     * Returns the date that this user's account will expire.
+     *
+     * @return Date representing the account expiration time.
+     */
+    Date getExpirationTime();
+
+    /**
+     * Returns the number of failed login attempts since the last successful login for an account. This method is
+     * intended to be used as a part of the account lockout feature, to help protect against brute force attacks.
+     * However, the implementor should be aware that lockouts can be used to prevent access to an application by a
+     * legitimate user, and should consider the risk of denial of service.
+     * 
+     * @return the number of failed login attempts since the last successful login
+     */
+    int getFailedLoginCount();
+
+    /**
+     * Returns the last host address used by the user. This will be used in any log messages generated by the processing
+     * of this request.
+     * 
+     * @return the last host address used by the user
+     */
+    String getLastHostAddress();
+
+	/**
+     * Returns the date of the last failed login time for a user. This date should be used in a message to users after a
+     * successful login, to notify them of potential attack activity on their account.
+     * 
+     * @return date of the last failed login
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    Date getLastFailedLoginTime() throws AuthenticationException;
+
+    /**
+     * Returns the date of the last successful login time for a user. This date should be used in a message to users
+     * after a successful login, to notify them of potential attack activity on their account.
+     * 
+     * @return date of the last successful login
+     */
+    Date getLastLoginTime();
+
+    /**
+     * Gets the date of user's last password change.
+     * 
+     * @return the date of last password change
+     */
+    Date getLastPasswordChangeTime();
+
+    /**
+     * Gets the roles assigned to a particular account.
+     * 
+     * @return an immutable set of roles
+     */
+    Set<String> getRoles();
+
+    /**
+     * Gets the screen name (alias) for the current user.
+     * 
+     * @return the screen name
+     */
+    String getScreenName();
+
+    /**
+     * Adds a session for this User.
+     * 
+     * @param s
+     * 			The session to associate with this user.
+     */
+    void addSession( HttpSession s );
+    
+    /**
+     * Removes a session for this User.
+     * 
+     * @param s
+     * 			The session to remove from being associated with this user.
+     */
+    void removeSession( HttpSession s );
+    
+    /**
+     * Returns the list of sessions associated with this User.
+     * @return
+     */
+    Set getSessions();
+    
+    /**
+     * Increment failed login count.
+     */
+    void incrementFailedLoginCount();
+
+    /**
+     * Checks if user is anonymous.
+     * 
+     * @return true, if user is anonymous
+     */
+    boolean isAnonymous();
+
+    /**
+     * Checks if this user's account is currently enabled.
+     * 
+     * @return true, if account is enabled 
+     */
+    boolean isEnabled();
+
+    /**
+     * Checks if this user's account is expired.
+     * 
+     * @return true, if account is expired
+     */
+    boolean isExpired();
+
+    /**
+     * Checks if this user's account is assigned a particular role.
+     * 
+     * @param role 
+     * 		the role for which to check
+     * 
+     * @return true, if role has been assigned to user
+     */
+    boolean isInRole(String role);
+
+    /**
+     * Checks if this user's account is locked.
+     * 
+     * @return true, if account is locked
+     */
+    boolean isLocked();
+
+    /**
+     * Tests to see if the user is currently logged in.
+     * 
+     * @return true, if the user is logged in
+     */
+    boolean isLoggedIn();
+
+    /**
+     * Tests to see if this user's session has exceeded the absolute time out based 
+      * on ESAPI's configuration settings.
+     * 
+     * @return true, if user's session has exceeded the absolute time out
+     */
+    boolean isSessionAbsoluteTimeout();
+
+    /**
+      * Tests to see if the user's session has timed out from inactivity based 
+      * on ESAPI's configuration settings.
+      * 
+      * A session may timeout prior to ESAPI's configuration setting due to 
+      * the servlet container setting for session-timeout in web.xml. The 
+      * following is an example of a web.xml session-timeout set for one hour. 	
+      *
+      * <session-config>
+      *   <session-timeout>60</session-timeout> 
+      * </session-config>
+      * 
+      * @return true, if user's session has timed out from inactivity based 
+      *               on ESAPI configuration
+      */
+     boolean isSessionTimeout();
+
+    /**
+     * Lock this user's account.
+     */
+    void lock();
+
+    /**
+     * Login with password.
+     * 
+     * @param password 
+     * 		the password
+     * @throws AuthenticationException 
+     * 		if login fails
+     */
+    void loginWithPassword(String password) throws AuthenticationException;
+
+    /**
+     * Logout this user.
+     */
+    void logout();
+
+    /**
+     * Removes a role from this user's account.
+     * 
+     * @param role 
+     * 		the role to remove
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void removeRole(String role) throws AuthenticationException;
+
+    /**
+     * Returns a token to be used as a prevention against CSRF attacks. This token should be added to all links and
+     * forms. The application should verify that all requests contain the token, or they may have been generated by a
+     * CSRF attack. It is generally best to perform the check in a centralized location, either a filter or controller.
+     * See the verifyCSRFToken method.
+     * 
+     * @return the new CSRF token
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    String resetCSRFToken() throws AuthenticationException;
+
+    /**
+     * Sets this user's account name.
+     * 
+     * @param accountName the new account name
+     */
+    void setAccountName(String accountName);
+
+    /**
+     * Sets the date and time when this user's account will expire.
+     * 
+     * @param expirationTime the new expiration time
+     */
+	void setExpirationTime(Date expirationTime);
+
+	/**
+     * Sets the roles for this account.
+     * 
+     * @param roles 
+     * 		the new roles
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void setRoles(Set<String> roles) throws AuthenticationException;
+
+    /**
+     * Sets the screen name (username alias) for this user.
+     * 
+     * @param screenName the new screen name
+     */
+    void setScreenName(String screenName);
+
+    /**
+     * Unlock this user's account.
+     */
+    void unlock();
+
+	/**
+	 * Verify that the supplied password matches the password for this user. This method
+	 * is typically used for "reauthentication" for the most sensitive functions, such
+	 * as transactions, changing email address, and changing other account information.
+	 * 
+	 * @param password 
+	 * 		the password that the user entered
+	 * 
+	 * @return true, if the password passed in matches the account's password
+	 * 
+	 * @throws EncryptionException 
+	 */
+	public boolean verifyPassword(String password) throws EncryptionException;
+
+	/**
+	 * Set the time of the last failed login for this user.
+	 * 
+	 * @param lastFailedLoginTime the date and time when the user just failed to login correctly.
+	 */
+	void setLastFailedLoginTime(Date lastFailedLoginTime);
+	
+	/**
+	 * Set the last remote host address used by this user.
+	 * 
+	 * @param remoteHost The address of the user's current source host.
+	 */
+	void setLastHostAddress(String remoteHost) throws AuthenticationHostException;
+	
+	/**
+	 * Set the time of the last successful login for this user.
+	 * 
+	 * @param lastLoginTime the date and time when the user just successfully logged in.
+	 */
+	void setLastLoginTime(Date lastLoginTime);
+	
+	/**
+	 * Set the time of the last password change for this user.
+	 * 
+	 * @param lastPasswordChangeTime the date and time when the user just successfully changed his/her password.
+	 */
+	void setLastPasswordChangeTime(Date lastPasswordChangeTime);
+
+	/**
+	 * Returns the hashmap used to store security events for this user. Used by the
+	 * IntrusionDetector.
+	 */
+	HashMap getEventMap();
+	
+
+	/**
+	 * The ANONYMOUS user is used to represent an unidentified user. Since there is
+	 * always a real user, the ANONYMOUS user is better than using null to represent
+	 * this.
+	 */
+    public final User ANONYMOUS = new User() {
+
+		private static final long serialVersionUID = -1850916950784965502L;
+
+		private String csrfToken = "";
+    	private Set sessions = new HashSet();
+		private Locale locale = null;
+    	
+    	/**
+         * {@inheritDoc}
+         */
+        public void addRole(String role) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addRoles(Set newRoles) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void changePassword(String oldPassword, String newPassword1,
+                String newPassword2) throws AuthenticationException,
+                EncryptionException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void disable() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void enable() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public long getAccountId() {
+	        return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String getAccountName() {
+	        return "Anonymous";
+        }
+
+		/**
+		 * Alias method that is equivalent to getAccountName()
+		 * 
+		 * @return the name of the current user's account
+         */
+        public String getName() {
+        	return getAccountName();
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public String getCSRFToken() {
+	        return csrfToken;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getExpirationTime() {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getFailedLoginCount() {
+	        return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getLastFailedLoginTime() throws AuthenticationException {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String getLastHostAddress() {
+	        return "unknown";
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getLastLoginTime() {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getLastPasswordChangeTime() {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Set<String> getRoles() {
+	        return new HashSet<String>();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String getScreenName() {
+	        return "Anonymous";
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addSession(HttpSession s)  {
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void removeSession(HttpSession s)  {
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Set getSessions()  {
+            return sessions;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void incrementFailedLoginCount() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isAnonymous() {
+	        return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isEnabled() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isExpired() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isInRole(String role) {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isLocked() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isLoggedIn() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isSessionAbsoluteTimeout() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isSessionTimeout() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void lock() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void loginWithPassword(String password)
+                throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void logout() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void removeRole(String role) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String resetCSRFToken() throws AuthenticationException {
+    		csrfToken = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+    		return csrfToken;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void setAccountName(String accountName) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+    	public void setExpirationTime(Date expirationTime) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+        
+    	/**
+         * {@inheritDoc}
+         */
+        public void setRoles(Set roles) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void setScreenName(String screenName) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void unlock() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean verifyPassword(String password) throws EncryptionException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void setLastFailedLoginTime(Date lastFailedLoginTime) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+    	public void setLastLoginTime(Date lastLoginTime) {
+    		throw new RuntimeException("Invalid operation for the anonymous user");
+    	}
+
+    	/**
+         * {@inheritDoc}
+         */
+     	public void setLastHostAddress(String remoteHost) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+                
+     	/**
+         * {@inheritDoc}
+         */
+        public void setLastPasswordChangeTime(Date lastPasswordChangeTime) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+        
+        /**
+         *  {@inheritDoc}
+         */
+        public HashMap getEventMap() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+         /**
+    	 * @return the locale
+    	 */
+    	public Locale getLocale() {
+    		return locale;
+    	}
+
+    	/**
+    	 * @param locale the locale to set
+    	 */
+    	public void setLocale(Locale locale) {
+    		this.locale = locale;
+    	}
+    };
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/ValidationErrorList.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/ValidationErrorList.java.svn-base
new file mode 100644
index 0000000..df19444
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/ValidationErrorList.java.svn-base
@@ -0,0 +1,137 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * 
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * The ValidationErrorList class defines a well-formed collection of 
+ * ValidationExceptions so that groups of validation functions can be 
+ * called in a non-blocking fashion.
+ * <P>
+ * To use the ValidationErrorList to execute groups of validation 
+ * attempts, your controller code would look something like:
+ * 
+ * <PRE>
+ * ValidationErrorList() errorList = new ValidationErrorList();.
+ * String name  = getValidInput("Name", form.getName(), "SomeESAPIRegExName1", 255, false, errorList);
+ * String address = getValidInput("Address", form.getAddress(), "SomeESAPIRegExName2", 255, false, errorList);
+ * Integer weight = getValidInteger("Weight", form.getWeight(), 1, 1000000000, false, errorList);
+ * Integer sortOrder = getValidInteger("Sort Order", form.getSortOrder(), -100000, +100000, false, errorList);
+ * request.setAttribute( "ERROR_LIST", errorList );
+ * </PRE>
+ * 
+ * The at your view layer you would be able to retrieve all
+ * of your error messages via a helper function like:
+ * 
+ * <PRE>
+ * public static ValidationErrorList getErrors() {          
+ *     HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+ *     ValidationErrorList errors = new ValidationErrorList();
+ *     if (request.getAttribute(Constants.ERROR_LIST) != null) {
+ *        errors = (ValidationErrorList)request.getAttribute("ERROR_LIST");
+ *     }
+ * 	   return errors;
+ * }
+ * </PRE>
+ * 
+ * You can list all errors like:
+ * 
+ * <PRE>
+ * <%
+ *      for (Object vo : errorList.errors()) {
+ *         ValidationException ve = (ValidationException)vo;
+ * %>
+ * <%= ESAPI.encoder().encodeForHTML(ve.getMessage()) %><br/>
+ * <%
+ *     }
+ * %>
+ * </PRE>
+ * 
+ * And even check if a specific UI component is in error via calls like:
+ * 
+ * <PRE>
+ * ValidationException e = errorList.getError("Name");
+ * </PRE>
+ * 
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @since August 15, 2008
+ */
+public class ValidationErrorList {
+
+	/**
+	 * Error list of ValidationException's
+	 */
+	private HashMap<String, ValidationException> errorList = new HashMap<String, ValidationException>();
+
+	/**
+	 * Adds a new error to list with a unique named context.
+	 * No action taken if either element is null. 
+	 * Existing contexts will be overwritten.
+	 * 
+	 * @param context Unique named context for this {@code ValidationErrorList}.
+	 * @param vex	A {@code ValidationException}.
+	 */
+	public void addError(String context, ValidationException vex) {
+		if ( context == null ) throw new RuntimeException( "Context for cannot be null: " + vex.getLogMessage(), vex );
+		if ( vex == null ) throw new RuntimeException( "Context (" + context + ") cannot be null" );
+		if (getError(context) != null) throw new RuntimeException("Context (" + context + ") already exists, must be unique");
+		errorList.put(context, vex);
+	}
+
+	/**
+	 * Returns list of ValidationException, or empty list of no errors exist.
+	 * 
+	 * @return List
+	 */
+	public List<ValidationException> errors() {
+		return new ArrayList<ValidationException>( errorList.values() );
+	}
+
+	/**
+	 * Retrieves ValidationException for given context if one exists.
+	 * 
+	 * @param context unique name for each error
+	 * @return ValidationException or null for given context
+	 */
+	public ValidationException getError(String context) {
+		if (context == null) return null;		
+		return errorList.get(context);
+	}
+
+	/**
+	 * Returns true if no error are present.
+	 * 
+	 * @return boolean
+	 */
+	public boolean isEmpty() {
+		return errorList.isEmpty();
+	}
+	
+	/**
+	 * Returns the numbers of errors present.
+	 * 
+	 * @return boolean
+	 */
+	public int size() {
+		return errorList.size();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/ValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/ValidationRule.java.svn-base
new file mode 100644
index 0000000..b9b6835
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/ValidationRule.java.svn-base
@@ -0,0 +1,81 @@
+package org.owasp.esapi;
+
+import java.util.Set;
+
+import org.owasp.esapi.errors.ValidationException;
+
+public interface ValidationRule {
+
+	/**
+	 * Parse the input, throw exceptions if validation fails
+	 * 
+	 * @param context
+	 *            for logging
+	 * @param input
+	 *            the value to be parsed
+	 * @return a validated value
+	 * @throws ValidationException
+	 *             if any validation rules fail
+	 */
+	Object getValid(String context, String input)
+			throws ValidationException;
+
+	/**
+	 * Whether or not a valid valid can be null. getValid will throw an
+	 * Exception and getSafe will return the default value if flag is set to
+	 * true
+	 * 
+	 * @param flag
+	 *            whether or not null values are valid/safe
+	 */
+	void setAllowNull(boolean flag);
+
+	/**
+	 * Programmatically supplied name for the validator
+	 * @return a name, describing the validator
+	 */
+	String getTypeName();
+
+	/**
+	 * @param typeName a name, describing the validator
+	 */
+	void setTypeName(String typeName);
+
+	/**
+	 * @param encoder the encoder to use
+	 */
+	void setEncoder(Encoder encoder);
+
+	/**
+	 * Check if the input is valid, throw an Exception otherwise 
+	 */
+	void assertValid(String context, String input)
+			throws ValidationException;
+
+	/**
+	 * Get a validated value, add the errors to an existing error list
+	 */
+	Object getValid(String context, String input,
+			ValidationErrorList errorList) throws ValidationException;
+
+	/**
+	 * Try to call get valid, then call sanitize, finally return a default value
+	 */
+	Object getSafe(String context, String input);
+	
+	/**
+	 * @return true if the input passes validation
+	 */
+	boolean isValid(String context, String input);
+
+	/**
+	 * String the input of all chars contained in the list
+	 */
+	String whitelist(String input, char[] list);
+	
+	/**
+	 * String the input of all chars contained in the list
+	 */
+	String whitelist(String input, Set<Character> list);
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/Validator.java.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/Validator.java.svn-base
new file mode 100644
index 0000000..951d8c4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/Validator.java.svn-base
@@ -0,0 +1,691 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * The Validator interface defines a set of methods for canonicalizing and
+ * validating untrusted input. Implementors should feel free to extend this
+ * interface to accommodate their own data formats. Rather than throw exceptions,
+ * this interface returns boolean results because not all validation problems
+ * are security issues. Boolean returns allow developers to handle both valid
+ * and invalid results more cleanly than exceptions.
+ * <P>
+ * Implementations must adopt a "whitelist" approach to validation where a
+ * specific pattern or character set is matched. "Blacklist" approaches that
+ * attempt to identify the invalid or disallowed characters are much more likely
+ * to allow a bypass with encoding or other tricks.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Validator {
+
+	void addRule( ValidationRule rule );
+
+	ValidationRule getRule( String name );
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws IntrusionException;
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated input as a String. Invalid input will generate a descriptive ValidationException,
+	 * and input that is clearly an attack will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param type
+	 * 		The regular expression name that maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength
+	 * 		The maximum post-canonicalized String length allowed.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return The canonicalized user input.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Returns validated input as a String with optional canonicalization. Invalid input will generate a descriptive ValidationException,
+	 * and input that is clearly an attack will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param type
+	 * 		The regular expression name that maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength
+	 * 		The maximum post-canonicalized String length allowed.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param canonicalize
+	 *      If canonicalize is true then input will be canonicalized before validation
+	 *
+	 * @return The canonicalized user input.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidInput with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidInput with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls isValidDate and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDate(String context, String input, DateFormat format, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls isValidDate and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a valid date as a Date. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param format
+	 * 		Required formatting of date inputted.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A valid date as a Date
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	Date getValidDate(String context, String input, DateFormat format, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidDate with the supplied errorList to capture ValidationExceptions
+	 */
+	Date getValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML and returns true if no exceptions are thrown.
+	 */
+	boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML and returns true if no exceptions are thrown.
+	 */
+	boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated "safe" HTML that does not contain unwanted scripts in the body, attributes, CSS, URLs, or anywhere else.
+	 * Implementors should reference the OWASP AntiSamy project for ideas
+	 * on how to do HTML validation in a whitelist way, as this is an extremely difficult problem.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param maxLength
+	 * 		The maximum String length allowed.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return Valid safe HTML
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidCreditCard and returns true if no exceptions are thrown.
+	 */
+	boolean isValidCreditCard(String context, String input, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidCreditCard and returns true if no exceptions are thrown.
+	 */
+	boolean isValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a canonicalized and validated credit card number as a String. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A valid credit card number
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidCreditCard(String context, String input, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidCreditCard with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidDirectoryPath and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidDirectoryPath and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a canonicalized and validated directory path as a String, provided that the input
+	 * maps to an existing directory that is an existing subdirectory (at any level) of the specified parent. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException. Instead of throwing a ValidationException
+	 * on error, this variant will store the exception inside of the ValidationErrorList.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual input data to validate.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A valid directory path
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidDirectoryPath with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName with the default list of allowedExtensions
+	 */
+	boolean isValidFileName(String context, String input, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName with the default list of allowedExtensions
+	 */
+	boolean isValidFileName(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a canonicalized and validated file name as a String. Implementors should check for allowed file extensions here, as well as allowed file name characters, as declared in "ESAPI.properties". Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     *
+     * @return A valid file name
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	String getValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidFileName with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidNumber and returns true if no exceptions are thrown.
+	 */
+	boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidNumber and returns true if no exceptions are thrown.
+	 */
+	boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a validated number as a double within the range of minValue to maxValue. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     * @param minValue
+     * 		Lowest legal value for input.
+     * @param maxValue
+     * 		Highest legal value for input.
+     *
+     * @return A validated number as a double.
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML with the supplied errorList to capture ValidationExceptions
+	 */
+	Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidInteger and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidInteger and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a validated integer. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     * @param minValue
+     * 		Lowest legal value for input.
+     * @param maxValue
+     * 		Highest legal value for input.
+     *
+     * @return A validated number as an integer.
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidInteger with the supplied errorList to capture ValidationExceptions
+	 */
+	Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidDouble and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidDouble and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a validated real number as a double. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     * @param minValue
+     * 		Lowest legal value for input.
+     * @param maxValue
+     * 		Highest legal value for input.
+     *
+     * @return A validated real number as a double.
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidDouble with the supplied errorList to capture ValidationExceptions
+	 */
+	Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileContent and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileContent and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns validated file content as a byte array. This is a good place to check for max file size, allowed character sets, and do virus scans.  Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual input data to validate.
+	 * @param maxBytes
+	 * 		The maximum number of bytes allowed in a legal file.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A byte array containing valid file content.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidFileContent with the supplied errorList to capture ValidationExceptions
+	 */
+	byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileUpload and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileUpload and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Validates the filepath, filename, and content of a file. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param filepath
+	 * 		The file path of the uploaded file.
+	 * @param filename
+	 * 		The filename of the uploaded file
+	 * @param content
+	 * 		A byte array containing the content of the uploaded file.
+	 * @param maxBytes
+	 * 		The max number of bytes allowed for a legal file upload.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidFileUpload with the supplied errorList to capture ValidationExceptions
+	 */
+	void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidListItem and returns true if no exceptions are thrown.
+	 */
+	boolean isValidListItem(String context, String input, List<String> list) throws IntrusionException;
+
+	/**
+	 * Calls getValidListItem and returns true if no exceptions are thrown.
+	 */
+	boolean isValidListItem(String context, String input, List<String> list, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns the list item that exactly matches the canonicalized input. Invalid or non-matching input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The value to search 'list' for.
+	 * @param list
+	 * 		The list to search for 'input'.
+	 *
+	 * @return The list item that exactly matches the canonicalized input.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidListItem(String context, String input, List<String> list) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidListItem with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidListItem(String context, String input, List<String> list, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls assertValidHTTPRequestParameterSet and returns true if no exceptions are thrown.
+	 */
+	boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional) throws IntrusionException;
+
+	/**
+	 * Calls assertValidHTTPRequestParameterSet and returns true if no exceptions are thrown.
+	 */
+	boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Validates that the parameters in the current request contain all required parameters and only optional ones in
+	 * addition. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param required
+	 * 		parameters that are required to be in HTTP request
+	 * @param optional
+	 * 		additional parameters that may be in HTTP request
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidHTTPRequestParameterSet with the supplied errorList to capture ValidationExceptions
+	 */
+	void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws IntrusionException;
+
+        /**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated printable characters as a byte array. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 *  @param context
+	 *  		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 *  @param input
+	 *  		data to be returned as valid and printable
+	 *  @param maxLength
+	 *  		Maximum number of bytes stored in 'input'
+	 *  @param allowNull
+	 *  		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 *  @return a byte array containing only printable characters, made up of data from 'input'
+	 *
+	 *  @throws ValidationException
+	 */
+	char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws ValidationException;
+
+	/**
+	 * Calls getValidPrintable with the supplied errorList to capture ValidationExceptions
+	 */
+	char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+
+	/**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull) throws IntrusionException;
+
+        /**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated printable characters as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 *  @param context
+	 *  		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 *  @param input
+	 *  		data to be returned as valid and printable
+	 *  @param maxLength
+	 *  		Maximum number of bytes stored in 'input' after canonicalization
+	 *  @param allowNull
+	 *  		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 *  @return a String containing only printable characters, made up of data from 'input'
+	 *
+	 *  @throws ValidationException
+	 */
+	String getValidPrintable(String context, String input, int maxLength, boolean allowNull) throws ValidationException;
+
+	/**
+	 * Calls getValidPrintable with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidRedirectLocation and returns true if no exceptions are thrown.
+	 */
+	boolean isValidRedirectLocation(String context, String input, boolean allowNull);
+
+        /**
+	 * Calls getValidRedirectLocation and returns true if no exceptions are thrown.
+	 */
+	boolean isValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errorList);
+
+	/**
+	 * Returns a canonicalized and validated redirect location as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 *  @param context
+	 *  		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 *  @param input
+	 *  		redirect location to be returned as valid, according to encoding rules set in "ESAPI.properties"
+	 *  @param allowNull
+	 *  		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 *  @return A canonicalized and validated redirect location, as defined in "ESAPI.properties"
+	 *
+	 *  @throws ValidationException
+	 *  @throws IntrusionException
+	 */
+	String getValidRedirectLocation(String context, String input, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidRedirectLocation with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Reads from an input stream until end-of-line or a maximum number of
+	 * characters. This method protects against the inherent denial of service
+	 * attack in reading until the end of a line. If an attacker doesn't ever
+	 * send a newline character, then a normal input stream reader will read
+	 * until all memory is exhausted and the platform throws an OutOfMemoryError
+	 * and probably terminates.
+	 *
+	 * @param inputStream
+	 * 		The InputStream from which to read data
+	 * @param maxLength
+	 * 		Maximum characters allowed to be read in per line
+	 *
+	 * @return a String containing the current line of inputStream
+	 *
+	 * @throws ValidationException
+	 */
+	String safeReadLine(InputStream inputStream, int maxLength) throws ValidationException;
+
+}
+
diff --git a/src/main/java/org/owasp/esapi/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..cb23a58
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/.svn/text-base/package.html.svn-base
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+The ESAPI interfaces and {@code Exception} classes model the most
+important security functions to enterprise web applications.
+The interfaces in this package are intended to be extended and
+customized within an enterprise to match their custom data,
+security services, and application environment. A reference
+implementation of this interface is provided as an example of how this
+library can be implemented successfully, but is useful in many ways
+by itself as well.
+<p>
+OWASP ESAPI interfaces and reference implementation provides
+enterprise web application developers with the most important
+security functions they need in ordre to build secure web applications
+and web services that stand up to most common-day web-based attacks.
+</p>
+<h2>Sponsor</h2>
+<p>The <a href="http://www.owasp.org">The Open Web Application
+Security Project (OWASP)</a> is a worldwide free and open community focused
+on improving the security of application software. Our mission is to
+make application security "visible," so that people and organizations
+can make informed decisions about application security risks. Everyone
+is free to participate in OWASP and all of our materials are available
+under an open source license. The OWASP Foundation is a 501c3
+not-for-profit charitable organization that ensures the ongoing
+availability and support for our work.</p> 
+ 
+<p>The
+<a href="http://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API">
+OWASP ESAPI Project</a>
+is led by Jeff Williams,
+<a href="http://www.aspectsecurity.com">Aspect Security</a>.
+ 
+<p>You can find more information about the ESAPI Java project, or join
+the mailing list and help us make it better from the OWASP project page
+at <a
+href="http://www.owasp.org/index.php/ESAPI#tab=Java_EE">http://www.owasp.org/index.php/ESAPI#tab=Java_EE</a>.</p> 
+ 
+<h2>ESAPI Architecture</h2>
+ 
+<p>The ESAPI class library builds on the excellent security libraries available,
+such as Java Logging, JCE, and Adobe Commons FileUpload. It uses the
+concepts from many of the security packages out there, such as ACEGI,
+Apache Commons Validator, Microsoft's AntiXSS library, and many many
+more. This library provides a single consistent interface to security
+functions that is intuitive for enterprise developers.</p> 
+
+<img src="doc-files/Architecture.jpg"> 
+
+<h2>Addressing OWASP Top Ten</h2>
+ 
+<p>Used properly, the ESAPI provides enough functions to protect
+against most of the
+<a href="http://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project">
+OWASP Top Ten</a>.  The only real exception is the
+Insecure Communications category, which is generally outside the control
+of the software developer.</p> 
+<img src="doc-files/OWASPTopTen.jpg"> 
+
+<h2>Copyright and License</h2>
+ 
+<p>This project and all associated code is Copyright (c) 2007 - The OWASP Foundation</p> 
+ 
+<p>This project licensed under the <a href="http://en.wikipedia.org/wiki/BSD_license">BSD license</a>, 
+which is very permissive and about as close to public domain as is possible. You can use or modify 
+ESAPI however you want, even include it in commercial products.</p>
+
+<h2>References</h2>
+
+This library builds on some of the ideas found in:
+<ul>
+  <li><a href="http://www.owasp.org/">OWASP</a></li>
+  <li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=051ee83c-5ccf-48ed-8463-02f56a6bfc09&displaylang=en">Microsoft's AntiXSS</a></li>
+  <li><a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/JCERefGuide.html">Java JCE</a></li>
+  <li><a href="http://java.sun.com/j2se/1.5.0/docs/guide/logging/overview.html">Java Logging API</a></li>
+  <li><a href="http://commons.apache.org/fileupload/">Apache Commons FileUpload</a></li>
+</ul>
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/AccessControlRule.java b/src/main/java/org/owasp/esapi/AccessControlRule.java
new file mode 100644
index 0000000..becdb99
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/AccessControlRule.java
@@ -0,0 +1,8 @@
+package org.owasp.esapi;
+
+
+public interface AccessControlRule<P, R> {
+	public void setPolicyParameters(P policyParameter);
+	public P getPolicyParameters();
+	public boolean isAuthorized(R runtimeParameter) throws Exception;
+}
diff --git a/src/main/java/org/owasp/esapi/AccessController.java b/src/main/java/org/owasp/esapi/AccessController.java
new file mode 100644
index 0000000..e6269e1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/AccessController.java
@@ -0,0 +1,351 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AccessControlException;
+
+
+
+/**
+ * The AccessController interface defines a set of methods that can be used in a wide variety of applications to
+ * enforce access control. In most applications, access control must be performed in multiple different locations across
+ * the various application layers. This class provides access control for URLs, business functions, data, services, and
+ * files.
+ * <P>
+ * The implementation of this interface will need to access the current User object (from Authenticator.getCurrentUser())
+ * to determine roles or permissions. In addition, the implementation
+ * will also need information about the resources that are being accessed. Using the user information and the resource
+ * information, the implementation should return an access control decision. 
+ * <P>
+ * Implementers are encouraged to implement the ESAPI access control rules, like assertAuthorizedForFunction() using 
+ * existing access control mechanisms, such as methods like isUserInRole() or hasPrivilege(). While powerful, 
+ * methods like isUserInRole() can be confusing for developers, as users may be in multiple roles or possess multiple 
+ * overlapping privileges. Direct use of these finer grained access control methods encourages the use of complex boolean 
+ * tests throughout the code, which can easily lead to developer mistakes.
+ * <P>
+ * The point of the ESAPI access control interface is to centralize access control logic behind easy to use calls like 
+ * assertAuthorized() so that access control is easy to use and easy to verify. Here is an example of a very 
+ * straightforward to implement, understand, and verify ESAPI access control check: 
+ * 
+ * <pre>
+ * try {
+ *     ESAPI.accessController().assertAuthorized("businessFunction", runtimeData);
+ *     // execute BUSINESS_FUNCTION
+ * } catch (AccessControlException ace) {
+ * ... attack in progress
+ * }
+ * </pre>
+ * 
+ * Note that in the user interface layer, access control checks can be used to control whether particular controls are
+ * rendered or not. These checks are supposed to fail when an unauthorized user is logged in, and do not represent
+ * attacks. Remember that regardless of how the user interface appears, an attacker can attempt to invoke any business
+ * function or access any data in your application. Therefore, access control checks in the user interface should be
+ * repeated in both the business logic and data layers.
+ * 
+ * <pre>
+ * <% if ( ESAPI.accessController().isAuthorized( "businessFunction", runtimeData ) ) { %>
+ * <a href="/doAdminFunction">ADMIN</a>
+ * <% } else { %>
+ * <a href="/doNormalFunction">NORMAL</a>
+ * <% } %>
+ * </pre>
+ * 
+ * @author Mike H. Fauzy (mike.fauzy at aspectsecurity.com) ESAPI v1.6-
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com) ESAPI v0-1.5
+ */
+public interface AccessController {
+
+	/**
+	 * <code>isAuthorized</code> executes the <code>AccessControlRule</code> 
+	 * that is identified by <code>key</code> and listed in the 
+	 * <code>resources/ESAPI-AccessControlPolicy.xml</code> file. It returns 
+	 * true if the <code>AccessControlRule</code> decides that the operation 
+	 * should be allowed. Otherwise, it returns false. Any exception thrown by 
+	 * the <code>AccessControlRule</code> must result in false. If 
+	 * <code>key</code> does not map to an <code>AccessControlRule</code>, then 
+	 * false is returned. 
+	 *  
+	 * Developers should call isAuthorized to control execution flow. For 
+	 * example, if you want to decide whether to display a UI widget in the 
+	 * browser using the same logic that you will use to enforce permissions
+	 * on the server, then isAuthorized is the method that you want to use.
+	 * 
+	 * Typically, assertAuthorized should be used to enforce permissions on the 
+	 * server.
+	 *  
+	 * @param key <code>key</code> maps to 
+	 * <code><AccessControlPolicy><AccessControlRules>
+	 *     <AccessControlRule name="key"</code>
+	 * @param runtimeParameter runtimeParameter can contain anything that 
+	 *        the AccessControlRule needs from the runtime system. 
+	 * @return Returns <code>true</code> if and only if the AccessControlRule specified 
+	 *        by <code>key</code> exists and returned <code>true</code>. 
+	 *        Otherwise returns <code>false</code> 
+	 */
+	public boolean isAuthorized(Object key, Object runtimeParameter);
+	
+	/**
+	 * <code>assertAuthorized</code> executes the <code>AccessControlRule</code> 
+	 * that is identified by <code>key</code> and listed in the 
+	 * <code>resources/ESAPI-AccessControlPolicy.xml</code> file. It does 
+	 * nothing if the <code>AccessControlRule</code> decides that the operation 
+	 * should be allowed. Otherwise, it throws an 
+	 * <code>org.owasp.esapi.errors.AccessControlException</code>. Any exception
+	 * thrown by the <code>AccessControlRule</code> will also result in an 
+	 * <code>AccesControlException</code>. If <code>key</code> does not map to 
+	 * an <code>AccessControlRule</code>, then an <code>AccessControlException
+	 * </code> is thrown.  
+	 *  
+	 * Developers should call {@code assertAuthorized} to enforce privileged access to 
+	 * the system. It should be used to answer the question: "Should execution 
+	 * continue." Ideally, the call to <code>assertAuthorized</code> should
+	 * be integrated into the application framework so that it is called 
+	 * automatically. 
+	 *  
+	 * @param key <code>key</code> maps to 
+	 * <AccessControlPolicy><AccessControlRules>
+	 *     <AccessControlRule name="key"
+	 * @param runtimeParameter runtimeParameter can contain anything that 
+	 *        the AccessControlRule needs from the runtime system.
+	 */
+	public void assertAuthorized(Object key, Object runtimeParameter)
+		throws AccessControlException;
+
+		
+
+	
+	/*** Below this line has been deprecated as of ESAPI 1.6 ***/ 
+	
+	
+	
+	
+    /**
+     * Checks if the current user is authorized to access the referenced URL. Generally, this method should be invoked in the
+     * application's controller or a filter as follows:
+     * <PRE>ESAPI.accessController().isAuthorizedForURL(request.getRequestURI().toString());</PRE>
+     * 
+     * The implementation of this method should call assertAuthorizedForURL(String url), and if an AccessControlException is 
+     * not thrown, this method should return true. This way, if the user is not authorized, false would be returned, and the 
+     * exception would be logged.
+     * 
+     * @param url 
+     * 		the URL as returned by request.getRequestURI().toString()
+     * 
+     * @return 
+     * 		true, if is authorized for URL
+     */
+    boolean isAuthorizedForURL(String url);
+
+    /**
+     * Checks if the current user is authorized to access the referenced function. 
+     * 
+     * The implementation of this method should call assertAuthorizedForFunction(String functionName), and if an 
+     * AccessControlException is not thrown, this method should return true.
+     * 
+     * @param functionName 
+     * 		the name of the function
+     * 
+     * @return 
+     * 		true, if is authorized for function
+     */
+    boolean isAuthorizedForFunction(String functionName);
+
+
+    /**
+     * Checks if the current user is authorized to access the referenced data, represented as an Object. 
+     * 
+     * The implementation of this method should call assertAuthorizedForData(String action, Object data), and if an 
+     * AccessControlException is not thrown, this method should return true.
+     * 
+     * @param action
+     *      The action to verify for an access control decision, such as a role, or an action being performed on the object 
+     *      (e.g., Read, Write, etc.), or the name of the function the data is being passed to.
+     * 
+     * @param data
+     * 		The actual object or object identifier being accessed or a reference to the object being accessed.
+     * 
+     * @return 
+     * 		true, if is authorized for the data
+     */
+    boolean isAuthorizedForData(String action, Object data);
+    
+    /**
+     * Checks if the current user is authorized to access the referenced file. 
+     * 
+     * The implementation of this method should call assertAuthorizedForFile(String filepath), and if an AccessControlException 
+     * is not thrown, this method should return true.
+     *   
+     * @param filepath 
+     * 		the path of the file to be checked, including filename
+     * 
+     * @return 
+     * 		true, if is authorized for the file
+     */
+    boolean isAuthorizedForFile(String filepath);
+
+    /**
+     * Checks if the current user is authorized to access the referenced service. This can be used in applications that
+     * provide access to a variety of back end services.
+     * 
+     * The implementation of this method should call assertAuthorizedForService(String serviceName), and if an 
+     * AccessControlException is not thrown, this method should return true.
+     * 
+     * @param serviceName 
+     * 		the service name
+     * 
+     * @return 
+     * 		true, if is authorized for the service
+     */
+    boolean isAuthorizedForService(String serviceName);
+
+    /**
+     * Checks if the current user is authorized to access the referenced URL. The implementation should allow
+     * access to be granted to any part of the URL. Generally, this method should be invoked in the
+     * application's controller or a filter as follows:
+     * <PRE>ESAPI.accessController().assertAuthorizedForURL(request.getRequestURI().toString());</PRE> 
+     * 
+     * This method throws an AccessControlException if access is not authorized, or if the referenced URL does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the resource exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * @param url 
+     * 		the URL as returned by request.getRequestURI().toString()
+     * 
+     * @throws AccessControlException 
+     * 		if access is not permitted
+     */
+    void assertAuthorizedForURL(String url) throws AccessControlException;
+    
+    /**
+     * Checks if the current user is authorized to access the referenced function. The implementation should define the
+     * function "namespace" to be enforced. Choosing something simple like the class name of action classes or menu item
+     * names will make this implementation easier to use.
+     * <P>
+     * This method throws an AccessControlException if access is not authorized, or if the referenced function does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the function exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param functionName 
+     * 		the function name
+     * 
+     * @throws AccessControlException 
+     * 		if access is not permitted
+     */
+    void assertAuthorizedForFunction(String functionName) throws AccessControlException;
+    
+
+    /**
+     * Checks if the current user is authorized to access the referenced data.  This method simply returns if access is authorized.  
+     * It throws an AccessControlException if access is not authorized, or if the referenced data does not exist.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the resource exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param action
+     *      The action to verify for an access control decision, such as a role, or an action being performed on the object 
+     *      (e.g., Read, Write, etc.), or the name of the function the data is being passed to.
+     * 
+     * @param data
+     * 		The actual object or object identifier being accessed or a reference to the object being accessed.
+     * 
+     * @throws AccessControlException 
+     * 		if access is not permitted
+     */
+    void assertAuthorizedForData(String action, Object data) throws AccessControlException;
+   
+    /**
+     * Checks if the current user is authorized to access the referenced file. The implementation should validate and canonicalize the 
+     * input to be sure the filepath is not malicious.
+     * <P>
+     * This method throws an AccessControlException if access is not authorized, or if the referenced File does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the File exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param filepath
+     * 			Path to the file to be checked
+     * @throws AccessControlException if access is denied
+     */
+    void assertAuthorizedForFile(String filepath) throws AccessControlException;
+    
+    /**
+     * Checks if the current user is authorized to access the referenced service. This can be used in applications that
+     * provide access to a variety of backend services.
+     * <P>
+     * This method throws an AccessControlException if access is not authorized, or if the referenced service does not exist.
+     * If the User is authorized, this method simply returns.
+     * <P>
+     * Specification:  The implementation should do the following:
+     * <ol>
+     * <li>Check to see if the service exists and if not, throw an AccessControlException</li>
+     * <li>Use available information to make an access control decision</li>
+     *      <ol type="a">
+     *      <li>Ideally, this policy would be data driven</li>
+     * 		<li>You can use the current User, roles, data type, data name, time of day, etc.</li>
+     *  	<li>Access control decisions must deny by default</li>
+     *      </ol>
+     * <li>If access is not permitted, throw an AccessControlException with details</li>
+     * </ol> 
+     * 
+     * @param serviceName 
+     * 		the service name
+     * 
+     * @throws AccessControlException
+     * 		if access is not permitted
+     */				
+    void assertAuthorizedForService(String serviceName) throws AccessControlException;
+    
+}
diff --git a/src/main/java/org/owasp/esapi/AccessReferenceMap.java b/src/main/java/org/owasp/esapi/AccessReferenceMap.java
new file mode 100644
index 0000000..a20338e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/AccessReferenceMap.java
@@ -0,0 +1,176 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AccessControlException;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * The AccessReferenceMap interface is used to map from a set of internal
+ * direct object references to a set of indirect references that are safe to
+ * disclose publicly. This can be used to help protect database keys,
+ * filenames, and other types of direct object references. As a rule, developers
+ * should not expose their direct object references as it enables attackers to
+ * attempt to manipulate them.
+ * <P>
+ * Indirect references are handled as strings, to facilitate their use in HTML.
+ * Implementations can generate simple integers or more complicated random
+ * character strings as indirect references. Implementations should probably add
+ * a constructor that takes a list of direct references.
+ * <P>
+ * Note that in addition to defeating all forms of parameter tampering attacks,
+ * there is a side benefit of the AccessReferenceMap. Using random strings as indirect object
+ * references, as opposed to simple integers makes it impossible for an attacker to
+ * guess valid identifiers. So if per-user AccessReferenceMaps are used, then request
+ * forgery (CSRF) attacks will also be prevented.
+ * 
+ * <pre>
+ * Set fileSet = new HashSet();
+ * fileSet.addAll(...); // add direct references (e.g. File objects)
+ * AccessReferenceMap map = new AccessReferenceMap( fileSet );
+ * // store the map somewhere safe - like the session!
+ * String indRef = map.getIndirectReference( file1 );
+ * String href = "http://www.aspectsecurity.com/esapi?file=" + indRef );
+ * ...
+ * // if the indirect reference doesn't exist, it's likely an attack
+ * // getDirectReference throws an AccessControlException
+ * // you should handle as appropriate
+ * String indref = request.getParameter( "file" );
+ * File file = (File)map.getDirectReference( indref );
+ * </pre>
+ * 
+ * <P>
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author Chris Schmidt (chrisisbeef at gmail.com)
+ */
+public interface AccessReferenceMap<K> extends Serializable {
+
+	/**
+	 * Get an iterator through the direct object references. No guarantee is made as 
+	 * to the order of items returned.
+	 * 
+	 * @return the iterator
+	 */
+	Iterator iterator();
+
+	/**
+	 * Get a safe indirect reference to use in place of a potentially sensitive
+	 * direct object reference. Developers should use this call when building
+	 * URL's, form fields, hidden fields, etc... to help protect their private
+	 * implementation information.
+	 * 
+	 * @param directReference
+	 * 		the direct reference
+	 * 
+	 * @return 
+	 * 		the indirect reference
+	 */
+	<T> K getIndirectReference(T directReference);
+
+	/**
+	 * Get the original direct object reference from an indirect reference.
+	 * Developers should use this when they get an indirect reference from a
+	 * request to translate it back into the real direct reference. If an
+	 * invalid indirect reference is requested, then an AccessControlException is
+	 * thrown.
+    *
+    * If a type is implied the requested object will be cast to that type, if the
+    * object is not of the requested type, a AccessControlException will be thrown to
+    * the caller.
+    *
+    * For example:
+    * <pre>
+    * UserProfile profile = arm.getDirectReference( indirectRef );
+    * </pre>
+    *
+    * Will throw a AccessControlException if the object stored in memory is not of type
+    * UserProfile.
+    *
+    * However,
+    * <pre>
+    * Object uncastObject = arm.getDirectReference( indirectRef );
+    * </pre>
+    *
+    * Will never throw a AccessControlException as long as the object exists. If you are
+    * unsure of the object type of that an indirect reference references you should get
+    * the uncast object and test for type in the calling code.
+    * <pre>
+    * Object uncastProfile = arm.getDirectReference( indirectRef );
+    * if ( uncastProfile instanceof UserProfile ) {
+    *     UserProfile userProfile = (UserProfile) uncastProfile;
+    *     // ...
+    * } else {
+    *     EmployeeProfile employeeProfile = (EmployeeProfile) uncastProfile;
+    *     // ...
+    * }
+    * </pre>
+	 * 
+	 * @param indirectReference
+	 * 		the indirect reference
+	 * 
+	 * @return 
+	 * 		the direct reference
+	 * 
+	 * @throws AccessControlException 
+	 * 		if no direct reference exists for the specified indirect reference
+    * @throws ClassCastException
+    *       if the implied type is not the same as the referenced object type
+	 */
+	<T> T getDirectReference(K indirectReference) throws AccessControlException;
+
+	/**
+	 * Adds a direct reference to the AccessReferenceMap, then generates and returns 
+	 * an associated indirect reference.
+	 *  
+	 * @param direct 
+	 * 		the direct reference
+	 * 
+	 * @return 
+	 * 		the corresponding indirect reference
+	 */
+	<T> K addDirectReference(T direct);
+	
+	/**
+	 * Removes a direct reference and its associated indirect reference from the AccessReferenceMap.
+	 * 
+	 * @param direct 
+	 * 		the direct reference to remove
+	 * 
+	 * @return 
+	 * 		the corresponding indirect reference
+	 * 
+	 * @throws AccessControlException
+    *          if the reference does not exist.
+	 */
+	<T> K removeDirectReference(T direct) throws AccessControlException;
+
+	/**
+	 * Updates the access reference map with a new set of direct references, maintaining
+	 * any existing indirect references associated with items that are in the new list.
+	 * New indirect references could be generated every time, but that
+	 * might mess up anything that previously used an indirect reference, such
+	 * as a URL parameter. 
+	 * 
+	 * @param directReferences
+	 * 		a Set of direct references to add
+	 */
+	void update(Set directReferences);
+}
diff --git a/src/main/java/org/owasp/esapi/Authenticator.java b/src/main/java/org/owasp/esapi/Authenticator.java
new file mode 100644
index 0000000..9cb4013
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/Authenticator.java
@@ -0,0 +1,324 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Set;
+
+
+/**
+ * The Authenticator interface defines a set of methods for generating and
+ * handling account credentials and session identifiers. The goal of this
+ * interface is to encourage developers to protect credentials from disclosure
+ * to the maximum extent possible.
+ * <P>
+ * One possible implementation relies on the use of a thread local variable to
+ * store the current user's identity. The application is responsible for calling
+ * setCurrentUser() as soon as possible after each HTTP request is received. The
+ * value of getCurrentUser() is used in several other places in this API. This
+ * eliminates the need to pass a user object to methods throughout the library.
+ * For example, all of the logging, access control, and exception calls need
+ * access to the currently logged in user.
+ * <P>
+ * The goal is to minimize the responsibility of the developer for
+ * authentication. In this example, the user simply calls authenticate with the
+ * current request and the name of the parameters containing the username and
+ * password. The implementation should verify the password if necessary, create
+ * a session if necessary, and set the user as the current user.
+ * 
+ * <pre>
+ * public void doPost(ServletRequest request, ServletResponse response) {
+ * try {
+ * User user = ESAPI.authenticator().login(request, response);
+ * // continue with authenticated user
+ * } catch (AuthenticationException e) {
+ * // handle failed authentication (it's already been logged)
+ * }
+ * </pre>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Authenticator {
+
+	/**
+	 * Clears the current User. This allows the thread to be reused safely.
+     * 
+     * This clears all threadlocal variables from the thread. This should ONLY be called after
+     * all possible ESAPI operations have concluded. If you clear too early, many calls will
+     * fail, including logging, which requires the user identity.  
+	 */
+	void clearCurrent();
+
+	/**
+	 * Calls login with the *current* request and response.
+	 * @return Authenticated {@code User} if login is successful.
+	 * @see HTTPUtilities#setCurrentHTTP(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+	 */
+	User login() throws AuthenticationException;
+	
+	/**
+	 * This method should be called for every HTTP request, to login the current user either from the session of HTTP
+     * request. This method will set the current user so that getCurrentUser() will work properly. 
+	 * 
+	 * Authenticates the user's credentials from the HttpServletRequest if
+	 * necessary, creates a session if necessary, and sets the user as the
+	 * current user.
+	 * 
+	 * Specification:  The implementation should do the following:
+     * 	1) Check if the User is already stored in the session
+     * 		a. If so, check that session absolute and inactivity timeout have not expired
+     * 		b. Step 2 may not be required if 1a has been satisfied
+     * 	2) Verify User credentials
+     * 		a. It is recommended that you use 
+     * 			loginWithUsernameAndPassword(HttpServletRequest, HttpServletResponse) to verify credentials
+     * 	3) Set the last host of the User (ex.  user.setLastHostAddress(address) )
+     * 	4) Verify that the request is secure (ex. over SSL)
+     * 	5) Verify the User account is allowed to be logged in
+     * 		a. Verify the User is not disabled, expired or locked
+     * 	6) Assign User to session variable      	
+	 * 
+	 * @param request
+	 *            the current HTTP request
+	 * @param response
+	 *            the HTTP response
+	 * 
+	 * @return 
+	 * 		the User
+	 * 
+	 * @throws AuthenticationException
+	 *             if the credentials are not verified, or if the account is disabled, locked, expired, or timed out
+	 */
+	User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException;
+
+	/**
+	 * Verify that the supplied password matches the password for this user. Password should
+	 * be stored as a hash. It is recommended you use the hashPassword(password, accountName) method
+	 * in this class.
+	 * This method is typically used for "reauthentication" for the most sensitive functions, such
+	 * as transactions, changing email address, and changing other account information.
+	 * 
+	 * @param user 
+	 * 		the user who requires verification
+	 * @param password 
+	 * 		the hashed user-supplied password
+	 * 
+	 * @return 
+	 * 		true, if the password is correct for the specified user
+	 */
+	boolean verifyPassword(User user, String password);
+	
+	/**
+	 * Logs out the current user.
+	 * 
+	 * This is usually done by calling User.logout on the current User. 
+	 */
+    void logout();
+
+	/**
+	 * Creates a new User with the information provided. Implementations should check
+	 * accountName and password for proper format and strength against brute force 
+	 * attacks ( verifyAccountNameStrength(String), verifyPasswordStrength(String, String)  ).
+	 * 
+	 * Two copies of the new password are required to encourage user interface designers to
+	 * include a "re-type password" field in their forms. Implementations should verify that 
+	 * both are the same. 
+	 * 
+	 * @param accountName 
+	 * 		the account name of the new user
+	 * @param password1 
+	 * 		the password of the new user
+	 * @param password2 
+	 * 		the password of the new user.  This field is to encourage user interface designers to include two password fields in their forms.
+	 * 
+	 * @return 
+	 * 		the User that has been created 
+	 * 
+	 * @throws AuthenticationException 
+	 * 		if user creation fails due to any of the qualifications listed in this method's description
+	 */
+	User createUser(String accountName, String password1, String password2) throws AuthenticationException;
+
+	/**
+	 * Generate a strong password. Implementations should use a large character set that does not
+	 * include confusing characters, such as i I 1 l 0 o and O.  There are many algorithms to
+	 * generate strong memorable passwords that have been studied in the past.
+	 * 
+	 * @return 
+	 * 		a password with strong password strength
+	 */
+	String generateStrongPassword();
+
+	/**
+	 * Generate strong password that takes into account the user's information and old password. Implementations
+	 * should verify that the new password does not include information such as the username, fragments of the
+	 * old password, and other information that could be used to weaken the strength of the password.
+	 * 
+	 * @param user 
+	 * 		the user whose information to use when generating password
+	 * @param oldPassword 
+	 * 		the old password to use when verifying strength of new password.  The new password may be checked for fragments of oldPassword.
+	 * 
+	 * @return 
+	 * 		a password with strong password strength
+	 */
+	String generateStrongPassword(User user, String oldPassword);
+
+	/**
+	 * Changes the password for the specified user. This requires the current password, as well as 
+	 * the password to replace it with. The new password should be checked against old hashes to be sure the new password does not closely resemble or equal any recent passwords for that User.
+	 * Password strength should also be verified.  This new password must be repeated to ensure that the user has typed it in correctly.
+	 * 
+	 * @param user 
+	 * 		the user to change the password for
+	 * @param currentPassword 
+	 * 		the current password for the specified user
+	 * @param newPassword 
+	 * 		the new password to use
+	 * @param newPassword2 
+	 * 		a verification copy of the new password
+	 * 
+	 * @throws AuthenticationException 
+	 * 		if any errors occur
+	 */
+	void changePassword(User user, String currentPassword, String newPassword, String newPassword2) throws AuthenticationException;
+	
+	/**
+	 * Returns the User matching the provided accountId.  If the accoundId is not found, an Anonymous
+	 * User or null may be returned.
+	 * 
+	 * @param accountId
+	 *            the account id
+	 * 
+	 * @return 
+	 * 		the matching User object, or the Anonymous User if no match exists
+	 */
+	User getUser(long accountId);
+		
+	/**
+	 * Returns the User matching the provided accountName.  If the accoundId is not found, an Anonymous
+	 * User or null may be returned.
+	 * 
+	 * @param accountName
+	 *            the account name
+	 * 
+	 * @return 
+	 * 		the matching User object, or the Anonymous User if no match exists
+	 */
+	User getUser(String accountName);
+
+	/**
+	 * Gets a collection containing all the existing user names.
+	 * 
+	 * @return 
+	 * 		a set of all user names
+	 */
+	Set getUserNames();
+
+	/**
+	 * Returns the currently logged in User.
+	 * 
+	 * @return 
+	 * 		the matching User object, or the Anonymous User if no match
+	 *         exists
+	 */
+	User getCurrentUser();
+
+	/**
+	 * Sets the currently logged in User.
+	 * 
+	 * @param user
+	 *          the user to set as the current user
+	 */
+	void setCurrentUser(User user);
+
+	/**
+	 * Returns a string representation of the hashed password, using the
+	 * accountName as the salt. The salt helps to prevent against "rainbow"
+	 * table attacks where the attacker pre-calculates hashes for known strings.
+	 * This method specifies the use of the user's account name as the "salt"
+	 * value. The Encryptor.hash method can be used if a different salt is
+	 * required.
+	 * 
+	 * @param password
+	 *            the password to hash
+	 * @param accountName
+	 *            the account name to use as the salt
+	 * 
+	 * @return 
+     * 		the hashed password
+     * @throws EncryptionException
+	 */
+	String hashPassword(String password, String accountName) throws EncryptionException;
+
+	/**
+	 * Removes the account of the specified accountName.
+	 * 
+	 * @param accountName
+	 *            the account name to remove
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception if user does not exist
+	 */
+	void removeUser(String accountName) throws AuthenticationException;
+
+	/**
+	 * Ensures that the account name passes site-specific complexity requirements, like minimum length.
+	 * 
+	 * @param accountName
+	 *            the account name
+	 * 
+	 * @throws AuthenticationException
+	 *             if account name does not meet complexity requirements
+	 */
+	void verifyAccountNameStrength(String accountName) throws AuthenticationException;
+
+	/**
+	 * Ensures that the password meets site-specific complexity requirements, like length or number 
+	 * of character sets. This method takes the old password so that the algorithm can analyze the 
+	 * new password to see if it is too similar to the old password. Note that this has to be
+	 * invoked when the user has entered the old password, as the list of old
+	 * credentials stored by ESAPI is all hashed.
+	 * Additionally, the user object is taken in order to verify the password and account name differ.
+	 * 
+	 * @param oldPassword
+	 *            the old password
+	 * @param newPassword
+	 *            the new password
+	 * @param user
+	 * 			  the user
+	 * 
+	 * @throws AuthenticationException
+	 *				if newPassword is too similar to oldPassword or if newPassword does not meet complexity requirements
+	 */
+	void verifyPasswordStrength(String oldPassword, String newPassword, User user) throws AuthenticationException;
+
+	/**
+	 * Determine if the account exists.
+	 * 
+	 * @param accountName
+	 *            the account name
+	 * 
+	 * @return true, if the account exists
+	 */
+	boolean exists(String accountName);
+
+}
diff --git a/src/main/java/org/owasp/esapi/ESAPI.java b/src/main/java/org/owasp/esapi/ESAPI.java
new file mode 100644
index 0000000..de93a73
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/ESAPI.java
@@ -0,0 +1,224 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2008 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Mike Fauzy <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Rogan Dawes <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2008
+ */
+package org.owasp.esapi;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.util.ObjFactory;
+
+/**
+ * ESAPI locator class is provided to make it easy to gain access to the current ESAPI classes in use.
+ * Use the set methods to override the reference implementations with instances of any custom ESAPI implementations.
+ */
+public final class ESAPI {
+	private static String securityConfigurationImplName = System.getProperty("org.owasp.esapi.SecurityConfiguration", "org.owasp.esapi.reference.DefaultSecurityConfiguration");
+
+	/**
+	 * prevent instantiation of this class
+	 */
+	private ESAPI() {
+	}
+	
+    /**
+	 * Clears the current User, HttpRequest, and HttpResponse associated with the current thread. This method
+	 * MUST be called as some containers do not properly clear threadlocal variables when the execution of
+	 * a thread is complete. The suggested approach is to put this call in a finally block inside a filter.
+	 * <pre>
+		public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException {
+			try {
+				HttpServletRequest request = (HttpServletRequest) req;
+				HttpServletResponse response = (HttpServletResponse) resp;
+				ESAPI.httpUtilities().setCurrentHTTP(request, response);
+				ESAPI.authenticator().login();
+				chain.doFilter(request, response);
+			} catch (Exception e) {
+				logger.error( Logger.SECURITY_FAILURE, "Error in ESAPI security filter: " + e.getMessage(), e );
+			} finally {
+				// VERY IMPORTANT
+				// clear out ThreadLocal variables
+				ESAPI.clearCurrent();
+			}
+		}
+	 * </pre>
+	 * The advantages of having identity everywhere are worth the risk here.
+	 */
+	public static void clearCurrent() {
+		authenticator().clearCurrent();
+		httpUtilities().clearCurrent();
+	}
+
+	/**
+	 * Get the current HTTP Servlet Request being processed.
+	 * @return the current HTTP Servlet Request.
+	 */
+	public static HttpServletRequest currentRequest() {
+		return httpUtilities().getCurrentRequest();
+	}
+	
+	/**
+	 * Get the current HTTP Servlet Response being generated.
+	 * @return the current HTTP Servlet Response.
+	 */
+	public static HttpServletResponse currentResponse() {
+		return httpUtilities().getCurrentResponse();
+	}
+	
+	/**
+	 * @return the current ESAPI AccessController object being used to maintain the access control rules for this application. 
+	 */
+	public static AccessController accessController() {
+        return ObjFactory.make( securityConfiguration().getAccessControlImplementation(), "AccessController" );
+	}
+
+	/**
+	 * @return the current ESAPI Authenticator object being used to authenticate users for this application. 
+	 */
+	public static Authenticator authenticator() {
+        return ObjFactory.make( securityConfiguration().getAuthenticationImplementation(), "Authenticator" );
+	}
+
+	/**
+	 * @return the current ESAPI Encoder object being used to encode and decode data for this application. 
+	 */
+	public static Encoder encoder() {
+        return ObjFactory.make( securityConfiguration().getEncoderImplementation(), "Encoder" );
+	}
+
+	/**
+	 * @return the current ESAPI Encryptor object being used to encrypt and decrypt data for this application. 
+	 */
+	public static Encryptor encryptor() {
+        return ObjFactory.make( securityConfiguration().getEncryptionImplementation(), "Encryptor" );
+	}
+
+	/**
+	 * @return the current ESAPI Executor object being used to safely execute OS commands for this application. 
+	 */
+	public static Executor executor() {
+        return ObjFactory.make( securityConfiguration().getExecutorImplementation(), "Executor" );
+	}
+
+	/**
+	 * @return the current ESAPI HTTPUtilities object being used to safely access HTTP requests and responses 
+	 * for this application. 
+	 */
+	public static HTTPUtilities httpUtilities() {
+        return ObjFactory.make( securityConfiguration().getHTTPUtilitiesImplementation(), "HTTPUtilities" );
+	}
+
+	/**
+	 * @return the current ESAPI IntrusionDetector being used to monitor for intrusions in this application. 
+	 */
+	public static IntrusionDetector intrusionDetector() {
+        return ObjFactory.make( securityConfiguration().getIntrusionDetectionImplementation(), "IntrusionDetector" );
+	}
+
+	/**
+	 * Get the current LogFactory being used by ESAPI. If there isn't one yet, it will create one, and then 
+	 * return this same LogFactory from then on.
+	 * @return The current LogFactory being used by ESAPI.
+	 */
+	private static LogFactory logFactory() {
+        return ObjFactory.make( securityConfiguration().getLogImplementation(), "LogFactory" );
+	}
+	
+	/**
+	 * @param clazz The class to associate the logger with.
+	 * @return The current Logger associated with the specified class.
+	 */
+	@SuppressWarnings("unchecked")		// Because Eclipse wants Class<T> instead.
+	public static Logger getLogger(Class clazz) {
+		return logFactory().getLogger(clazz);
+	}
+	
+	/**
+	 * @param moduleName The module to associate the logger with.
+	 * @return The current Logger associated with the specified module.
+	 */
+	public static Logger getLogger(String moduleName) {
+		return logFactory().getLogger(moduleName);
+	}
+	
+	/**
+	 * @return The default Logger.
+	 */
+	public static Logger log() {
+        return logFactory().getLogger("DefaultLogger");
+    }
+	
+	/**
+	 * @return the current ESAPI Randomizer being used to generate random numbers in this application. 
+	 */
+	public static Randomizer randomizer() {
+        return ObjFactory.make( securityConfiguration().getRandomizerImplementation(), "Randomizer" );
+	}
+
+    private static volatile SecurityConfiguration overrideConfig = null;
+
+	/**
+	 * @return the current ESAPI SecurityConfiguration being used to manage the security configuration for 
+	 * ESAPI for this application. 
+	 */
+	public static SecurityConfiguration securityConfiguration() {
+		// copy the volatile into a non-volatile to prevent TOCTTOU race condition
+		SecurityConfiguration override = overrideConfig;
+		if ( override != null ) {
+			return override;
+        }
+
+        return ObjFactory.make( securityConfigurationImplName, "SecurityConfiguration" );
+	}
+
+	/**
+	 * @return the current ESAPI Validator being used to validate data in this application. 
+	 */
+	public static Validator validator() {
+        return ObjFactory.make( securityConfiguration().getValidationImplementation(), "Validator" );
+	}
+
+    // TODO: This should probably use the SecurityManager or some value within the current
+    // securityConfiguration to determine if this method is allowed to be called. This could
+    // allow for unit tests internal to ESAPI to modify the configuration for the purpose of
+    // testing stuff, and allow developers to allow this in development environments but make
+    // it so the securityConfiguration implementation *cannot* be modified in production environments.
+    //
+    // The purpose of this method is to replace the functionality provided by the setSecurityConfiguration
+    // method that is no longer on this class, and allow the context configuration of the ESAPI
+    // to be modified at Runtime.
+    public static String initialize( String impl ) {
+        String oldImpl = securityConfigurationImplName;
+        securityConfigurationImplName = impl;
+        return oldImpl;
+    }
+
+    /**
+     * Overrides the current security configuration with a new implementation. This is meant
+     * to be used as a temporary means to alter the behavior of the ESAPI and should *NEVER*
+     * be used in a production environment as it will affect the behavior and configuration of
+     * the ESAPI *GLOBALLY*.
+     *
+     * To clear an overridden Configuration, simple call this method with null for the config
+     * parameter.
+     *
+     * @param config
+     * @return
+     */
+    public static void override( SecurityConfiguration config ) {
+        overrideConfig = config;
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java
new file mode 100644
index 0000000..3bce42f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/Encoder.java
@@ -0,0 +1,516 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.IOException;
+
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.errors.EncodingException;
+
+
+/**
+ * The Encoder interface contains a number of methods for decoding input and encoding output
+ * so that it will be safe for a variety of interpreters. To prevent
+ * double-encoding, callers should make sure input does not already contain encoded characters
+ * by calling canonicalize. Validator implementations should call canonicalize on user input
+ * <b>before</b> validating to prevent encoded attacks.
+ * <p>
+ * All of the methods must use a "whitelist" or "positive" security model.
+ * For the encoding methods, this means that all characters should be encoded, except for a specific list of
+ * "immune" characters that are known to be safe.
+ * <p>
+ * The Encoder performs two key functions, encoding and decoding. These functions rely
+ * on a set of codecs that can be found in the org.owasp.esapi.codecs package. These include:
+ * <ul><li>CSS Escaping</li>
+ * <li>HTMLEntity Encoding</li>
+ * <li>JavaScript Escaping</li>
+ * <li>MySQL Escaping</li>
+ * <li>Oracle Escaping</li>
+ * <li>Percent Encoding (aka URL Encoding)</li>
+ * <li>Unix Escaping</li>
+ * <li>VBScript Escaping</li>
+ * <li>Windows Encoding</li></ul>
+ * <p>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Encoder {
+	
+	/**
+	 * Standard character sets
+	 */
+
+	/**  
+	 * @deprecated Use {@link EncoderConstants#CHAR_LOWERS} instead
+	 */
+	@Deprecated
+	public final static char[] CHAR_LOWERS = EncoderConstants.CHAR_LOWERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_UPPERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_UPPERS = EncoderConstants.CHAR_UPPERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_DIGITS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_DIGITS = EncoderConstants.CHAR_DIGITS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_SPECIALS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_SPECIALS = EncoderConstants.CHAR_SPECIALS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_LETTERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_LETTERS = EncoderConstants.CHAR_LETTERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_ALPHANUMERICS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_ALPHANUMERICS = EncoderConstants.CHAR_ALPHANUMERICS;
+	
+	
+	/**
+	 * Password character set, is alphanumerics (without l, i, I, o, O, and 0)
+	 * selected specials like + (bad for URL encoding, | is like i and 1,
+	 * etc...)
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LOWERS} instead
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_LOWERS = EncoderConstants.CHAR_PASSWORD_LOWERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_UPPERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_UPPERS = EncoderConstants.CHAR_PASSWORD_UPPERS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_DIGITS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_DIGITS = EncoderConstants.CHAR_PASSWORD_DIGITS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_SPECIALS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_SPECIALS = EncoderConstants.CHAR_PASSWORD_SPECIALS;
+    /**
+	 * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LETTERS} instead
+	 *
+	 */
+	@Deprecated
+	public final static char[] CHAR_PASSWORD_LETTERS = EncoderConstants.CHAR_PASSWORD_LETTERS;
+    
+
+
+	/**
+	 * This method is equivalent to calling <pre>Encoder.canonicalize(input, restrictMultiple, restrictMixed);</pre>
+	 *
+	 * The default values for restrictMultiple and restrictMixed come from ESAPI.properties
+	 * <pre>
+	 * Encoder.AllowMultipleEncoding=false
+	 * Encoder.AllowMixedEncoding=false
+	 * </pre>
+	 *
+	 * @see Encoder#canonicalize(String, boolean, boolean) canonicalize
+	 * @see <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4">W3C specifications</a>
+	 * 
+	 * @param input the text to canonicalize
+	 * @return a String containing the canonicalized text
+	 */
+	String canonicalize(String input);
+	
+	/**
+	 * This method is the equivalent to calling <pre>Encoder.canonicalize(input, strict, strict);</pre>
+	 *
+	 * @see Encoder#canonicalize(String, boolean, boolean) canonicalize
+	 * @see <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4">W3C specifications</a>
+	 *  
+	 * @param input 
+	 * 		the text to canonicalize
+	 * @param strict 
+	 * 		true if checking for multiple and mixed encoding is desired, false otherwise
+	 * 
+	 * @return a String containing the canonicalized text
+	 */
+	String canonicalize(String input, boolean strict);
+
+	/**
+	 * Canonicalization is simply the operation of reducing a possibly encoded
+	 * string down to its simplest form. This is important, because attackers
+	 * frequently use encoding to change their input in a way that will bypass
+	 * validation filters, but still be interpreted properly by the target of
+	 * the attack. Note that data encoded more than once is not something that a
+	 * normal user would generate and should be regarded as an attack.
+	 * <p>
+     * Everyone <a href="http://cwe.mitre.org/data/definitions/180.html">says</a> you shouldn't do validation
+     * without canonicalizing the data first. This is easier said than done. The canonicalize method can
+     * be used to simplify just about any input down to its most basic form. Note that canonicalize doesn't
+     * handle Unicode issues, it focuses on higher level encoding and escaping schemes. In addition to simple
+     * decoding, canonicalize also handles:
+     * <ul><li>Perverse but legal variants of escaping schemes</li>
+     * <li>Multiple escaping (%2526 or &#x26;lt;)</li>
+     * <li>Mixed escaping (%26lt;)</li>
+     * <li>Nested escaping (%%316 or &%6ct;)</li>
+     * <li>All combinations of multiple, mixed, and nested encoding/escaping (%2&#x35;3c or &#x2526gt;)</li></ul>
+     * <p>
+     * Using canonicalize is simple. The default is just...
+     * <pre>
+     *     String clean = ESAPI.encoder().canonicalize( request.getParameter("input"));
+     * </pre>
+     * You need to decode untrusted data so that it's safe for ANY downstream interpreter or decoder. For
+     * example, if your data goes into a Windows command shell, then into a database, and then to a browser,
+     * you're going to need to decode for all of those systems. You can build a custom encoder to canonicalize
+     * for your application like this...
+     * <pre>
+     *     ArrayList list = new ArrayList();
+     *     list.add( new WindowsCodec() );
+     *     list.add( new MySQLCodec() );
+     *     list.add( new PercentCodec() );
+     *     Encoder encoder = new DefaultEncoder( list );
+     *     String clean = encoder.canonicalize( request.getParameter( "input" ));
+     * </pre>
+     * In ESAPI, the Validator uses the canonicalize method before it does validation.  So all you need to
+     * do is to validate as normal and you'll be protected against a host of encoded attacks.
+     * <pre>
+     *     String input = request.getParameter( "name" );
+     *     String name = ESAPI.validator().isValidInput( "test", input, "FirstName", 20, false);
+     * </pre>
+     * However, the default canonicalize() method only decodes HTMLEntity, percent (URL) encoding, and JavaScript
+     * encoding. If you'd like to use a custom canonicalizer with your validator, that's pretty easy too.
+     * <pre>
+     *     ... setup custom encoder as above
+     *     Validator validator = new DefaultValidator( encoder );
+     *     String input = request.getParameter( "name" );
+     *     String name = validator.isValidInput( "test", input, "name", 20, false);
+     * </pre>
+     * Although ESAPI is able to canonicalize multiple, mixed, or nested encoding, it's safer to not accept
+     * this stuff in the first place. In ESAPI, the default is "strict" mode that throws an IntrusionException
+     * if it receives anything not single-encoded with a single scheme. This is configurable
+     * in ESAPI.properties using the properties:
+	 * <pre>
+	 * Encoder.AllowMultipleEncoding=false
+	 * Encoder.AllowMixedEncoding=false
+	 * </pre>
+	 * This method allows you to override the default behavior by directly specifying whether to restrict
+	 * multiple or mixed encoding. Even if you disable restrictions, you'll still get
+     * warning messages in the log about each multiple encoding and mixed encoding received.
+     * <pre>
+     *     // disabling strict mode to allow mixed encoding
+     *     String url = ESAPI.encoder().canonicalize( request.getParameter("url"), false, false);
+     * </pre>
+	 *
+	 * @see <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4">W3C specifications</a>
+	 *
+	 * @param input
+	 * 		the text to canonicalize
+	 * @param restrictMultiple
+	 * 		true if checking for multiple encoding is desired, false otherwise
+	 * @param restrictMixed
+	 * 		true if checking for mixed encoding is desired, false otherwise
+	 *
+	 * @return a String containing the canonicalized text
+	 */
+	String canonicalize(String input, boolean restrictMultiple, boolean restrictMixed);
+
+	/**
+	 * Encode data for use in Cascading Style Sheets (CSS) content.
+	 * 
+	 * @see <a href="http://www.w3.org/TR/CSS21/syndata.html#escaped-characters">CSS Syntax [w3.org]</a>
+	 * 
+	 * @param input 
+	 * 		the text to encode for CSS
+	 * 
+	 * @return input encoded for CSS
+	 */
+	String encodeForCSS(String input);
+
+	/**
+	 * Encode data for use in HTML using HTML entity encoding
+	 * <p> 
+	 * Note that the following characters:
+	 * 00-08, 0B-0C, 0E-1F, and 7F-9F
+	 * <p>cannot be used in HTML. 
+	 * 
+	 * @see <a href="http://en.wikipedia.org/wiki/Character_encodings_in_HTML">HTML Encodings [wikipedia.org]</a> 
+	 * @see <a href="http://www.w3.org/TR/html4/sgml/sgmldecl.html">SGML Specification [w3.org]</a>
+     * @see <a href="http://www.w3.org/TR/REC-xml/#charsets">XML Specification [w3.org]</a>
+	 * 
+	 * @param input 
+	 * 		the text to encode for HTML
+	 * 
+	 * @return input encoded for HTML
+	 */
+	String encodeForHTML(String input);
+
+	/**
+     * Decodes HTML entities.
+     * @param input the <code>String</code> to decode
+     * @return the newly decoded <code>String</code>
+     */
+	String decodeForHTML(String input);
+		
+	/**
+	 * Encode data for use in HTML attributes.
+	 * 
+	 * @param input 
+	 * 		the text to encode for an HTML attribute
+	 * 
+	 * @return input encoded for use as an HTML attribute
+	 */
+	String encodeForHTMLAttribute(String input);
+
+
+    /**
+     * Encode data for insertion inside a data value or function argument in JavaScript. Including user data 
+     * directly inside a script is quite dangerous. Great care must be taken to prevent including user data
+     * directly into script code itself, as no amount of encoding will prevent attacks there.
+     * 
+     * Please note there are some JavaScript functions that can never safely receive untrusted data 
+     * as input – even if the user input is encoded.
+     * 
+     * For example:
+     * <pre>
+     *  <script>
+     *      window.setInterval('<%= EVEN IF YOU ENCODE UNTRUSTED DATA YOU ARE XSSED HERE %>');
+     *  </script>
+     * </pre>
+     * @param input 
+     *          the text to encode for JavaScript
+     * 
+     * @return input encoded for use in JavaScript
+     */
+	String encodeForJavaScript(String input);
+
+	/**
+	 * Encode data for insertion inside a data value in a Visual Basic script. Putting user data directly
+	 * inside a script is quite dangerous. Great care must be taken to prevent putting user data
+	 * directly into script code itself, as no amount of encoding will prevent attacks there.
+	 * 
+	 * This method is not recommended as VBScript is only supported by Internet Explorer
+	 * 
+	 * @param input 
+	 * 		the text to encode for VBScript
+	 * 
+	 * @return input encoded for use in VBScript
+	 */
+	String encodeForVBScript(String input);
+
+
+	/**
+	 * Encode input for use in a SQL query, according to the selected codec 
+	 * (appropriate codecs include the MySQLCodec and OracleCodec).
+	 * 
+	 * This method is not recommended. The use of the PreparedStatement 
+	 * interface is the preferred approach. However, if for some reason 
+	 * this is impossible, then this method is provided as a weaker 
+	 * alternative. 
+	 * 
+	 * The best approach is to make sure any single-quotes are double-quoted.
+	 * Another possible approach is to use the {escape} syntax described in the
+	 * JDBC specification in section 1.5.6.
+	 * 
+	 * However, this syntax does not work with all drivers, and requires
+	 * modification of all queries.
+	 * 
+	 * @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/getstart/statement.html">JDBC Specification</a>
+	 *  
+	 * @param codec 
+	 * 		a Codec that declares which database 'input' is being encoded for (ie. MySQL, Oracle, etc.)
+	 * @param input 
+	 * 		the text to encode for SQL
+	 * 
+	 * @return input encoded for use in SQL
+	 */
+	String encodeForSQL(Codec codec, String input);
+
+    /**
+     * Encode for an operating system command shell according to the selected codec (appropriate codecs include the WindowsCodec and UnixCodec). 
+     *
+     * Please note the following recommendations before choosing to use this method: 
+     * 
+     * 1)      It is strongly recommended that applications avoid making direct OS system calls if possible as such calls are not portable, and they are potentially unsafe. Please use language provided features if at all possible, rather than native OS calls to implement the desired feature.
+     * 2)      If an OS call cannot be avoided, then it is recommended that the program to be invoked be invoked directly (e.g., System.exec("nameofcommand" + "parameterstocommand");) as this avoids the use of the command shell. The "parameterstocommand" should of course be validated before passing them to the OS command.
+     * 3)      If you must use this method, then we recommend validating all user supplied input passed to the command shell as well, in addition to using this method in order to make the command shell invocation safe.
+     *  
+     * An example use of this method would be: System.exec("dir " + ESAPI.encodeForOS(WindowsCodec, "parameter(s)tocommandwithuserinput");
+     * 
+     * @param codec 
+     *      a Codec that declares which operating system 'input' is being encoded for (ie. Windows, Unix, etc.)
+     * @param input 
+     *      the text to encode for the command shell
+     * 
+     * @return input encoded for use in command shell
+     */
+	String encodeForOS(Codec codec, String input);
+
+	/**
+	 * Encode data for use in LDAP queries.
+	 * 
+	 * @param input 
+	 * 		the text to encode for LDAP
+	 * 
+	 * @return input encoded for use in LDAP
+	 */
+	String encodeForLDAP(String input);
+
+	/**
+	 * Encode data for use in an LDAP distinguished name.
+	 * 
+	 *  @param input 
+	 *  		the text to encode for an LDAP distinguished name
+	 * 
+	 *  @return input encoded for use in an LDAP distinguished name
+	 */
+	String encodeForDN(String input);
+
+	/**
+	 * Encode data for use in an XPath query.
+	 * 
+	 * NB: The reference implementation encodes almost everything and may over-encode. 
+	 * 
+	 * The difficulty with XPath encoding is that XPath has no built in mechanism for escaping
+	 * characters. It is possible to use XQuery in a parameterized way to
+	 * prevent injection. 
+	 * 
+	 * For more information, refer to <a
+	 * href="http://www.ibm.com/developerworks/xml/library/x-xpathinjection.html">this
+	 * article</a> which specifies the following list of characters as the most
+	 * dangerous: ^&"*';<>(). <a
+	 * href="http://www.packetstormsecurity.org/papers/bypass/Blind_XPath_Injection_20040518.pdf">This
+	 * paper</a> suggests disallowing ' and " in queries.
+	 * 
+	 * @see <a href="http://www.ibm.com/developerworks/xml/library/x-xpathinjection.html">XPath Injection [ibm.com]</a>
+	 * @see <a href="http://www.packetstormsecurity.org/papers/bypass/Blind_XPath_Injection_20040518.pdf">Blind XPath Injection [packetstormsecurity.org]</a>
+	 *  
+	 * @param input
+	 *      the text to encode for XPath
+	 * @return 
+	 * 		input encoded for use in XPath
+	 */
+	String encodeForXPath(String input);
+
+	/**
+	 * Encode data for use in an XML element. The implementation should follow the <a
+	 * href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding
+	 * Standard</a> from the W3C.
+	 * <p>
+	 * The use of a real XML parser is strongly encouraged. However, in the
+	 * hopefully rare case that you need to make sure that data is safe for
+	 * inclusion in an XML document and cannot use a parse, this method provides
+	 * a safe mechanism to do so.
+	 * 
+	 * @see <a href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding Standard</a>
+	 * 
+	 * @param input
+	 * 			the text to encode for XML
+	 * 
+	 * @return
+	 *			input encoded for use in XML
+	 */
+	String encodeForXML(String input);
+
+	/**
+	 * Encode data for use in an XML attribute. The implementation should follow
+	 * the <a href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding
+	 * Standard</a> from the W3C.
+	 * <p>
+	 * The use of a real XML parser is highly encouraged. However, in the
+	 * hopefully rare case that you need to make sure that data is safe for
+	 * inclusion in an XML document and cannot use a parse, this method provides
+	 * a safe mechanism to do so.
+	 * 
+	 * @see <a href="http://www.w3schools.com/xml/xml_encoding.asp">XML Encoding Standard</a>
+	 * 
+	 * @param input
+	 * 			the text to encode for use as an XML attribute
+	 * 
+	 * @return 
+	 * 			input encoded for use in an XML attribute
+	 */
+	String encodeForXMLAttribute(String input);
+
+	/**
+	 * Encode for use in a URL. This method performs <a
+	 * href="http://en.wikipedia.org/wiki/Percent-encoding">URL encoding</a>
+	 * on the entire string.
+	 * 
+	 * @see <a href="http://en.wikipedia.org/wiki/Percent-encoding">URL encoding</a>
+	 * 
+	 * @param input 
+	 * 		the text to encode for use in a URL
+	 * 
+	 * @return input 
+	 * 		encoded for use in a URL
+	 * 
+	 * @throws EncodingException 
+	 * 		if encoding fails
+	 */
+	String encodeForURL(String input) throws EncodingException;
+
+	/**
+	 * Decode from URL. Implementations should first canonicalize and
+	 * detect any double-encoding. If this check passes, then the data is decoded using URL
+	 * decoding.
+	 * 
+	 * @param input 
+	 * 		the text to decode from an encoded URL
+	 * 
+	 * @return 
+	 * 		the decoded URL value
+	 * 
+	 * @throws EncodingException 
+	 * 		if decoding fails
+	 */
+	String decodeFromURL(String input) throws EncodingException;
+
+	/**
+	 * Encode for Base64.
+	 * 
+	 * @param input 
+	 * 		the text to encode for Base64
+	 * @param wrap
+	 * 		the encoder will wrap lines every 64 characters of output
+	 * 
+	 * @return input encoded for Base64
+	 */
+	String encodeForBase64(byte[] input, boolean wrap);
+
+	/**
+	 * Decode data encoded with BASE-64 encoding.
+	 * 
+	 * @param input 
+	 * 		the Base64 text to decode
+	 * 
+	 * @return input 
+	 * 		decoded from Base64
+	 * 
+	 * @throws IOException
+	 */
+	byte[] decodeFromBase64(String input) throws IOException;
+
+}
diff --git a/src/main/java/org/owasp/esapi/EncoderConstants.java b/src/main/java/org/owasp/esapi/EncoderConstants.java
new file mode 100644
index 0000000..d148027
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/EncoderConstants.java
@@ -0,0 +1,117 @@
+package org.owasp.esapi;
+
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * Common character classes used for input validation, output encoding, verifying password strength
+ * CSRF token generation, generating salts, etc
+ * @author Neil Matatall (neil.matatall .at. gmail.com)
+ * @see User
+ */
+public class EncoderConstants {
+	/**
+	 * !$*-.=?@_
+	 */
+	public final static char[] CHAR_PASSWORD_SPECIALS = { '!', '$', '*', '-', '.', '=', '?', '@', '_' };
+	public final static Set<Character> PASSWORD_SPECIALS;
+	static {
+		PASSWORD_SPECIALS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS);
+	}
+	
+	/**
+	 * a-b
+	 */
+	public final static char[] CHAR_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
+	public final static Set<Character> LOWERS;
+	static {
+		LOWERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS);
+	}
+	
+	/**
+	 * A-Z
+	 */
+	public final static char[] CHAR_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
+	public final static Set<Character> UPPERS;
+	static {
+		UPPERS = CollectionsUtil.arrayToSet(CHAR_UPPERS);
+	}
+	/**
+	 * 0-9
+	 */
+	public final static char[] CHAR_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+	public final static Set<Character> DIGITS;
+	static {
+		DIGITS = CollectionsUtil.arrayToSet(CHAR_DIGITS);
+	}
+	
+	/**
+	 * !$*+-.=?@^_|~
+	 */
+	public final static char[] CHAR_SPECIALS = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' };
+	public final static Set<Character> SPECIALS;
+	static {
+		SPECIALS = CollectionsUtil.arrayToSet(CHAR_SPECIALS);
+	}
+	
+	/**
+	 * CHAR_LOWERS union CHAR_UPPERS
+	 */
+	public final static char[] CHAR_LETTERS = StringUtilities.union(CHAR_LOWERS, CHAR_UPPERS);
+	public final static Set<Character> LETTERS;
+	static {
+		LETTERS = CollectionsUtil.arrayToSet(CHAR_LETTERS);
+	}
+	
+	/**
+	 * CHAR_LETTERS union CHAR_DIGITS
+	 */
+	public final static char[] CHAR_ALPHANUMERICS = StringUtilities.union(CHAR_LETTERS, CHAR_DIGITS);
+	public final static Set<Character> ALPHANUMERICS;
+	static {
+		ALPHANUMERICS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS);
+	}
+	
+	/**
+	 * Password character set, is alphanumerics (without l, i, I, o, O, and 0)
+	 * selected specials like + (bad for URL encoding, | is like i and 1,
+	 * etc...)
+	 */
+	public final static char[] CHAR_PASSWORD_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
+	public final static Set<Character> PASSWORD_LOWERS;
+	static {
+		PASSWORD_LOWERS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS);
+	}
+	
+	/**
+	 * 
+	 */
+	public final static char[] CHAR_PASSWORD_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
+	public final static Set<Character> PASSWORD_UPPERS;
+	static {
+		PASSWORD_UPPERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_UPPERS);
+	}
+	
+	/**
+	 * 2-9
+	 */
+	public final static char[] CHAR_PASSWORD_DIGITS = { '2', '3', '4', '5', '6', '7', '8', '9' };
+	public final static Set<Character> PASSWORD_DIGITS;
+	static {
+		PASSWORD_DIGITS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_DIGITS);
+	}
+	
+	/**
+	 * CHAR_PASSWORD_LOWERS union CHAR_PASSWORD_UPPERS
+	 */
+	public final static char[] CHAR_PASSWORD_LETTERS = StringUtilities.union( CHAR_PASSWORD_LOWERS, CHAR_PASSWORD_UPPERS );
+	public final static Set<Character> PASSWORD_LETTERS;
+	static {
+		PASSWORD_LETTERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_LETTERS);
+	}
+
+	private EncoderConstants() {
+		// prevent instantiation
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/EncryptedProperties.java b/src/main/java/org/owasp/esapi/EncryptedProperties.java
new file mode 100644
index 0000000..e95b1b8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/EncryptedProperties.java
@@ -0,0 +1,112 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Set;
+
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The {@code EncryptedProperties} interface represents a properties file
+ * where all the data is encrypted before it is added, and decrypted when it
+ * retrieved. This interface can be implemented in a number of ways, the
+ * simplest being extending {@link java.util.Properties} and overloading
+ * the {@code getProperty} and {@code setProperty} methods. In all cases,
+ * the master encryption key, as given by the {@code Encryptor.MasterKey}
+ * property in <b><code>ESAPI.properties</code></b> file.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface EncryptedProperties {
+
+	/**
+	 * Gets the property value from the encrypted store, decrypts it, and
+	 * returns the plaintext value to the caller.
+	 * 
+	 * @param key
+	 *      the name of the property to get 
+	 * 
+	 * @return 
+	 * 	The decrypted property value. null if the key is not set.
+	 * 
+	 * @throws EncryptionException
+	 *      if the property could not be decrypted
+	 */
+	String getProperty(String key) throws EncryptionException;
+
+	/**
+	 * Encrypts the plaintext property value and stores the ciphertext value
+	 * in the encrypted store.
+	 * 
+	 * @param key
+	 *      the name of the property to set
+	 * @param value
+	 * 		the value of the property to set
+	 * 
+	 * @return 
+	 * 		the previously encrypted property value for the specified key, or
+	 *      {@code null} if it did not have one.
+	 * 
+	 * @throws EncryptionException
+	 *      if the property could not be encrypted
+	 */
+	String setProperty(String key, String value) throws EncryptionException;
+	
+	/**
+	 * Returns a {@code Set} view of properties. The {@code Set} is backed by a
+	 * {@code java.util.Hashtable}, so changes to the {@code Hashtable} are
+	 * reflected in the {@code Set}, and vice-versa. The {@code Set} supports element 
+	 * removal (which removes the corresponding entry from the {@code Hashtable),
+	 * but not element addition.
+	 * 
+	 * @return 
+	 * 		a set view of the properties contained in this map.
+	 */
+	public Set<?> keySet();
+		
+	/**
+	 * Reads a property list (key and element pairs) from the input stream.
+	 * 
+	 * @param in
+	 * 		the input stream that contains the properties file
+	 * 
+	 * @throws IOException
+	 *      Signals that an I/O exception has occurred.
+	 */
+	public void load(InputStream in) throws IOException;
+	
+	/**
+	 * Writes this property list (key and element pairs) in this Properties table to 
+	 * the output stream in a format suitable for loading into a Properties table using the load method. 
+	 * 
+	 * @param out
+	 * 		the output stream that contains the properties file
+	 * @param comments
+	 *            a description of the property list (ex. "Encrypted Properties File").
+	 * 
+	 * @throws IOException
+	 *             Signals that an I/O exception has occurred.
+	 */
+	public void store(OutputStream out, String comments) throws IOException;	
+	
+	
+}
diff --git a/src/main/java/org/owasp/esapi/Encryptor.java b/src/main/java/org/owasp/esapi/Encryptor.java
new file mode 100644
index 0000000..35bf094
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/Encryptor.java
@@ -0,0 +1,318 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2007,2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import javax.crypto.SecretKey;
+
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.IntegrityException;
+
+
+/**
+ * The Encryptor interface provides a set of methods for performing common
+ * encryption, random number, and hashing operations. Implementations should
+ * rely on a strong cryptographic implementation, such as JCE or BouncyCastle.
+ * Implementors should take care to ensure that they initialize their
+ * implementation with a strong "master key", and that they protect this secret
+ * as much as possible.
+ * <P>
+ * The main property controlling the selection of the implementation class is the
+ * property {@code ESAPI.Encryptor} in {@code ESAPI.properties}. Most of the
+ * the other encryption related properties have property names that start with
+ * the string "Encryptor.". These properties all you to do things such as
+ * select the encryption algorithms, the preferred JCE provider, etc.
+ * </P><P>
+ * In addition, there are two important properties (initially delivered as unset
+ * from the ESAPI download) named {@code Encryptor.MasterKey} and
+ * {@code Encryptor.MasterSalt} that must be set before using ESAPI encryption.
+ * There is a <i>bash</i>(1) shell script provided with the standard ESAPI distribution
+ * called 'setMasterKey.sh' that will assist you in setting these two properties. The
+ * script is in 'src/examples/scripts/setMasterKey.sh'.
+ * </P><P>
+ * Possible future enhancements (depending on feedback) are discussed in
+ * section 4 of
+ * <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-crypto-design-goals.doc">
+ * Design Goals in OWASP ESAPI Cryptography</a>.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html">User Guide for Symmetric Encryption in ESAPI 2.0</a>
+ */
+public interface Encryptor {
+
+	/**
+	 * Returns a string representation of the hash of the provided plaintext and
+	 * salt. The salt helps to protect against a rainbow table attack by mixing
+	 * in some extra data with the plaintext. Some good choices for a salt might
+	 * be an account name or some other string that is known to the application
+	 * but not to an attacker.
+	 * See <a href="http://www.matasano.com/log/958/enough-with-the-rainbow-tables-what-you-need-to-know-about-secure-password-schemes/">
+	 * this article</a> for more information about hashing as it pertains to password schemes.
+	 * 
+	 * @param plaintext
+	 * 		the plaintext String to encrypt
+	 * @param salt
+	 *      the salt to add to the plaintext String before hashing
+	 * 
+	 * @return 
+	 * 		the encrypted hash of 'plaintext' stored as a String
+	 * 
+	 * @throws EncryptionException
+	 *      if the specified hash algorithm could not be found or another problem exists with 
+	 *      the hashing of 'plaintext'
+	 */
+	String hash(String plaintext, String salt) throws EncryptionException;
+
+	/**
+	 * Returns a string representation of the hash of the provided plaintext and
+	 * salt. The salt helps to protect against a rainbow table attack by mixing
+	 * in some extra data with the plaintext. Some good choices for a salt might
+	 * be an account name or some other string that is known to the application
+	 * but not to an attacker. 
+	 * See <a href="http://www.matasano.com/log/958/enough-with-the-rainbow-tables-what-you-need-to-know-about-secure-password-schemes/">
+	 * this article</a> for more information about hashing as it pertains to password schemes.
+	 * 
+	 * @param plaintext
+	 * 		the plaintext String to encrypt
+	 * @param salt
+	 *      the salt to add to the plaintext String before hashing
+	 * @param iterations
+	 *      the number of times to iterate the hash
+	 * 
+	 * @return 
+	 * 		the encrypted hash of 'plaintext' stored as a String
+	 * 
+	 * @throws EncryptionException
+	 *      if the specified hash algorithm could not be found or another problem exists with 
+	 *      the hashing of 'plaintext'
+	 */
+	String hash(String plaintext, String salt, int iterations) throws EncryptionException;
+	
+	/**
+	 * Encrypts the provided plaintext bytes using the cipher transformation
+	 * specified by the property <code>Encryptor.CipherTransformation</code>
+	 * and the <i>master encryption key</i> as specified by the property
+	 * {@code Encryptor.MasterKey} as defined in the <code>ESAPI.properties</code> file.
+	 * </p>
+	 * 
+	 * @param plaintext	The {@code PlainText} to be encrypted.
+	 * @return the {@code CipherText} object from which the raw ciphertext, the
+	 * 				IV, the cipher transformation, and many other aspects about
+	 * 				the encryption detail may be extracted.
+	 * @throws EncryptionException Thrown if something should go wrong such as
+	 * 				the JCE provider cannot be found, the cipher algorithm,
+	 * 				cipher mode, or padding scheme not being supported, specifying
+	 * 				an unsupported key size, specifying an IV of incorrect length,
+	 * 				etc.
+	 * @see #encrypt(SecretKey, PlainText)
+	 * @since 2.0
+	 */
+	 CipherText encrypt(PlainText plaintext) throws EncryptionException;
+
+
+	 /**
+	  * Encrypts the provided plaintext bytes using the cipher transformation
+	  * specified by the property <code>Encryptor.CipherTransformation</code>
+	  * as defined in the <code>ESAPI.properties</code> file and the
+	  * <i>specified secret key</i>.
+	  * </p><p>
+	  * This method is similar to {@link #encrypt(PlainText)} except that it
+	  * permits a specific {@code SecretKey} to be used for encryption.
+	  *
+	  * @param key		The {@code SecretKey} to use for encrypting the plaintext.
+	  * @param plaintext	The byte stream to be encrypted. Note if a Java
+	  * 				{@code String} is to be encrypted, it should be converted
+	  * 				using {@code "some string".getBytes("UTF-8")}.
+	  * @return the {@code CipherText} object from which the raw ciphertext, the
+	  * 				IV, the cipher transformation, and many other aspects about
+	  * 				the encryption detail may be extracted.
+	  * @throws EncryptionException Thrown if something should go wrong such as
+	  * 				the JCE provider cannot be found, the cipher algorithm,
+	  * 				cipher mode, or padding scheme not being supported, specifying
+	  * 				an unsupported key size, specifying an IV of incorrect length,
+	  * 				etc.
+	  * @see #encrypt(PlainText)
+	  * @since 2.0
+	  */
+	 CipherText encrypt(SecretKey key, PlainText plaintext)
+	 		throws EncryptionException;
+
+	/**
+	 * Decrypts the provided {@link CipherText} using the information from it
+	 * and the <i>master encryption key</i> as specified by the property
+	 * {@code Encryptor.MasterKey} as defined in the {@code ESAPI.properties}
+	 * file.
+	 * </p>
+	 * @param ciphertext The {@code CipherText} object to be decrypted.
+	 * @return The {@code PlainText} object resulting from decrypting the specified
+	 * 		   ciphertext. Note that it it is desired to convert the returned
+	 * 		   plaintext byte array to a Java String is should be done using
+	 * 		   {@code new String(byte[], "UTF-8");} rather than simply using
+	 * 		   {@code new String(byte[]);} which uses native encoding and may
+	 * 		   not be portable across hardware and/or OS platforms.
+	 * @throws EncryptionException  Thrown if something should go wrong such as
+	 * 				the JCE provider cannot be found, the cipher algorithm,
+	 * 				cipher mode, or padding scheme not being supported, specifying
+	 * 				an unsupported key size, or incorrect encryption key was
+	 * 				specified or a {@code PaddingException} occurs.
+	 * @see #decrypt(SecretKey, CipherText)
+	 */
+	PlainText decrypt(CipherText ciphertext) throws EncryptionException;
+	
+	/**
+	 * Decrypts the provided {@link CipherText} using the information from it
+	 * and the <i>specified secret key</i>.
+	 * </p><p>
+	 * This decrypt method is similar to {@link #decrypt(CipherText)} except that
+	 * it allows decrypting with a secret key other than the <i>master secret key</i>.
+	 * </p>
+	 * @param key		The {@code SecretKey} to use for encrypting the plaintext.
+	 * @param ciphertext The {@code CipherText} object to be decrypted.
+	 * @return The {@code PlainText} object resulting from decrypting the specified
+	 * 		   ciphertext. Note that it it is desired to convert the returned
+	 * 		   plaintext byte array to a Java String is should be done using
+	 * 		   {@code new String(byte[], "UTF-8");} rather than simply using
+	 * 		   {@code new String(byte[]);} which uses native encoding and may
+	 * 		   not be portable across hardware and/or OS platforms.
+	 * @throws EncryptionException  Thrown if something should go wrong such as
+	 * 				the JCE provider cannot be found, the cipher algorithm,
+	 * 				cipher mode, or padding scheme not being supported, specifying
+	 * 				an unsupported key size, or incorrect encryption key was
+	 * 				specified or a {@code PaddingException} occurs.
+	 * @see #decrypt(CipherText)
+	 */
+	PlainText decrypt(SecretKey key, CipherText ciphertext) throws EncryptionException;
+	
+	/**
+	 * Create a digital signature for the provided data and return it in a
+	 * string.
+	 * <p>
+	 * <b>Limitations:</b> A new public/private key pair used for ESAPI 2.0 digital
+	 * signatures with this method and {@link #verifySignature(String, String)}
+	 * are dynamically created when the default reference implementation class,
+	 * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created.
+	 * Because this key pair is not persisted nor is the public key shared,
+	 * this method and the corresponding {@link #verifySignature(String, String)}
+	 * can not be used with expected results across JVM instances. This limitation
+	 * will be addressed in ESAPI 2.1.
+	 * </p>
+	 * 
+	 * @param data
+	 *      the data to sign
+	 * 
+	 * @return 
+	 * 		the digital signature stored as a String
+	 * 
+	 * @throws EncryptionException
+	 * 		if the specified signature algorithm cannot be found
+	 */
+	String sign(String data) throws EncryptionException;
+
+	/**
+	 * Verifies a digital signature (created with the sign method) and returns
+	 * the boolean result.
+     * <p>
+     * <b>Limitations:</b> A new public/private key pair used for ESAPI 2.0 digital
+     * signatures with this method and {@link #sign(String)}
+     * are dynamically created when the default reference implementation class,
+     * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created.
+     * Because this key pair is not persisted nor is the public key shared,
+     * this method and the corresponding {@link #sign(String)}
+     * can not be used with expected results across JVM instances. This limitation
+     * will be addressed in ESAPI 2.1.
+     * </p>
+	 * @param signature
+	 *      the signature to verify against 'data'
+	 * @param data
+	 *      the data to verify against 'signature'
+	 * 
+	 * @return 
+	 * 		true, if the signature is verified, false otherwise
+	 * 
+	 */
+	boolean verifySignature(String signature, String data);
+
+	/**
+	 * Creates a seal that binds a set of data and includes an expiration timestamp.
+	 * 
+	 * @param data
+	 *      the data to seal
+	 * @param timestamp
+	 *      the absolute expiration date of the data, expressed as seconds since the epoch
+	 * 
+	 * @return 
+     * 		the seal
+     * @throws IntegrityException
+	 * 
+	 */
+	String seal(String data, long timestamp) throws IntegrityException;
+
+	/**
+	 * Unseals data (created with the seal method) and throws an exception
+	 * describing any of the various problems that could exist with a seal, such
+	 * as an invalid seal format, expired timestamp, or decryption error.
+	 * 
+	 * @param seal
+	 *      the sealed data
+	 * 
+	 * @return 
+	 * 		the original (unsealed) data
+	 * 
+	 * @throws EncryptionException 
+	 * 		if the unsealed data cannot be retrieved for any reason
+	 */
+	String unseal( String seal ) throws EncryptionException;
+	
+	/**
+	 * Verifies a seal (created with the seal method) and throws an exception
+	 * describing any of the various problems that could exist with a seal, such
+	 * as an invalid seal format, expired timestamp, or data mismatch.
+	 * 
+	 * @param seal
+	 *      the seal to verify
+	 * 
+	 * @return 
+	 * 		true, if the seal is valid.  False otherwise
+	 */
+	boolean verifySeal(String seal);
+	
+	/**
+	 * Gets an absolute timestamp representing an offset from the current time to be used by
+	 * other functions in the library.
+	 * 
+	 * @param offset 
+	 * 		the offset to add to the current time
+	 * 
+	 * @return 
+	 * 		the absolute timestamp
+	 */
+	public long getRelativeTimeStamp( long offset );
+	
+	
+	/**
+	 * Gets a timestamp representing the current date and time to be used by
+	 * other functions in the library.
+	 * 
+	 * @return 
+	 * 		a timestamp representing the current time
+	 */
+	long getTimeStamp();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/ExecuteResult.java b/src/main/java/org/owasp/esapi/ExecuteResult.java
new file mode 100644
index 0000000..e9835e3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/ExecuteResult.java
@@ -0,0 +1,75 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2010 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Patrick Higgins
+ * @created 2010
+ */
+package org.owasp.esapi;
+
+/**
+ * The ExecuteResult class encapsulates the pieces of data that can be returned
+ * from a process executed by the Executor interface.
+ *
+ * This class is immutable for thread-safety.
+ *
+ * @author Patrick Higgins
+ * @since Aug 25, 2010
+ */
+public class ExecuteResult {
+    
+	private final int exitValue;
+	private final String output;
+	private final String errors;
+
+	/**
+	 * Constructs an ExecuteResult from the given values.
+	 *
+	 * @param exitValue
+	 *            the code from java.lang.Process.exitValue()
+	 * @param output
+	 *            the contents read from java.lang.Process.getInputStream()
+	 * @param errors
+	 *            the contents read from java.lang.Process.getErrorStream()
+	 */
+	public ExecuteResult(int exitValue, String output, String errors) {
+		this.exitValue = exitValue;
+		this.output = output;
+		this.errors = errors;
+	}
+
+	/**
+	 * @return the code from java.lang.Process.exitValue()
+	 */
+	public int getExitValue() {
+		return exitValue;
+	}
+
+	/**
+	 * @return the contents read from java.lang.Process.getInputStream()
+	 */
+	public String getOutput() {
+		return output;
+	}
+
+	/**
+	 * @return the contents read from java.lang.Process.getErrorStream()
+	 */
+	public String getErrors() {
+		return errors;
+	}
+	
+	@Override
+	public String toString() {
+		return "ExecuteResult[exitValue="+exitValue+",output="+output+",errors="+errors+"]";
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/Executor.java b/src/main/java/org/owasp/esapi/Executor.java
new file mode 100644
index 0000000..6286741
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/Executor.java
@@ -0,0 +1,78 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.util.List;
+
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.errors.ExecutorException;
+
+/**
+ * The Executor interface is used to run an OS command with reduced security risk.
+ * 
+ * <p>Implementations should do as much as possible to minimize the risk of
+ * injection into either the command or parameters. In addition, implementations
+ * should timeout after a specified time period in order to help prevent denial
+ * of service attacks.</p> 
+ * 
+ * <p>The class should perform logging and error handling as
+ * well. Finally, implementation should handle errors and generate an
+ * ExecutorException with all the necessary information.</p>
+ *
+ * <p>The reference implementation does all of the above.</p>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Executor {
+
+	/**
+	 * Invokes the specified executable with default workdir and codec and not logging parameters.
+	 * 
+	 * @param executable
+	 *            the command to execute
+	 * @param params
+	 *            the parameters of the command being executed
+	 */
+	ExecuteResult executeSystemCommand(File executable, List params) throws ExecutorException;
+
+	/**
+	 * Executes a system command after checking that the executable exists and
+	 * escaping all the parameters to ensure that injection is impossible.
+	 * Implementations must change to the specified working
+	 * directory before invoking the command.
+	 *
+	 * @param executable
+	 *            the command to execute
+	 * @param params
+	 *            the parameters of the command being executed
+	 * @param workdir
+	 *            the working directory
+	 * @param codec
+	 *            the codec to use to encode for the particular OS in use
+	 * @param logParams
+	 *            use false if any parameters contains sensitive or confidential information
+	 *
+	 * @return the output of the command being run
+	 *
+	 * @throws ExecutorException
+	 *             the service exception
+	 */
+	ExecuteResult executeSystemCommand(File executable, List params, File workdir, Codec codec, boolean logParams, boolean redirectErrorStream) throws ExecutorException;
+
+}
diff --git a/src/main/java/org/owasp/esapi/HTTPUtilities.java b/src/main/java/org/owasp/esapi/HTTPUtilities.java
new file mode 100644
index 0000000..f3eb169
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/HTTPUtilities.java
@@ -0,0 +1,649 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.*;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * The HTTPUtilities interface is a collection of methods that provide additional security related to HTTP requests,
+ * responses, sessions, cookies, headers, and logging.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface HTTPUtilities
+{
+
+    final static String REMEMBER_TOKEN_COOKIE_NAME = "rtoken";
+    final static int MAX_COOKIE_LEN = 4096;            // From RFC 2109
+	final static int MAX_COOKIE_PAIRS = 20;			// From RFC 2109
+	final static String CSRF_TOKEN_NAME = "ctoken";
+	final static String ESAPI_STATE = "estate";
+
+	final static int PARAMETER = 0;
+	final static int HEADER = 1;
+	final static int COOKIE = 2;
+
+
+	/**
+     * Calls addCookie with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+    void addCookie(Cookie cookie);
+
+	/**
+     * Add a cookie to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This method also sets
+     * the secure and HttpOnly flags on the cookie.
+     *
+     * @param cookie
+     */
+    void addCookie(HttpServletResponse response, Cookie cookie);
+
+	/**
+     * Adds the current user's CSRF token (see User.getCSRFToken()) to the URL for purposes of preventing CSRF attacks.
+     * This method should be used on all URLs to be put into all links and forms the application generates.
+     *
+     * @param href the URL to which the CSRF token will be appended
+     * @return the updated URL with the CSRF token parameter added
+     */
+    String addCSRFToken(String href);
+
+    /**
+     * Calls addHeader with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+    void addHeader(String name, String value);
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This implementation
+     * follows the following recommendation: "A recipient MAY replace any linear
+     * white space with a single SP before interpreting the field value or
+     * forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     *
+     * @param name
+     * @param value
+     */
+    void addHeader(HttpServletResponse response, String name, String value);
+
+	/**
+     * Calls assertSecureRequest with the *current* request.
+	 * @see {@link HTTPUtilities#assertSecureRequest(HttpServletRequest)}
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void assertSecureRequest() throws AccessControlException;
+
+	/**
+     * Calls assertSecureChannel with the *current* request.
+	 * @see {@link HTTPUtilities#assertSecureChannel(HttpServletRequest)}
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void assertSecureChannel() throws AccessControlException;
+
+	/**
+	 * Ensures that the request uses both SSL and POST to protect any sensitive parameters
+	 * in the querystring from being sniffed, logged, bookmarked, included in referer header, etc...
+	 * This method should be called for any request that contains sensitive data from a web form.
+     *
+     * @param request
+     * @throws AccessControlException if security constraints are not met
+	 */
+    void assertSecureRequest(HttpServletRequest request) throws AccessControlException;
+
+	/**
+	 * Ensures the use of SSL to protect any sensitive parameters in the request and
+	 * any sensitive data in the response. This method should be called for any request
+	 * that contains sensitive data from a web form or will result in sensitive data in the
+	 * response page.
+     *
+     * @param request
+     * @throws AccessControlException if security constraints are not met
+	 */
+    void assertSecureChannel(HttpServletRequest request) throws AccessControlException;
+
+	/**
+     * Calls changeSessionIdentifier with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+	HttpSession changeSessionIdentifier() throws AuthenticationException;
+
+	/**
+     * Invalidate the existing session after copying all of its contents to a newly created session with a new session id.
+     * Note that this is different from logging out and creating a new session identifier that does not contain the
+     * existing session contents. Care should be taken to use this only when the existing session does not contain
+     * hazardous contents.
+     *
+     * @param request
+     * @return the new HttpSession with a changed id
+     * @throws AuthenticationException the exception
+     */
+    HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException;
+
+    /**
+	 * Clears the current HttpRequest and HttpResponse associated with the current thread.
+     *
+	 * @see ESAPI#clearCurrent()
+	 */
+    void clearCurrent();
+
+    /**
+	 * Decrypts an encrypted hidden field value and returns the cleartext. If the field does not decrypt properly,
+	 * an IntrusionException is thrown to indicate tampering.
+     *
+	 * @param encrypted hidden field value to decrypt
+	 * @return decrypted hidden field value stored as a String
+	 */
+	String decryptHiddenField(String encrypted);
+
+    /**
+	 * Takes an encrypted querystring and returns a Map containing the original parameters.
+     *
+	 * @param encrypted the encrypted querystring to decrypt
+	 * @return a Map object containing the decrypted querystring
+	 * @throws EncryptionException
+	 */
+    Map<String, String> decryptQueryString(String encrypted) throws EncryptionException;
+
+    /**
+     * Calls decryptStateFromCookie with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+     */
+    Map<String, String> decryptStateFromCookie() throws EncryptionException;
+
+    /**
+     * Retrieves a map of data from a cookie encrypted with encryptStateInCookie().
+     *
+     * @param request
+     * @return a map containing the decrypted cookie state value
+	 * @throws EncryptionException
+     */
+    Map<String, String> decryptStateFromCookie(HttpServletRequest request) throws EncryptionException;
+
+    /**
+     * Encrypts a hidden field value for use in HTML.
+     *
+     * @param value the cleartext value of the hidden field
+     * @return the encrypted value of the hidden field
+     * @throws EncryptionException
+     */
+	String encryptHiddenField(String value) throws EncryptionException;
+
+	/**
+	 * Takes a querystring (everything after the question mark in the URL) and returns an encrypted string containing the parameters.
+     *
+	 * @param query the querystring to encrypt
+	 * @return encrypted querystring stored as a String
+	 * @throws EncryptionException
+	 */
+	String encryptQueryString(String query) throws EncryptionException;
+
+	/**
+	 * Calls encryptStateInCookie with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void encryptStateInCookie(Map<String, String> cleartext) throws EncryptionException;
+
+    /**
+     * Stores a Map of data in an encrypted cookie. Generally the session is a better
+     * place to store state information, as it does not expose it to the user at all.
+     * If there is a requirement not to use sessions, or the data should be stored
+     * across sessions (for a long time), the use of encrypted cookies is an effective
+     * way to prevent the exposure.
+     *
+     * @param response
+     * @param cleartext
+     * @throws EncryptionException
+     */
+    void encryptStateInCookie(HttpServletResponse response, Map<String, String> cleartext) throws EncryptionException;
+
+    /**
+	 * Calls getCookie with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	String getCookie(String name) throws ValidationException;
+
+	/**
+     * A safer replacement for getCookies() in HttpServletRequest that returns the canonicalized
+     * value of the named cookie after "global" validation against the
+     * general type defined in ESAPI.properties. This should not be considered a replacement for
+     * more specific validation.
+     *
+     * @param request
+     * @param name
+     * @return the requested cookie value
+     */
+	String getCookie(HttpServletRequest request, String name) throws ValidationException;
+
+    /**
+     * Returns the current user's CSRF token. If there is no current user then return null.
+     *
+     * @return the current users CSRF token
+     */
+    String getCSRFToken();
+
+	/**
+     * Retrieves the current HttpServletRequest
+     *
+     * @return the current request
+     */
+    HttpServletRequest getCurrentRequest();
+
+	/**
+     * Retrieves the current HttpServletResponse
+     *
+     * @return the current response
+     */
+    HttpServletResponse getCurrentResponse();
+
+	/**
+	 * Calls getFileUploads with the *current* request, default upload directory, and default allowed file extensions
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	List getFileUploads() throws ValidationException;
+
+    /**
+	 * Call getFileUploads with the specified request, default upload directory, and default allowed file extensions
+	 */
+	List getFileUploads(HttpServletRequest request) throws ValidationException;
+
+    /**
+	 * Call getFileUploads with the specified request, specified upload directory, and default allowed file extensions
+	 */
+    List getFileUploads(HttpServletRequest request, File finalDir) throws ValidationException;
+
+
+    /**
+     * Extract uploaded files from a multipart HTTP requests. Implementations must check the content to ensure that it
+     * is safe before making a permanent copy on the local filesystem. Checks should include length and content checks,
+     * possibly virus checking, and path and name checks. Refer to the file checking methods in Validator for more
+     * information.
+     * <p/>
+	 * This method uses {@link HTTPUtilities#getCurrentRequest()} to obtain the {@link HttpServletRequest} object
+     *
+     * @param request
+     * @return List of new File objects from upload
+     * @throws ValidationException if the file fails validation
+     */
+    List getFileUploads(HttpServletRequest request, File destinationDir, List allowedExtensions) throws ValidationException;
+
+
+	/**
+	 * Calls getHeader with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	String getHeader(String name) throws ValidationException;
+
+    /**
+     * A safer replacement for getHeader() in HttpServletRequest that returns the canonicalized
+     * value of the named header after "global" validation against the
+     * general type defined in ESAPI.properties. This should not be considered a replacement for
+     * more specific validation.
+     *
+     * @param request
+     * @param name
+     * @return the requested header value
+     */
+	String getHeader(HttpServletRequest request, String name) throws ValidationException;
+
+	/**
+	 * Calls getParameter with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	String getParameter(String name) throws ValidationException;
+
+    /**
+     * A safer replacement for getParameter() in HttpServletRequest that returns the canonicalized
+     * value of the named parameter after "global" validation against the
+     * general type defined in ESAPI.properties. This should not be considered a replacement for
+     * more specific validation.
+     *
+     * @param request
+     * @param name
+     * @return the requested parameter value
+     */
+    String getParameter(HttpServletRequest request, String name) throws ValidationException;
+
+	/**
+	 * Calls killAllCookies with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void killAllCookies();
+
+    /**
+     * Kill all cookies received in the last request from the browser. Note that new cookies set by the application in
+     * this response may not be killed by this method.
+     *
+     * @param request
+     * @param response
+     */
+    void killAllCookies(HttpServletRequest request, HttpServletResponse response);
+
+	/**
+	 * Calls killCookie with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void killCookie(String name);
+
+    /**
+     * Kills the specified cookie by setting a new cookie that expires immediately. Note that this
+     * method does not delete new cookies that are being set by the application for this response.
+     *
+     * @param request
+     * @param name
+     * @param response
+     */
+    void killCookie(HttpServletRequest request, HttpServletResponse response, String name);
+
+	/**
+	 * Calls logHTTPRequest with the *current* request and logger.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+	void logHTTPRequest();
+
+    /**
+     * Format the Source IP address, URL, URL parameters, and all form
+     * parameters into a string suitable for the log file. Be careful not
+     * to log sensitive information, and consider masking with the
+     * logHTTPRequest( List parameterNamesToObfuscate ) method.
+     *
+     * @param request
+     * @param logger the logger to write the request to
+     */
+    void logHTTPRequest(HttpServletRequest request, Logger logger);
+
+    /**
+     * Format the Source IP address, URL, URL parameters, and all form
+     * parameters into a string suitable for the log file. The list of parameters to
+     * obfuscate should be specified in order to prevent sensitive information
+     * from being logged. If a null list is provided, then all parameters will
+     * be logged. If HTTP request logging is done in a central place, the
+     * parameterNamesToObfuscate could be made a configuration parameter. We
+     * include it here in case different parts of the application need to obfuscate
+     * different parameters.
+     *
+     * @param request
+     * @param logger the logger to write the request to
+     * @param parameterNamesToObfuscate the sensitive parameters
+     */
+    void logHTTPRequest(HttpServletRequest request, Logger logger, List parameterNamesToObfuscate);
+
+	/**
+	 * Calls sendForward with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void sendForward(String location) throws AccessControlException, ServletException, IOException;
+
+    /**
+     * This method performs a forward to any resource located inside the WEB-INF directory. Forwarding to
+     * publicly accessible resources can be dangerous, as the request will have already passed the URL
+     * based access control check. This method ensures that you can only forward to non-publicly
+     * accessible resources.
+     *
+     * @param request
+     * @param response
+     * @param location the URL to forward to, including parameters
+     * @throws AccessControlException
+     * @throws ServletException
+     * @throws IOException
+     */
+    void sendForward(HttpServletRequest request, HttpServletResponse response, String location) throws AccessControlException, ServletException, IOException;
+
+
+	/**
+	 * Calls sendRedirect with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void sendRedirect(String location) throws AccessControlException, IOException;
+
+
+    /**
+     * This method performs a forward to any resource located inside the WEB-INF directory. Forwarding to
+     * publicly accessible resources can be dangerous, as the request will have already passed the URL
+     * based access control check. This method ensures that you can only forward to non-publicly
+     * accessible resources.
+     *
+     * @param response
+     * @param location the URL to forward to, including parameters
+     * @throws AccessControlException
+     * @throws ServletException
+     * @throws IOException
+     */
+    void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException;
+
+	/**
+	 * Calls setContentType with the *current* request and response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void setContentType();
+
+     /**
+	 * Set the content type character encoding header on every HttpServletResponse in order to limit
+	 * the ways in which the input data can be represented. This prevents
+	 * malicious users from using encoding and multi-byte escape sequences to
+	 * bypass input validation routines.
+     * <p/>
+	 * Implementations of this method should set the content type header to a safe value for your environment.
+     * The default is text/html; charset=UTF-8 character encoding, which is the default in early
+	 * versions of HTML and HTTP. See RFC 2047 (http://ds.internic.net/rfc/rfc2045.txt) for more
+	 * information about character encoding and MIME.
+     * <p/>
+	 * The DefaultHTTPUtilities reference implementation sets the content type as specified.
+     *
+     * @param response The servlet response to set the content type for.
+     */
+    void setContentType(HttpServletResponse response);
+
+    /**
+     * Stores the current HttpRequest and HttpResponse so that they may be readily accessed throughout
+     * ESAPI (and elsewhere)
+     *
+     * @param request  the current request
+     * @param response the current response
+     */
+    void setCurrentHTTP(HttpServletRequest request, HttpServletResponse response);
+
+
+	/**
+	 * Calls setHeader with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void setHeader(String name, String value);
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and value. "A recipient MAY replace any
+     * linear white space with a single SP before interpreting the field value
+     * or forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     *
+     * @param name
+     * @param value
+     */
+    void setHeader(HttpServletResponse response, String name, String value);
+
+
+	/**
+	 * Calls setNoCacheHeaders with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void setNoCacheHeaders();
+
+
+    /**
+     * Set headers to protect sensitive information against being cached in the browser. Developers should make this
+     * call for any HTTP responses that contain any sensitive data that should not be cached within the browser or any
+     * intermediate proxies or caches. Implementations should set headers for the expected browsers. The safest approach
+     * is to set all relevant headers to their most restrictive setting. These include:
+     * <p/>
+     * <PRE>
+     * Cache-Control: no-store<BR>
+     * Cache-Control: no-cache<BR>
+     * Cache-Control: must-revalidate<BR>
+     * Expires: -1<BR>
+     * </PRE>
+     * <p/>
+     * Note that the header "pragma: no-cache" is intended only for use in HTTP requests, not HTTP responses. However, Microsoft has chosen to
+     * directly violate the standards, so we need to include that header here. For more information, please refer to the relevant standards:
+     * <UL>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">HTTP/1.1 Cache-Control "no-cache"</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1">HTTP/1.1 Cache-Control "no-store"</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2">HTTP/1.0 Pragma "no-cache"</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32">HTTP/1.0 Expires</a>
+     * <LI><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">IE6 Caching Issues</a>
+     * <LI><a href="http://support.microsoft.com/kb/937479">Firefox browser.cache.disk_cache_ssl</a>
+     * <LI><a href="http://support.microsoft.com/kb/234067">Microsoft directly violates specification for pragma: no-cache</a>
+     * <LI><a href="http://www.mozilla.org/quality/networking/docs/netprefs.html">Mozilla</a>
+     * </UL>
+     *
+     * @param response
+     */
+    void setNoCacheHeaders(HttpServletResponse response);
+
+	/**
+	 * Calls setNoCacheHeaders with the *current* response.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    String setRememberToken(String password, int maxAge, String domain, String path);
+
+
+    /**
+	 * Set a cookie containing the current User's remember me token for automatic authentication. The use of remember me tokens
+	 * is generally not recommended, but this method will help do it as safely as possible. The user interface should strongly warn
+     * the user that this should only be enabled on computers where no other users will have access.
+     * <p/>
+     * Implementations should save the user's remember me data in an encrypted cookie and send it to the user.
+     * Any old remember me cookie should be destroyed first. Setting this cookie should keep the user
+	 * logged in until the maxAge passes, the password is changed, or the cookie is deleted.
+	 * If the cookie exists for the current user, it should automatically be used by ESAPI to
+     * log the user in, if the data is valid and not expired.
+     * <p/>
+	 * The ESAPI reference implementation, DefaultHTTPUtilities.setRememberToken() implements all these suggestions.
+     * <p/>
+     * The username can be retrieved with: User username = ESAPI.authenticator().getCurrentUser();
+     *
+     * @param request
+     * @param password the user's password
+     * @param response
+     * @param maxAge the length of time that the token should be valid for in relative seconds
+	 * @param domain the domain to restrict the token to or null
+	 * @param path the path to restrict the token to or null
+	 * @return encrypted "Remember Me" token stored as a String
+	 */
+    String setRememberToken(HttpServletRequest request, HttpServletResponse response, String password, int maxAge, String domain, String path);
+
+
+	/**
+	 * Calls verifyCSRFToken with the *current* request.
+     *
+	 * @see {@link HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse)}
+	 */
+    void verifyCSRFToken();
+
+    /**
+     * Checks the CSRF token in the URL (see User.getCSRFToken()) against the user's CSRF token and
+	 * throws an IntrusionException if it is missing.
+     *
+     * @param request
+     * @throws IntrusionException if CSRF token is missing or incorrect
+	 */
+    void verifyCSRFToken(HttpServletRequest request) throws IntrusionException;
+
+   /**
+    * Gets a typed attribute from the session associated with the calling thread. If the
+    * object referenced by the passed in key is not of the implied type, a ClassCastException
+    * will be thrown to the calling code.
+    *
+    * @param    key
+    *           The key that references the session attribute
+    * @param    <T>
+    *           The implied type of object expected.
+    * @return
+    *           The requested object.
+    * @see      #getSessionAttribute(javax.servlet.http.HttpSession, String)
+    */
+    <T> T getSessionAttribute( String key );
+
+    /**
+     * Gets a typed attribute from the passed in session. This method has the same
+     * responsibility as {link #getSessionAttribute(String} however only it references
+     * the passed in session and thus performs slightly better since it does not need
+     * to return to the Thread to get the {@link HttpSession} associated with the current
+     * thread.
+     *
+     * @param session
+     *          The session to retrieve the attribute from
+     * @param key
+     *          The key that references the requested object
+     * @param <T>
+     *          The implied type of object expected
+     * @return  The requested object
+     */
+    <T> T getSessionAttribute( HttpSession session, String key );
+
+    /**
+     * Gets a typed attribute from the {@link HttpServletRequest} associated
+     * with the caller thread. If the attribute on the request is not of the implied
+     * type, a ClassCastException will be thrown back to the caller.
+     *
+     * @param key The key that references the request attribute.
+     * @param <T> The implied type of the object expected
+     * @return The requested object
+     */
+    <T> T getRequestAttribute( String key );
+
+    /**
+     * Gets a typed attribute from the {@link HttpServletRequest} associated
+     * with the passed in request. If the attribute on the request is not of the implied
+     * type, a ClassCastException will be thrown back to the caller.
+     *
+     * @param request The request to retrieve the attribute from
+     * @param key The key that references the request attribute.
+     * @param <T> The implied type of the object expected
+     * @return The requested object
+     */
+    <T> T getRequestAttribute( HttpServletRequest request, String key );
+}
diff --git a/src/main/java/org/owasp/esapi/IntrusionDetector.java b/src/main/java/org/owasp/esapi/IntrusionDetector.java
new file mode 100644
index 0000000..57ea32b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/IntrusionDetector.java
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.IntrusionException;
+
+
+/**
+ * The IntrusionDetector interface is intended to track security relevant events and identify attack behavior. The
+ * implementation can use as much state as necessary to detect attacks, but note that storing too much state will burden
+ * your system.
+ * <P>
+ * The interface is currently designed to accept exceptions as well as custom events. Implementations can use this
+ * stream of information to detect both normal and abnormal behavior.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface IntrusionDetector {
+
+    /**
+     * Adds the exception to the IntrusionDetector.  This method should immediately log the exception so that developers throwing an 
+     * IntrusionException do not have to remember to log every error.  The implementation should store the exception somewhere for the current user
+     * in order to check if the User has reached the threshold for any Enterprise Security Exceptions.  The User object is the recommended location for storing
+     * the current user's security exceptions.  If the User has reached any security thresholds, the appropriate security action can be taken and logged.
+     * 
+     * @param exception 
+     * 		the exception thrown
+     * 
+     * @throws IntrusionException 
+     * 		the intrusion exception
+     */
+    void addException(Exception exception) throws IntrusionException;
+
+    /**
+     * Adds the event to the IntrusionDetector.  This method should immediately log the event.  The implementation should store the event somewhere for the current user
+     * in order to check if the User has reached the threshold for any Enterprise Security Exceptions.  The User object is the recommended location for storing
+     * the current user's security event.  If the User has reached any security thresholds, the appropriate security action can be taken and logged.
+     * 
+     * @param eventName 
+     * 		the event to add
+     * @param logMessage 
+     * 		the message to log with the event
+     * 
+     * @throws IntrusionException 
+     * 		the intrusion exception
+     */
+    void addEvent(String eventName, String logMessage) throws IntrusionException;
+
+}
diff --git a/src/main/java/org/owasp/esapi/LogFactory.java b/src/main/java/org/owasp/esapi/LogFactory.java
new file mode 100644
index 0000000..e373698
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/LogFactory.java
@@ -0,0 +1,60 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Rogan Dawes<a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2008
+ */
+package org.owasp.esapi;
+
+/**
+ * The LogFactory interface is intended to allow substitution of various logging packages, while providing
+ * a common interface to access them.
+ * 
+ * In the reference implementation, JavaLogFactory.java implements this interface.  JavaLogFactory.java also contains an 
+ * inner class called JavaLogger which implements Logger.java and uses the Java logging package to log events. 
+ * 
+ * @see org.owasp.esapi.ESAPI
+ * 
+ * @author rdawes
+ *
+ */
+public interface LogFactory {
+	
+	/**
+	 * Gets the logger associated with the specified module name. The module name is used by the logger to log which 
+	 * module is generating the log events. The implementation of this method should return any preexisting Logger 
+	 * associated with this module name, rather than creating a new Logger.
+	 * <br><br>
+	 * The JavaLogFactory reference implementation meets these requirements.
+	 * 
+	 * @param moduleName
+	 * 			The name of the module requesting the logger.
+	 * @return
+	 * 			The Logger associated with this module.
+	 */
+	Logger getLogger(String moduleName);
+	
+	/**
+	 * Gets the logger associated with the specified class. The class is used by the logger to log which 
+	 * class is generating the log events. The implementation of this method should return any preexisting Logger 
+	 * associated with this class name, rather than creating a new Logger.
+	 * <br><br>
+	 * The JavaLogFactory reference implementation meets these requirements.
+	 * 
+	 * @param clazz
+	 * 			The name of the class requesting the logger.
+	 * @return
+	 * 			The Logger associated with this class.
+	 */
+	Logger getLogger(Class clazz);
+	
+}
diff --git a/src/main/java/org/owasp/esapi/Logger.java b/src/main/java/org/owasp/esapi/Logger.java
new file mode 100644
index 0000000..0bd9517
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/Logger.java
@@ -0,0 +1,423 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+
+/**
+ * The Logger interface defines a set of methods that can be used to log
+ * security events. It supports a hierarchy of logging levels which can be configured at runtime to determine
+ * the severity of events that are logged, and those below the current threshold that are discarded.
+ * Implementors should use a well established logging library
+ * as it is quite difficult to create a high-performance logger.
+ * <P>
+ * The logging levels defined by this interface (in descending order) are:
+ * <ul>
+ * <li>fatal (highest value)</li>
+ * <li>error</li>
+ * <li>warning</li>
+ * <li>info</li>
+ * <li>debug</li>
+ * <li>trace (lowest value)</li>
+ * </ul>
+ * There are also several variations of {@code always()} methods that will <i>always</i>
+ * log a message regardless of the log level.
+ * <p>
+ * ESAPI also allows for the definition of the type of log event that is being generated.
+ * The Logger interface predefines 6 types of Log events:
+ * <ul>
+ * <li>SECURITY_SUCCESS</li>
+ * <li>SECURITY_FAILURE</li>
+ * <li>SECURITY_AUDIT</li>
+ * <li>EVENT_SUCCESS</li>
+ * <li>EVENT_FAILURE</li>
+ * <li>EVENT_UNSPECIFIED</li>
+ * </ul>
+ * <p>
+ * Your implementation can extend or change this list if desired. 
+ * <p>
+ * This Logger allows callers to determine which logging levels are enabled, and to submit events 
+ * at different severity levels.<br>
+ * <br>Implementors of this interface should:
+ * 
+ * <ol>
+ * <li>provide a mechanism for setting the logging level threshold that is currently enabled. This usually works by logging all 
+ * events at and above that severity level, and discarding all events below that level.
+ * This is usually done via configuration, but can also be made accessible programmatically.</li>
+ * <li>ensure that dangerous HTML characters are encoded before they are logged to defend against malicious injection into logs 
+ * that might be viewed in an HTML based log viewer.</li>
+ * <li>encode any CRLF characters included in log data in order to prevent log injection attacks.</li>
+ * <li>avoid logging the user's session ID. Rather, they should log something equivalent like a 
+ * generated logging session ID, or a hashed value of the session ID so they can track session specific 
+ * events without risking the exposure of a live session's ID.</li> 
+ * <li>record the following information with each event:</li>
+ *   <ol type="a">
+ *   <li>identity of the user that caused the event,</li>
+ *   <li>a description of the event (supplied by the caller),</li>
+ *   <li>whether the event succeeded or failed (indicated by the caller),</li>
+ *   <li>severity level of the event (indicated by the caller),</li>
+ *   <li>that this is a security relevant event (indicated by the caller),</li>
+ *   <li>hostname or IP where the event occurred (and ideally the user's source IP as well),</li>
+ *   <li>a time stamp</li>
+ *   </ol>
+ * </ol>
+ *  
+ * Custom logger implementations might also:
+ * <ol start="6">
+ * <li>filter out any sensitive data specific to the current application or organization, such as credit cards, 
+ * social security numbers, etc.</li>
+ * </ol>
+ * 
+ * There are both Log4j and native Java Logging default implementations. JavaLogger uses the java.util.logging package as the basis for its logging 
+ * implementation. Both default implementations implements requirements #1 thru #5 above.<br>
+ * <br>
+ * Customization: It is expected that most organizations will implement their own custom Logger class in 
+ * order to integrate ESAPI logging with their logging infrastructure. The ESAPI Reference Implementation 
+ * is intended to provide a simple functional example of an implementation.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ * href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Logger {
+
+	/**
+     * A security type of log event that has succeeded. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType SECURITY_SUCCESS = new EventType( "SECURITY SUCCESS", true);
+
+	/**
+     * A security type of log event that has failed. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType SECURITY_FAILURE = new EventType( "SECURITY FAILURE", false);
+
+	/**
+	 * A security type of log event that is associated with an audit trail of some type,
+	 * but the log event is not specifically something that has either succeeded or failed
+	 * or that is irrelevant in the case of this logged message.
+	 */
+	// CHECKME: Should the Boolean for this be 'null' or 'true'? See EVENT_UNSPECIFIED.
+	public static final EventType SECURITY_AUDIT = new EventType( "SECURITY AUDIT", null);
+
+	/**
+     * A non-security type of log event that has succeeded. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType EVENT_SUCCESS = new EventType( "EVENT SUCCESS", true);
+	
+	/**
+     * A non-security type of log event that has failed. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType EVENT_FAILURE = new EventType( "EVENT FAILURE", false);
+
+	/**
+     * A non-security type of log event that is unspecified. This is one of 6 predefined
+     * ESAPI logging events. New events can be added.
+     */
+	public static final EventType EVENT_UNSPECIFIED = new EventType( "EVENT UNSPECIFIED", null);
+
+	/**
+	 * Defines the type of log event that is being generated. The Logger interface defines 6 types of Log events:
+	 * SECURITY_SUCCESS, SECURITY_FAILURE, EVENT_SUCCESS, EVENT_FAILURE, EVENT_UNSPECIFIED.
+     * Your implementation can extend or change this list if desired. 
+	 */
+	public class EventType {
+		
+		private String type;
+		private Boolean success = null;
+		
+		public EventType (String name, Boolean newSuccess) {
+			this.type = name;
+			this.success = newSuccess;
+		}
+		
+		public Boolean isSuccess() {
+			return success;
+		}
+		
+        /**
+         * Convert the {@code EventType} to a string.
+         * @return The event type name.
+         */
+		@Override
+        public String toString() {
+			return this.type;
+		}
+	}
+	
+	/*
+     * The Logger interface defines 6 logging levels: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE. It also 
+     * supports ALL, which logs all events, and OFF, which disables all logging.
+     * Your implementation can extend or change this list if desired. 
+     */
+	
+	/** OFF indicates that no messages should be logged. This level is initialized to Integer.MAX_VALUE. */
+	public static final int OFF = Integer.MAX_VALUE;
+
+	/** FATAL indicates that only FATAL messages should be logged. This level is initialized to 1000. */
+	public static final int FATAL = 1000;
+
+	/** ERROR indicates that ERROR messages and above should be logged. 
+	 * This level is initialized to 800. */
+    public static final int ERROR = 800;
+
+    /** WARNING indicates that WARNING messages and above should be logged. 
+     * This level is initialized to 600. */
+    public static final int WARNING = 600;
+
+    /** INFO indicates that INFO messages and above should be logged. 
+     * This level is initialized to 400. */
+    public static final int INFO = 400;
+
+    /** DEBUG indicates that DEBUG messages and above should be logged. 
+     * This level is initialized to 200. */
+    public static final int DEBUG = 200;
+
+    /** TRACE indicates that TRACE messages and above should be logged. 
+     * This level is initialized to 100. */
+    public static final int TRACE = 100;
+
+    /** ALL indicates that all messages should be logged. This level is initialized to Integer.MIN_VALUE. */
+    public static final int ALL = Integer.MIN_VALUE;
+    
+
+    /**
+     * Dynamically set the ESAPI logging severity level. All events of this level and higher will be logged from 
+     * this point forward for all logs. All events below this level will be discarded.
+     * 
+     * @param level The level to set the logging level to. 
+     */
+    void setLevel(int level);
+    
+    /** Retrieve the current ESAPI logging level for this logger. See
+     * {@link org.owasp.esapi.reference.Log4JLogger} for an explanation of
+     * why this method is not simply called {@code getLevel()}.
+     * 
+     * @return The current logging level.
+     */
+    int getESAPILevel();
+    
+	/**
+     * Log a fatal event if 'fatal' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void fatal(EventType type, String message);
+	
+	/**
+     * Log a fatal level security event if 'fatal' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void fatal(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if fatal level messages will be output to the log
+	 */
+	boolean isFatalEnabled();
+
+	/**
+     * Log an error level security event if 'error' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     */
+	void error(EventType type, String message);
+	
+	/**
+     * Log an error level security event if 'error' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void error(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if error level messages will be output to the log
+	 */
+	boolean isErrorEnabled();
+
+	/**
+     * Log a warning level security event if 'warning' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     */
+	void warning(EventType type, String message);
+	
+	/**
+     * Log a warning level security event if 'warning' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void warning(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if warning level messages will be output to the log
+	 */
+	boolean isWarningEnabled();
+
+	/**
+     * Log an info level security event if 'info' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     */
+	void info(EventType type, String message);
+	
+	/**
+     * Log an info level security event if 'info' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void info(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if info level messages will be output to the log
+	 */
+	boolean isInfoEnabled();
+
+	/**
+     * Log a debug level security event if 'debug' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void debug(EventType type, String message);
+	
+	/**
+     * Log a debug level security event if 'debug' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void debug(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if debug level messages will be output to the log
+	 */
+	boolean isDebugEnabled();
+
+	/**
+     * Log a trace level security event if 'trace' level logging is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void trace(EventType type, String message);
+	
+	/**
+     * Log a trace level security event if 'trace' level logging is enabled 
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void trace(EventType type, String message, Throwable throwable);
+
+	/**
+	 * Allows the caller to determine if messages logged at this level
+	 * will be discarded, to avoid performing expensive processing.
+	 * 
+	 * @return true if trace level messages will be output to the log
+	 */
+	boolean isTraceEnabled();
+
+	/**
+     * Log an event regardless of what logging level is enabled.
+     * 
+     * @param type 
+     * 		the type of event
+     * @param message 
+     * 		the message to log
+     */
+	void always(EventType type, String message);
+	
+	/**
+     * Log an event regardless of what logging level is enabled
+     * and also record the stack trace associated with the event.
+     * 
+     * @param type 
+     * 		the type of event 
+     * @param message 
+     * 		the message to log
+     * @param throwable 
+     * 		the exception to be logged
+     */
+	void always(EventType type, String message, Throwable throwable);
+}
diff --git a/src/main/java/org/owasp/esapi/PreparedString.java b/src/main/java/org/owasp/esapi/PreparedString.java
new file mode 100644
index 0000000..4e300be
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/PreparedString.java
@@ -0,0 +1,139 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi;
+
+import java.util.ArrayList;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.HTMLEntityCodec;
+
+
+/**
+ * A parameterized string that uses escaping to make untrusted data safe before combining it with
+ * a command or query intended for use in an interpreter.
+ * <pre> 
+ * PreparedString div = new PreparedString( "<a href=\"http:\\\\example.com?id=?\" onmouseover=\"alert('?')\">test</a>", new HTMLEntityCodec() );
+ * div.setURL( 1, request.getParameter( "url" ), new PercentCodec() );
+ * div.set( 2, request.getParameter( "message" ), new JavaScriptCodec() );
+ * out.println( div.toString() );
+ * 
+ * // escaping for SQL
+ * PreparedString query = new PreparedString( "SELECT * FROM users WHERE name='?' AND password='?'", new OracleCodec() );
+ * query.set( 1, request.getParameter( "name" ) );
+ * query.set( 2, request.getParameter( "pass" ) );
+ * stmt.execute( query.toString() );
+ * </pre>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public class PreparedString {
+	char parameterCharacter = '?';
+	Codec codec = null;
+	String[] parameters = null;
+	ArrayList parts = new ArrayList();
+	private final static char[] IMMUNE = {};
+
+	/**
+	 * Create a PreparedString with the supplied template and Codec. The template should use the 
+	 * default parameter placeholder character (?) in the place where actual parameters are to be inserted.
+	 * The supplied Codec will be used to escape characters in calls to set, unless a specific Codec is
+	 * provided to override it.
+	 * @param template
+	 * @param codec
+	 */
+	public PreparedString( String template, Codec codec ) {
+		this.codec = codec;
+		split( template, parameterCharacter );
+	}
+
+	/**
+	 * Create a PreparedString with the supplied template, parameter placeholder character, and Codec. The parameter character
+	 * can be any character, but should not be one that will be used in the template. The parameter character can safely
+	 * be used in a parameter passed into the set methods.
+	 * @param template
+	 * @param parameterCharacter
+	 * @param codec
+	 */
+	public PreparedString( String template, char parameterCharacter, Codec codec ) {
+		this.codec = codec;
+		this.parameterCharacter = parameterCharacter;
+		split( template, parameterCharacter );
+	}
+
+	/**
+	 * Split a string with a particular character.
+	 * @param str
+	 * @param c
+	 */
+	private void split( String str, char c ) {
+		int index = 0;
+		int pcount = 0;
+		for ( int i = 0; i < str.length(); i++ ) {
+			if ( str.charAt(i) == c ) {
+				pcount++;
+				parts.add( str.substring(index,i) );
+				index = i + 1;
+			}
+		}
+		parts.add( str.substring(index) );
+		parameters = new String[pcount];
+	}
+	
+	/**
+	 * Set the parameter at index with supplied value using the default Codec to escape. 
+	 * @param index
+	 * @param value
+	 */
+	public void set( int index, String value ) {
+		if ( index < 1 || index > parameters.length ) {
+			throw new IllegalArgumentException( "Attempt to set parameter " + index + " on a PreparedString with only " + parameters.length + " placeholders" );
+		}
+		String encoded = codec.encode( IMMUNE, value );
+		parameters[index-1] = encoded;
+	}
+	
+	/**
+	 * Set the parameter at index with supplied value using the supplied Codec to escape. 
+	 * @param index
+	 * @param value
+	 * @param codec
+	 */
+	public void set( int index, String value, Codec codec ) {
+		if ( index < 1 || index > parameters.length ) {
+			throw new IllegalArgumentException( "Attempt to set parameter " + index + " on a PreparedString with only " + parameters.length + " placeholders" );
+		}
+		String encoded = codec.encode( IMMUNE, value );
+		parameters[index-1] = encoded;
+	}
+	
+	/**
+	 * Render the PreparedString by combining the template with properly escaped parameters.
+	 */
+	public String toString() {
+		for ( int ix = 0; ix < parameters.length; ix++ ) {
+			if ( parameters[ix] == null ) {
+				throw new RuntimeException( "Attempt to render PreparedString without setting parameter " + ( ix + 1 ));
+			}
+		}
+		StringBuilder sb = new StringBuilder();
+		int i = 0;
+		for ( int p=0; p < parts.size(); p++ ) {
+			sb.append( parts.get( p ) );
+			if ( i < parameters.length ) sb.append( parameters[i++] );
+		}
+		return sb.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/Randomizer.java b/src/main/java/org/owasp/esapi/Randomizer.java
new file mode 100644
index 0000000..17532c3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/Randomizer.java
@@ -0,0 +1,148 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The Randomizer interface defines a set of methods for creating
+ * cryptographically random numbers and strings. Implementers should be sure to
+ * use a strong cryptographic implementation, such as the JCE or BouncyCastle.
+ * Weak sources of randomness can undermine a wide variety of security
+ * mechanisms. The specific algorithm used is configurable in ESAPI.properties.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Randomizer {
+
+	/**
+	 * Gets a random string of a desired length and character set.  The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @param length 
+	 * 		the length of the string
+	 * @param characterSet 
+	 * 		the set of characters to include in the created random string
+	 * 
+	 * @return 
+	 * 		the random string of the desired length and character set
+	 */
+	String getRandomString(int length, char[] characterSet);
+
+	/**
+	 * Returns a random boolean.  The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @return 
+	 * 		true or false, randomly
+	 */
+	boolean getRandomBoolean();
+	
+	/**
+	 * Gets the random integer. The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @param min 
+	 * 		the minimum integer that will be returned
+	 * @param max 
+	 * 		the maximum integer that will be returned
+	 * 
+	 * @return 
+	 * 		the random integer
+	 */
+	int getRandomInteger(int min, int max);
+
+	
+	/**
+	 * Gets the random long. The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @return 
+	 * 		the random long
+	 */
+    public long getRandomLong();
+	
+	
+    /**
+     * Returns an unguessable random filename with the specified extension.  This method could call
+     * getRandomString(length, charset) from this Class with the desired length and alphanumerics as the charset 
+     * then merely append "." + extension.
+     * 
+     * @param extension 
+     * 		extension to add to the random filename
+     * 
+     * @return 
+     * 		a random unguessable filename ending with the specified extension
+     */
+    public String getRandomFilename( String extension );
+    
+    
+	/**
+	 * Gets the random real.  The use of java.security.SecureRandom
+	 * is recommended because it provides a cryptographically strong pseudo-random number generator. 
+	 * If SecureRandom is not used, the pseudo-random number gernerator used should comply with the 
+	 * statistical random number generator tests specified in <a href="http://csrc.nist.gov/cryptval/140-2.htm">
+	 * FIPS 140-2, Security Requirements for Cryptographic Modules</a>, section 4.9.1.
+	 * 
+	 * @param min 
+	 * 		the minimum real number that will be returned
+	 * @param max 
+	 * 		the maximum real number that will be returned
+	 * 
+	 * @return 
+	 * 		the random real
+	 */
+	float getRandomReal(float min, float max);
+
+    /**
+     * Generates a random GUID.  This method could use a hash of random Strings, the current time,
+     * and any other random data available.  The format is a well-defined sequence of 32 hex digits 
+     * grouped into chunks of 8-4-4-4-12.
+     * <p>
+     * For more information including algorithms used to create <tt>UUID</tt>s,
+     * see the Internet-Draft <a href="http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-03.txt">UUIDs and GUIDs</a>
+     * or the standards body definition at <a href="http://www.iso.ch/cate/d2229.html">ISO/IEC 11578:1996</a>.
+     * @return 
+     * 		the GUID
+     * 
+     * @throws 
+     * 		EncryptionException if hashing or encryption fails 
+     */
+    String getRandomGUID() throws EncryptionException;
+           
+    /**
+     * Generates a specified number of random bytes.
+     * @param n	The requested number of random bytes.
+     * @return The {@code n} random bytes are returned.
+     */
+    public byte[] getRandomBytes(int n);
+           
+}
diff --git a/src/main/java/org/owasp/esapi/SafeFile.java b/src/main/java/org/owasp/esapi/SafeFile.java
new file mode 100644
index 0000000..73643c0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/SafeFile.java
@@ -0,0 +1,107 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2008 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2008
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * Extension to java.io.File to prevent against null byte injections and
+ * other unforeseen problems resulting from unprintable characters
+ * causing problems in path lookups. This does _not_ prevent against
+ * directory traversal attacks.
+ */
+public class SafeFile extends File {
+
+	private static final long serialVersionUID = 1L;
+	private static final Pattern PERCENTS_PAT = Pattern.compile("(%)([0-9a-fA-F])([0-9a-fA-F])");	
+	private static final Pattern FILE_BLACKLIST_PAT = Pattern.compile("([\\\\/:*?<>|])");	
+	private static final Pattern DIR_BLACKLIST_PAT = Pattern.compile("([*?<>|])");
+
+	public SafeFile(String path) throws ValidationException {
+		super(path);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	public SafeFile(String parent, String child) throws ValidationException {
+		super(parent, child);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	public SafeFile(File parent, String child) throws ValidationException {
+		super(parent, child);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	public SafeFile(URI uri) throws ValidationException {
+		super(uri);
+		doDirCheck(this.getParent());
+		doFileCheck(this.getName());
+	}
+
+	
+	private void doDirCheck(String path) throws ValidationException {
+		Matcher m1 = DIR_BLACKLIST_PAT.matcher( path );
+		if ( m1.find() ) {
+			throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains illegal character: " + m1.group() );
+		}
+
+		Matcher m2 = PERCENTS_PAT.matcher( path );
+		if ( m2.find() ) {
+			throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains encoded characters: " + m2.group() );
+		}
+		
+		int ch = containsUnprintableCharacters(path);
+		if (ch != -1) {
+			throw new ValidationException("Invalid directory", "Directory path (" + path + ") contains unprintable character: " + ch);
+		}
+	}
+	
+	private void doFileCheck(String path) throws ValidationException {
+		Matcher m1 = FILE_BLACKLIST_PAT.matcher( path );
+		if ( m1.find() ) {
+			throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains illegal character: " + m1.group() );
+		}
+
+		Matcher m2 = PERCENTS_PAT.matcher( path );
+		if ( m2.find() ) {
+			throw new ValidationException( "Invalid file", "File path (" + path + ") contains encoded characters: " + m2.group() );
+		}
+		
+		int ch = containsUnprintableCharacters(path);
+		if (ch != -1) {
+			throw new ValidationException("Invalid file", "File path (" + path + ") contains unprintable character: " + ch);
+		}
+	}
+
+	private int containsUnprintableCharacters(String s) {
+		for (int i = 0; i < s.length(); i++) {
+			char ch = s.charAt(i);
+			if (((int) ch) < 32 || ((int) ch) > 126) {
+				return (int) ch;
+			}
+		}
+		return -1;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/SecurityConfiguration.java b/src/main/java/org/owasp/esapi/SecurityConfiguration.java
new file mode 100644
index 0000000..9c54acb
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/SecurityConfiguration.java
@@ -0,0 +1,682 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Mike Fauzy <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * The {@code SecurityConfiguration} interface stores all configuration information
+ * that directs the behavior of the ESAPI implementation.
+ * <br><br>
+ * Protection of this configuration information is critical to the secure
+ * operation of the application using the ESAPI. You should use operating system
+ * access controls to limit access to wherever the configuration information is
+ * stored.
+ * <br><br>
+ * Please note that adding another layer of encryption does not make the
+ * attackers job much more difficult. Somewhere there must be a master "secret"
+ * that is stored unencrypted on the application platform (unless you are
+ * willing to prompt for some passphrase when you application starts or insert
+ * a USB thumb drive or an HSM card, etc., in which case this master "secret"
+ * it would only be in memory). Creating another layer of indirection provides
+ * additional obfuscation, but doesn't provide any real additional security.
+ * It's up to the reference implementation to decide whether this file should
+ * be encrypted or not.
+ * <br><br>
+ * The ESAPI reference implementation (DefaultSecurityConfiguration.java) does
+ * <i>not</i> encrypt its properties file.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface SecurityConfiguration {
+
+	/**
+	 * Gets the application name, used for logging
+	 * 
+	 * @return the name of the current application
+	 */
+	public String getApplicationName();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Logging implementation.
+	 */
+	public String getLogImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Authentication implementation.
+	 */
+	public String getAuthenticationImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Encoder implementation.
+	 */
+	public String getEncoderImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Access Control implementation.
+	 */
+	public String getAccessControlImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Intrusion Detection implementation.
+	 */
+	public String getIntrusionDetectionImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Randomizer implementation.
+	 */
+	public String getRandomizerImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Encryption implementation.
+	 */
+	public String getEncryptionImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI Validation implementation.
+	 */
+	public String getValidationImplementation();
+	
+	/**
+	 * Returns the validation pattern for a particular type
+	 * @param typeName
+	 * @return the validation pattern
+	 */
+    public Pattern getValidationPattern( String typeName );
+    
+    /**
+     * Determines whether ESAPI will accept "lenient" dates when attempt
+     * to parse dates. Controlled by ESAPI property
+     * {@code Validator.AcceptLenientDates}, which defaults to {@code false}
+     * if unset.
+     * 
+     * @return True if lenient dates are accepted; false otherwise.
+     * @see java.text.DateFormat#setLenient(boolean)
+     */
+    public boolean getLenientDatesAccepted();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI OS Execution implementation.
+	 */
+	public String getExecutorImplementation();
+	
+	/**
+	 * Returns the fully qualified classname of the ESAPI HTTPUtilities implementation.
+	 */
+	public String getHTTPUtilitiesImplementation();
+	
+	/**
+	 * Gets the master key. This password is used to encrypt/decrypt other files or types
+	 * of data that need to be protected by your application.
+	 * 
+	 * @return the current master key
+	 */
+	public byte[] getMasterKey();
+
+	/**
+     * Retrieves the upload directory as specified in the ESAPI.properties file.
+     * @return the upload directory
+     */
+    public File getUploadDirectory();
+	
+    /**
+     * Retrieves the temp directory to use when uploading files, as specified in ESAPI.properties.
+     * @return the temp directory
+     */
+    public File getUploadTempDirectory();
+
+	/**
+	 * Gets the key length to use in cryptographic operations declared in the ESAPI properties file.
+	 * 
+	 * @return the key length.
+	 */
+    public int getEncryptionKeyLength();
+    
+	/**
+	 * Gets the master salt that is used to salt stored password hashes and any other location 
+	 * where a salt is needed.
+	 * 
+	 * @return the current master salt
+	 */
+	public byte[] getMasterSalt();
+
+	/**
+	 * Gets the allowed executables to run with the Executor.
+	 * 
+	 * @return a list of the current allowed file extensions
+	 */
+	public List<String> getAllowedExecutables();
+
+	/**
+	 * Gets the allowed file extensions for files that are uploaded to this application.
+	 * 
+	 * @return a list of the current allowed file extensions
+	 */
+	public List<String> getAllowedFileExtensions();
+
+	/**
+	 * Gets the maximum allowed file upload size.
+	 * 
+	 * @return the current allowed file upload size
+	 */
+	public int getAllowedFileUploadSize();
+
+	/**
+	 * Gets the name of the password parameter used during user authentication.
+	 * 
+	 * @return the name of the password parameter
+	 */
+	public String getPasswordParameterName();
+
+	/**
+	 * Gets the name of the username parameter used during user authentication.
+	 * 
+	 * @return the name of the username parameter
+	 */
+	public String getUsernameParameterName();
+
+	/**
+	 * Gets the encryption algorithm used by ESAPI to protect data. This is
+	 * mostly used for compatibility with ESAPI 1.4; ESAPI 2.0 prefers to
+	 * use "cipher transformation" since it supports multiple cipher modes
+	 * and padding schemes.
+	 * 
+	 * @return the current encryption algorithm
+	 */
+	public String getEncryptionAlgorithm();
+
+	/**
+	 * Retrieve the <i>cipher transformation</i>. In general, the cipher transformation
+	 * is a specification of cipher algorithm, cipher mode, and padding scheme
+	 * and in general, is a {@code String} that takes the following form:
+	 * <pre>
+	 * 		<i>cipher_alg</i>/<i>cipher_mode[bits]</i>/<i>padding_scheme</i>
+	 * </pre>
+	 * where <i>cipher_alg</i> is the JCE cipher algorithm (e.g., "DESede"),
+	 * <i>cipher_mode</i> is the cipher mode (e.g., "CBC", "CFB", "CTR", etc.),
+	 * and <i>padding_scheme</i> is the cipher padding scheme (e.g., "NONE" for
+	 * no padding, "PKCS5Padding" for PKCS#5 padding, etc.) and where
+	 * <i>[bits]</i> is an optional bit size that applies to certain cipher
+	 * modes such as {@code CFB} and {@code OFB}. Using modes such as CFB and
+	 * OFB, block ciphers can encrypt data in units smaller than the cipher's
+	 * actual block size. When requesting such a mode, you may optionally
+	 * specify the number of bits to be processed at a time. This generally must
+	 * be an integral multiple of 8-bits so that it can specify a whole number
+	 * of octets. 
+	 * </p><p>
+	 * Examples are:
+	 * <pre>
+	 * 		"AES/ECB/NoPadding"		// Default for ESAPI Java 1.4 (insecure)
+	 * 		"AES/CBC/PKCS5Padding"	// Default for ESAPI Java 2.0
+	 * 		"DESede/OFB32/PKCS5Padding"
+	 * </pre>
+	 * <b>NOTE:</b> Occasionally, in cryptographic literature, you may also
+	 * see the key size (in bits) specified after the cipher algorithm in the
+	 * cipher transformation. Generally, this is done to account for cipher
+	 * algorithms that have variable key sizes. The Blowfish cipher for example
+	 * supports key sizes from 32 to 448 bits. So for Blowfish, you might see
+	 * a cipher transformation something like this:
+	 * <pre>
+	 * 		"Blowfish-192/CFB8/PKCS5Padding"
+	 * </pre>
+	 * in the cryptographic literature. It should be noted that the Java
+	 * Cryptography Extensions (JCE) do not generally support this (at least
+	 * not the reference JCE implementation of "SunJCE"), and therefore it
+	 * should be avoided.
+	 * @return	The cipher transformation.
+	 */
+    public String getCipherTransformation();
+    
+    /**
+     * Set the cipher transformation. This allows a different cipher transformation
+     * to be used without changing the {@code ESAPI.properties} file. For instance
+     * you may normally want to use AES/CBC/PKCS5Padding, but have some legacy
+     * encryption where you have ciphertext that was encrypted using 3DES.
+     * 
+     * @param cipherXform	The new cipher transformation. See
+     * 						{@link #getCipherTransformation} for format. If
+     * 						{@code null} is passed as the parameter, the cipher
+     * 						transformation will be set to the the default taken
+     * 						from the property {@code Encryptor.CipherTransformation}
+     * 						in the {@code ESAPI.properties} file. <b>BEWARE:</b>
+     * 						there is <b>NO</b> sanity checking here (other than
+     * 						the empty string, and then, only if Java assertions are
+     * 						enabled), so if you set this wrong, you will not get
+     * 						any errors until you later try to use it to encrypt
+     * 						or decrypt data.
+     * @return	The previous cipher transformation is returned for convenience,
+     * 			with the assumption that you may wish to restore it once you have
+     * 			completed the encryption / decryption with the new cipher
+     * 			transformation.
+     * @deprecated To be replaced by new class in ESAPI 2.1, but here if you need it
+     *          until then. Details of replacement forthcoming to ESAPI-Dev list.
+     */
+    @Deprecated
+    public String setCipherTransformation(String cipherXform);
+
+    /**
+     * Retrieve the <i>preferred</i> JCE provider for ESAPI and your application.
+     * ESAPI 2.0 now allows setting the property
+     * {@code Encryptor.PreferredJCEProvider} in the
+     * {@code ESAPI.properties} file, which will cause the specified JCE
+     * provider to be automatically and dynamically loaded (assuming that
+     * {@code SecurityManager} permissions allow) as the Ii>preferred</i>
+     * JCE provider. (Note this only happens if the JCE provider is not already
+     * loaded.) This method returns the property {@code Encryptor.PreferredJCEProvider}.
+     * </p<p>
+     * By default, this {@code Encryptor.PreferredJCEProvider} property is set
+     * to an empty string, which means that the preferred JCE provider is not
+     * changed.
+     * @return The property {@code Encryptor.PreferredJCEProvider} is returned.
+     * @see org.owasp.esapi.crypto.SecurityProviderLoader
+     */
+    public String getPreferredJCEProvider();
+    
+// TODO - DISCUSS: Where should this web page (below) go? Maybe with the Javadoc? But where?
+//				   Think it makes more sense as part of the release notes, but OTOH, I
+//				   really don't want to rewrite this as a Wiki page either.
+    /**
+     * Determines whether the {@code CipherText} should be used with a Message
+     * Authentication Code (MAC). Generally this makes for a more robust cryptographic
+     * scheme, but there are some minor performance implications. Controlled by
+     * the ESAPI property <i>Encryptor.CipherText.useMAC</i>.
+     * </p><p>
+     * For further details, see the "Advanced Usage" section of
+     * <a href="http://www.owasp.org/ESAPI_2.0_ReleaseNotes_CryptoChanges.html">
+     * "Why Is OWASP Changing ESAPI Encryption?"</a>.
+     * </p>
+     * @return	{@code true} if a you want a MAC to be used, otherwise {@code false}.
+     */
+    public boolean useMACforCipherText();
+
+    /**
+     * Indicates whether the {@code PlainText} objects may be overwritten after
+     * they have been encrypted. Generally this is a good idea, especially if
+     * your VM is shared by multiple applications (e.g., multiple applications
+     * running in the same J2EE container) or if there is a possibility that
+     * your VM may leave a core dump (say because it is running non-native
+     * Java code.
+     * <p>
+     * Controlled by the property {@code Encryptor.PlainText.overwrite} in
+     * the {@code ESAPI.properties} file.
+     * </p>
+     * @return	True if it is OK to overwrite the {@code PlainText} objects
+     *			after encrypting, false otherwise.
+     */
+    public boolean overwritePlainText();
+    
+    /**
+     * Get a string indicating how to compute an Initialization Vector (IV).
+     * Currently supported modes are "random" to generate a random IV or
+     * "fixed" to use a fixed (static) IV. If a "fixed" IV is chosen, then the
+     * the value of this fixed IV must be specified as the property
+     * {@code Encryptor.fixedIV} and be of the appropriate length.
+     * 
+     * @return A string specifying the IV type. Should be "random" or "fixed".
+     * 
+     * @see #getFixedIV()
+     */
+    public String getIVType();
+    
+    /**
+     * If a "fixed" (i.e., static) Initialization Vector (IV) is to be used,
+     * this will return the IV value as a hex-encoded string.
+     * @return The fixed IV as a hex-encoded string.
+     */
+    public String getFixedIV();
+    
+    /**
+     * Return a {@code List} of strings of combined cipher modes that support
+     * <b>both</b> confidentiality and authenticity. These would be preferred
+     * cipher modes to use if your JCE provider supports them. If such a
+     * cipher mode is used, no explicit <i>separate</i> MAC is calculated as part of
+     * the {@code CipherText} object upon encryption nor is any attempt made
+     * to verify the same on decryption.
+     * </p><p>
+     * The list is taken from the comma-separated list of cipher modes specified
+     * by the ESAPI property
+     * {@code Encryptor.cipher_modes.combined_modes}.
+     * 
+     * @return The parsed list of comma-separated cipher modes if the property
+     * was specified in {@code ESAPI.properties}; otherwise the empty list is
+     * returned.
+     */
+    public List<String> getCombinedCipherModes();
+
+    /**
+     * Return {@code List} of strings of additional cipher modes that are
+     * permitted (i.e., in <i>addition<i> to those returned by
+     * {@link #getPreferredCipherModes()}) to be used for encryption and
+     * decryption operations.
+     * </p><p>
+     * The list is taken from the comma-separated list of cipher modes specified
+     * by the ESAPI property
+     * {@code Encryptor.cipher_modes.additional_allowed}.
+     * 
+     * @return The parsed list of comma-separated cipher modes if the property
+     * was specified in {@code ESAPI.properties}; otherwise the empty list is
+     * returned.
+     *
+     * @see #getPreferredCipherModes() 
+     */
+    public List<String> getAdditionalAllowedCipherModes();
+
+	/**
+	 * Gets the hashing algorithm used by ESAPI to hash data.
+	 * 
+	 * @return the current hashing algorithm
+	 */
+	public String getHashAlgorithm();
+
+	/**
+	 * Gets the hash iterations used by ESAPI to hash data.
+	 * 
+	 * @return the current hashing algorithm
+	 */
+	public int getHashIterations();
+
+	/**
+	 * Retrieve the Pseudo Random Function (PRF) used by the ESAPI
+	 * Key Derivation Function (KDF).
+	 * 
+	 * @return	The KDF PRF algorithm name.
+	 */
+	public String getKDFPseudoRandomFunction();
+	
+	/**
+	 * Gets the character encoding scheme supported by this application. This is used to set the
+	 * character encoding scheme on requests and responses when setCharacterEncoding() is called
+	 * on SafeRequests and SafeResponses. This scheme is also used for encoding/decoding URLs 
+	 * and any other place where the current encoding scheme needs to be known.
+	 * <br><br>
+	 * Note: This does not get the configured response content type. That is accessed by calling 
+	 * getResponseContentType().
+	 * 
+	 * @return the current character encoding scheme
+	 */
+	public String getCharacterEncoding();
+
+	/**
+	 * Return true if multiple encoding is allowed
+	 *
+	 * @return whether multiple encoding is allowed when canonicalizing data
+	 */
+	public boolean getAllowMultipleEncoding();
+
+	/**
+	 * Return true if mixed encoding is allowed
+	 *
+	 * @return whether mixed encoding is allowed when canonicalizing data
+	 */
+	public boolean getAllowMixedEncoding();
+
+	/**
+	 * Returns the List of Codecs to use when canonicalizing data
+	 * 
+	 * @return the codec list
+	 */
+	public List<String> getDefaultCanonicalizationCodecs();
+
+	/**
+	 * Gets the digital signature algorithm used by ESAPI to generate and verify signatures.
+	 * 
+	 * @return the current digital signature algorithm
+	 */
+	public String getDigitalSignatureAlgorithm();
+
+	/**
+	 * Gets the digital signature key length used by ESAPI to generate and verify signatures.
+	 * 
+	 * @return the current digital signature key length
+	 */
+	public int getDigitalSignatureKeyLength();
+		   
+	/**
+	 * Gets the random number generation algorithm used to generate random numbers where needed.
+	 * 
+	 * @return the current random number generation algorithm
+	 */
+	public String getRandomAlgorithm();
+
+	/**
+	 * Gets the number of login attempts allowed before the user's account is locked. If this 
+	 * many failures are detected within the alloted time period, the user's account will be locked.
+	 * 
+	 * @return the number of failed login attempts that cause an account to be locked
+	 */
+	public int getAllowedLoginAttempts();
+
+	/**
+	 * Gets the maximum number of old password hashes that should be retained. These hashes can 
+	 * be used to ensure that the user doesn't reuse the specified number of previous passwords
+	 * when they change their password.
+	 * 
+	 * @return the number of old hashed passwords to retain
+	 */
+	public int getMaxOldPasswordHashes();
+
+	/**
+	 * Allows for complete disabling of all intrusion detection mechanisms
+	 * 
+	 * @return true if intrusion detection should be disabled
+	 */
+	public boolean getDisableIntrusionDetection();
+	
+	/**
+	 * Gets the intrusion detection quota for the specified event.
+	 * 
+	 * @param eventName the name of the event whose quota is desired
+	 * 
+	 * @return the Quota that has been configured for the specified type of event
+	 */
+	public Threshold getQuota(String eventName);
+
+	/**
+	 * Gets a file from the resource directory
+     *
+     * @param filename The file name resource.
+     * @return A {@code File} object representing the specified file name or null if not found.
+     */
+    public File getResourceFile( String filename );
+    
+	/**
+	 * Forces new cookies to have HttpOnly flag set.
+     */
+    public boolean getForceHttpOnlySession() ;
+
+	/**
+	 * Forces session cookies to have Secure flag set.
+     */
+    public boolean getForceSecureSession() ;
+
+	/**
+	 * Forces new cookies to have HttpOnly flag set.
+     */
+    public boolean getForceHttpOnlyCookies() ;
+
+	/**
+	 * Forces new cookies to have Secure flag set.
+     */
+    public boolean getForceSecureCookies() ;
+
+	/**
+	 * Returns the maximum allowable HTTP header size.
+	 */
+	public int getMaxHttpHeaderSize() ;
+
+	/**
+	 * Gets an InputStream to a file in the resource directory
+     *
+     * @param filename A file name in the resource directory.
+     * @return An {@code InputStream} to the specified file name in the resource directory.
+     * @throws IOException If the specified file name cannot be found or opened for reading.
+     */
+    public InputStream getResourceStream( String filename ) throws IOException;
+
+    	
+	/**
+	 * Sets the ESAPI resource directory.
+	 * 
+	 * @param dir The location of the resource directory.
+	 */
+	public void setResourceDirectory(String dir);
+	
+	/**
+	 * Gets the content type for responses used when setSafeContentType() is called.
+	 * <br><br>
+	 * Note: This does not get the configured character encoding scheme. That is accessed by calling 
+	 * getCharacterEncoding().
+	 * 
+	 * @return The current content-type set for responses.
+	 */
+	public String getResponseContentType();
+
+	/**
+	 * This method returns the configured name of the session identifier, 
+	 * likely "JSESSIONID" though this can be overridden.
+	 * 
+	 * @return The name of the session identifier, like "JSESSIONID"
+	 */
+	public String getHttpSessionIdName();
+	
+	/**
+	 * Gets the length of the time to live window for remember me tokens (in milliseconds).
+	 * 
+	 * @return The time to live length for generated remember me tokens.
+	 */
+	public long getRememberTokenDuration();
+
+	
+	/**
+	 * Gets the idle timeout length for sessions (in milliseconds). This is the amount of time that a session
+	 * can live before it expires due to lack of activity. Applications or frameworks could provide a reauthenticate
+	 * function that enables a session to continue after reauthentication.
+	 * 
+	 * @return The session idle timeout length.
+	 */
+	public int getSessionIdleTimeoutLength();
+	
+	/**
+	 * Gets the absolute timeout length for sessions (in milliseconds). This is the amount of time that a session
+	 * can live before it expires regardless of the amount of user activity. Applications or frameworks could 
+	 * provide a reauthenticate function that enables a session to continue after reauthentication.
+	 * 
+	 * @return The session absolute timeout length.
+	 */
+	public int getSessionAbsoluteTimeoutLength();
+	
+	
+	/**
+	 * Returns whether HTML entity encoding should be applied to log entries.
+	 * 
+	 * @return True if log entries are to be HTML Entity encoded. False otherwise.
+	 */
+	public boolean getLogEncodingRequired();
+	
+	/**
+	 * Returns whether ESAPI should log the application name. This might be clutter in some
+	 * single-server/single-app environments.
+	 * 
+	 * @return True if ESAPI should log the application name, False otherwise
+	 */
+	public boolean getLogApplicationName();
+
+	/**
+	 * Returns whether ESAPI should log the server IP. This might be clutter in some
+	 * single-server environments.
+	 * 
+	 * @return True if ESAPI should log the server IP and port, False otherwise
+	 */
+	public boolean getLogServerIP();
+
+	/**
+	 * Returns the current log level.
+	 * @return	An integer representing the current log level.
+	 */
+    public int getLogLevel();
+	
+    /**
+     * Get the name of the log file specified in the ESAPI configuration properties file. Return a default value 
+     * if it is not specified.
+     * 
+     * @return the log file name defined in the properties file.
+     */
+    public String getLogFileName();
+
+    /**
+     * Get the maximum size of a single log file from the ESAPI configuration properties file. Return a default value 
+     * if it is not specified. Once the log hits this file size, it will roll over into a new log.
+     * 
+     * @return the maximum size of a single log file (in bytes).
+     */
+    public int getMaxLogFileSize();
+
+	/**
+	 * Models a simple threshold as a count and an interval, along with a set of actions to take if 
+	 * the threshold is exceeded. These thresholds are used to define when the accumulation of a particular event
+	 * has met a set number within the specified time period. Once a threshold value has been met, various
+	 * actions can be taken at that point.
+	 */
+	public static class Threshold {
+		
+		/** The name of this threshold. */
+		public String name = null;
+		
+		/** The count at which this threshold is triggered. */
+		public int count = 0;
+		
+		/** 
+		 * The time frame within which 'count' number of actions has to be detected in order to
+		 * trigger this threshold.
+		 */
+		public long interval = 0;
+		
+		/**
+		 * The list of actions to take if the threshold is met. It is expected that this is a list of Strings, but 
+		 * your implementation could have this be a list of any type of 'actions' you wish to define. 
+		 */
+		public List<String> actions = null;
+
+		/**
+		 * Constructs a threshold that is composed of its name, its threshold count, the time window for
+		 * the threshold, and the actions to take if the threshold is triggered.
+		 * 
+		 * @param name The name of this threshold.
+		 * @param count The count at which this threshold is triggered.
+		 * @param interval The time frame within which 'count' number of actions has to be detected in order to
+		 * trigger this threshold.
+		 * @param actions The list of actions to take if the threshold is met.
+		 */
+		public Threshold(String name, int count, long interval, List<String> actions) {
+			this.name = name;
+			this.count = count;
+			this.interval = interval;
+			this.actions = actions;
+		}
+	}
+
+	/**
+	 * Returns the default working directory for executing native processes with Runtime.exec().
+	 */
+	public File getWorkingDirectory();
+}
diff --git a/src/main/java/org/owasp/esapi/StringUtilities.java b/src/main/java/org/owasp/esapi/StringUtilities.java
new file mode 100644
index 0000000..e41dd03
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/StringUtilities.java
@@ -0,0 +1,199 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+/**
+ * String utilities used in various filters.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ * href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public class StringUtilities {
+
+	private static final Pattern p = Pattern.compile( "\\s");
+	public static String replaceLinearWhiteSpace( String input ) {
+		return p.matcher(input).replaceAll( " " );
+	}
+	
+	/**
+	 * Removes all unprintable characters from a string 
+	 * and replaces with a space.
+	 * @param input
+	 * @return the stripped value
+	 */
+	public static String stripControls( String input ) {
+		StringBuilder sb = new StringBuilder();
+		for ( int i=0; i<input.length(); i++ ) {
+			char c = input.charAt( i );
+			if ( c > 0x20 && c < 0x7f ) {
+				sb.append( c );
+			} else {
+				sb.append( ' ' );
+			}
+		}
+		return sb.toString();
+	}
+
+	
+    /**
+     * Union multiple character arrays.
+     * 
+     * @param list the char[]s to union
+     * @return the union of the char[]s
+     */
+    public static char[] union(char[]... list) {
+    	StringBuilder sb = new StringBuilder();
+    	
+    	for (char[] characters : list) {
+	        for (int i = 0; i < list.length; i++) {
+	            if (!contains(sb, characters[i]))
+	                sb.append(list[i]);
+	        }
+    	}
+
+        char[] toReturn = new char[sb.length()];
+        sb.getChars(0, sb.length(), toReturn, 0);
+        Arrays.sort(toReturn);
+        return toReturn;
+    }
+
+
+	/**
+     * Returns true if the character is contained in the provided StringBuilder.
+     * @param input 	The input
+     * @param c 		The character to check for to see if {@code input} contains.
+     * @return			True if the specified character is contained; false otherwise.
+     */
+    public static boolean contains(StringBuilder input, char c) {
+        for (int i = 0; i < input.length(); i++) {
+            if (input.charAt(i) == c)
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the replace value if the value of test is null, "null", or ""
+     *
+     * @param test The value to test
+     * @param replace The replacement value
+     * @return The correct value
+     */
+    public static String replaceNull( String test, String replace ) {
+        return ( test == null || "null".equalsIgnoreCase( test.trim() ) || "".equals( test.trim() ) ) ? replace : test;
+    }
+
+    /**
+     * Calculate the Edit Distance between 2 Strings as a measure of similarity.
+     *
+     * For example, if the strings GUMBO and GAMBOL are passed in, the edit distance
+     * is 2, since GUMBO transforms into GAMBOL by replacing the 'U' with an 'A' and
+     * adding an 'L'.
+     *
+     * Original Implementation of this algorithm by Michael Gilleland, adapted by
+     * Chas Emerick for the Apache-Commons project
+     * http://www.merriampark.com/ldjava.htm
+     *
+     * @param s The source string
+     * @param t The target String
+     * @return The edit distance between the 2 strings
+     */
+    public static int getLevenshteinDistance (String s, String t) {
+      if (s == null || t == null) {
+        throw new IllegalArgumentException("Strings must not be null");
+      }
+
+      int n = s.length(); // length of s
+      int m = t.length(); // length of t
+
+      if (n == 0) {
+        return m;
+      } else if (m == 0) {
+        return n;
+      }
+
+      int p[] = new int[n+1]; //'previous' cost array, horizontally
+      int d[] = new int[n+1]; // cost array, horizontally
+      int _d[]; //placeholder to assist in swapping p and d
+
+      // indexes into strings s and t
+      int i; // iterates through s
+      int j; // iterates through t
+
+      char t_j; // jth character of t
+
+      int cost; // cost
+
+      for (i = 0; i<=n; i++) {
+         p[i] = i;
+      }
+
+      for (j = 1; j<=m; j++) {
+         t_j = t.charAt(j-1);
+         d[0] = j;
+
+         for (i=1; i<=n; i++) {
+            cost = s.charAt(i-1)==t_j ? 0 : 1;
+            // minimum of cell to the left+1, to the top+1, diagonally left and up +cost
+            d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1),  p[i-1]+cost);
+         }
+
+         // copy current distance counts to 'previous row' distance counts
+         _d = p;
+         p = d;
+         d = _d;
+      }
+
+      // our last action in the above loop was to switch d and p, so p now
+      // actually has the most recent cost counts
+      return p[n];
+    }
+
+    /**
+     * Check to ensure that a {@code String} is not null or empty (after optional
+     * trimming of leading and trailing whitespace). Usually used with
+     * assertions, as in
+     * <pre>
+     * 		assert StringUtils.notNullOrEmpty(cipherXform, true) :
+     * 								"Cipher transformation may not be null or empty!";
+     * </pre>
+     *
+     * @param str	The {@code String} to be checked.
+     * @param trim	If {@code true}, the string is first trimmed before checking
+     * 				to see if it is empty, otherwise it is not.
+     * @return		True if the string is null or empty (after possible
+     * 				trimming); otherwise false.
+     * @since 2.0
+     */
+    public static boolean notNullOrEmpty(String str, boolean trim) {
+    	if ( trim ) {
+    		return !( str == null || str.trim().equals("") );
+    	} else {
+    		return !( str == null || str.equals("") );
+    	}
+    }
+
+    /**
+     * Returns true if String is empty ("") or null.
+     */
+    public static boolean isEmpty(String str) {
+        return str == null || str.length() == 0;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/User.java b/src/main/java/org/owasp/esapi/User.java
new file mode 100644
index 0000000..f6b9fa2
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/User.java
@@ -0,0 +1,760 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.AuthenticationHostException;
+import org.owasp.esapi.errors.EncryptionException;
+
+import javax.servlet.http.HttpSession;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.*;
+
+/**
+ * The User interface represents an application user or user account. There is quite a lot of information that an
+ * application must store for each user in order to enforce security properly. There are also many rules that govern
+ * authentication and identity management.
+ * <P>
+ * A user account can be in one of several states. When first created, a User should be disabled, not expired, and
+ * unlocked. To start using the account, an administrator should enable the account. The account can be locked for a
+ * number of reasons, most commonly because they have failed login for too many times. Finally, the account can expire
+ * after the expiration date has been reached. The User must be enabled, not expired, and unlocked in order to pass
+ * authentication.
+ * 
+ * @author <a href="mailto:jeff.williams at aspectsecurity.com?subject=ESAPI question">Jeff Williams</a> at <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com) <a href="http://www.digital-ritual.com">Digital Ritual Software</a>
+ * @since June 1, 2007
+ */
+
+public interface User extends Principal, Serializable {
+	/**
+	 * @return the locale
+	 */
+	public Locale getLocale();
+
+	/**
+	 * @param locale the locale to set
+	 */
+	public void setLocale(Locale locale);
+
+    /**
+     * Adds a role to this user's account.
+     * 
+     * @param role 
+     * 		the role to add
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void addRole(String role) throws AuthenticationException;
+
+    /**
+     * Adds a set of roles to this user's account.
+     * 
+     * @param newRoles 
+     * 		the new roles to add
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void addRoles(Set<String> newRoles) throws AuthenticationException;
+
+    /**
+     * Sets the user's password, performing a verification of the user's old password, the equality of the two new
+     * passwords, and the strength of the new password.
+     * 
+     * @param oldPassword 
+     * 		the old password
+     * @param newPassword1 
+     * 		the new password
+     * @param newPassword2 
+     * 		the new password - used to verify that the new password was typed correctly
+     * 
+     * @throws AuthenticationException 
+     * 		if newPassword1 does not match newPassword2, if oldPassword does not match the stored old password, or if the new password does not meet complexity requirements 
+     * @throws EncryptionException 
+     */
+    void changePassword(String oldPassword, String newPassword1, String newPassword2) throws AuthenticationException, EncryptionException;
+
+    /**
+     * Disable this user's account.
+     */
+    void disable();
+
+    /**
+     * Enable this user's account.
+     */
+    void enable();
+
+    /**
+     * Gets this user's account id number.
+     * 
+     * @return the account id
+     */
+    long getAccountId();
+    
+    /**
+     * Gets this user's account name.
+     * 
+     * @return the account name
+     */
+    String getAccountName();
+
+    /**
+     * Gets the CSRF token for this user's current sessions.
+     * 
+     * @return the CSRF token
+     */
+    String getCSRFToken();
+
+    /**
+     * Returns the date that this user's account will expire.
+     *
+     * @return Date representing the account expiration time.
+     */
+    Date getExpirationTime();
+
+    /**
+     * Returns the number of failed login attempts since the last successful login for an account. This method is
+     * intended to be used as a part of the account lockout feature, to help protect against brute force attacks.
+     * However, the implementor should be aware that lockouts can be used to prevent access to an application by a
+     * legitimate user, and should consider the risk of denial of service.
+     * 
+     * @return the number of failed login attempts since the last successful login
+     */
+    int getFailedLoginCount();
+
+    /**
+     * Returns the last host address used by the user. This will be used in any log messages generated by the processing
+     * of this request.
+     * 
+     * @return the last host address used by the user
+     */
+    String getLastHostAddress();
+
+	/**
+     * Returns the date of the last failed login time for a user. This date should be used in a message to users after a
+     * successful login, to notify them of potential attack activity on their account.
+     * 
+     * @return date of the last failed login
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    Date getLastFailedLoginTime() throws AuthenticationException;
+
+    /**
+     * Returns the date of the last successful login time for a user. This date should be used in a message to users
+     * after a successful login, to notify them of potential attack activity on their account.
+     * 
+     * @return date of the last successful login
+     */
+    Date getLastLoginTime();
+
+    /**
+     * Gets the date of user's last password change.
+     * 
+     * @return the date of last password change
+     */
+    Date getLastPasswordChangeTime();
+
+    /**
+     * Gets the roles assigned to a particular account.
+     * 
+     * @return an immutable set of roles
+     */
+    Set<String> getRoles();
+
+    /**
+     * Gets the screen name (alias) for the current user.
+     * 
+     * @return the screen name
+     */
+    String getScreenName();
+
+    /**
+     * Adds a session for this User.
+     * 
+     * @param s
+     * 			The session to associate with this user.
+     */
+    void addSession( HttpSession s );
+    
+    /**
+     * Removes a session for this User.
+     * 
+     * @param s
+     * 			The session to remove from being associated with this user.
+     */
+    void removeSession( HttpSession s );
+    
+    /**
+     * Returns the list of sessions associated with this User.
+     * @return
+     */
+    Set getSessions();
+    
+    /**
+     * Increment failed login count.
+     */
+    void incrementFailedLoginCount();
+
+    /**
+     * Checks if user is anonymous.
+     * 
+     * @return true, if user is anonymous
+     */
+    boolean isAnonymous();
+
+    /**
+     * Checks if this user's account is currently enabled.
+     * 
+     * @return true, if account is enabled 
+     */
+    boolean isEnabled();
+
+    /**
+     * Checks if this user's account is expired.
+     * 
+     * @return true, if account is expired
+     */
+    boolean isExpired();
+
+    /**
+     * Checks if this user's account is assigned a particular role.
+     * 
+     * @param role 
+     * 		the role for which to check
+     * 
+     * @return true, if role has been assigned to user
+     */
+    boolean isInRole(String role);
+
+    /**
+     * Checks if this user's account is locked.
+     * 
+     * @return true, if account is locked
+     */
+    boolean isLocked();
+
+    /**
+     * Tests to see if the user is currently logged in.
+     * 
+     * @return true, if the user is logged in
+     */
+    boolean isLoggedIn();
+
+    /**
+     * Tests to see if this user's session has exceeded the absolute time out based 
+      * on ESAPI's configuration settings.
+     * 
+     * @return true, if user's session has exceeded the absolute time out
+     */
+    boolean isSessionAbsoluteTimeout();
+
+    /**
+      * Tests to see if the user's session has timed out from inactivity based 
+      * on ESAPI's configuration settings.
+      * 
+      * A session may timeout prior to ESAPI's configuration setting due to 
+      * the servlet container setting for session-timeout in web.xml. The 
+      * following is an example of a web.xml session-timeout set for one hour. 	
+      *
+      * <session-config>
+      *   <session-timeout>60</session-timeout> 
+      * </session-config>
+      * 
+      * @return true, if user's session has timed out from inactivity based 
+      *               on ESAPI configuration
+      */
+     boolean isSessionTimeout();
+
+    /**
+     * Lock this user's account.
+     */
+    void lock();
+
+    /**
+     * Login with password.
+     * 
+     * @param password 
+     * 		the password
+     * @throws AuthenticationException 
+     * 		if login fails
+     */
+    void loginWithPassword(String password) throws AuthenticationException;
+
+    /**
+     * Logout this user.
+     */
+    void logout();
+
+    /**
+     * Removes a role from this user's account.
+     * 
+     * @param role 
+     * 		the role to remove
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void removeRole(String role) throws AuthenticationException;
+
+    /**
+     * Returns a token to be used as a prevention against CSRF attacks. This token should be added to all links and
+     * forms. The application should verify that all requests contain the token, or they may have been generated by a
+     * CSRF attack. It is generally best to perform the check in a centralized location, either a filter or controller.
+     * See the verifyCSRFToken method.
+     * 
+     * @return the new CSRF token
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    String resetCSRFToken() throws AuthenticationException;
+
+    /**
+     * Sets this user's account name.
+     * 
+     * @param accountName the new account name
+     */
+    void setAccountName(String accountName);
+
+    /**
+     * Sets the date and time when this user's account will expire.
+     * 
+     * @param expirationTime the new expiration time
+     */
+	void setExpirationTime(Date expirationTime);
+
+	/**
+     * Sets the roles for this account.
+     * 
+     * @param roles 
+     * 		the new roles
+     * 
+     * @throws AuthenticationException 
+     * 		the authentication exception
+     */
+    void setRoles(Set<String> roles) throws AuthenticationException;
+
+    /**
+     * Sets the screen name (username alias) for this user.
+     * 
+     * @param screenName the new screen name
+     */
+    void setScreenName(String screenName);
+
+    /**
+     * Unlock this user's account.
+     */
+    void unlock();
+
+	/**
+	 * Verify that the supplied password matches the password for this user. This method
+	 * is typically used for "reauthentication" for the most sensitive functions, such
+	 * as transactions, changing email address, and changing other account information.
+	 * 
+	 * @param password 
+	 * 		the password that the user entered
+	 * 
+	 * @return true, if the password passed in matches the account's password
+	 * 
+	 * @throws EncryptionException 
+	 */
+	public boolean verifyPassword(String password) throws EncryptionException;
+
+	/**
+	 * Set the time of the last failed login for this user.
+	 * 
+	 * @param lastFailedLoginTime the date and time when the user just failed to login correctly.
+	 */
+	void setLastFailedLoginTime(Date lastFailedLoginTime);
+	
+	/**
+	 * Set the last remote host address used by this user.
+	 * 
+	 * @param remoteHost The address of the user's current source host.
+	 */
+	void setLastHostAddress(String remoteHost) throws AuthenticationHostException;
+	
+	/**
+	 * Set the time of the last successful login for this user.
+	 * 
+	 * @param lastLoginTime the date and time when the user just successfully logged in.
+	 */
+	void setLastLoginTime(Date lastLoginTime);
+	
+	/**
+	 * Set the time of the last password change for this user.
+	 * 
+	 * @param lastPasswordChangeTime the date and time when the user just successfully changed his/her password.
+	 */
+	void setLastPasswordChangeTime(Date lastPasswordChangeTime);
+
+	/**
+	 * Returns the hashmap used to store security events for this user. Used by the
+	 * IntrusionDetector.
+	 */
+	HashMap getEventMap();
+	
+
+	/**
+	 * The ANONYMOUS user is used to represent an unidentified user. Since there is
+	 * always a real user, the ANONYMOUS user is better than using null to represent
+	 * this.
+	 */
+    public final User ANONYMOUS = new User() {
+
+		private static final long serialVersionUID = -1850916950784965502L;
+
+		private String csrfToken = "";
+    	private Set sessions = new HashSet();
+		private Locale locale = null;
+    	
+    	/**
+         * {@inheritDoc}
+         */
+        public void addRole(String role) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addRoles(Set newRoles) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void changePassword(String oldPassword, String newPassword1,
+                String newPassword2) throws AuthenticationException,
+                EncryptionException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void disable() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void enable() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public long getAccountId() {
+	        return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String getAccountName() {
+	        return "Anonymous";
+        }
+
+		/**
+		 * Alias method that is equivalent to getAccountName()
+		 * 
+		 * @return the name of the current user's account
+         */
+        public String getName() {
+        	return getAccountName();
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public String getCSRFToken() {
+	        return csrfToken;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getExpirationTime() {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getFailedLoginCount() {
+	        return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getLastFailedLoginTime() throws AuthenticationException {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String getLastHostAddress() {
+	        return "unknown";
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getLastLoginTime() {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Date getLastPasswordChangeTime() {
+	        return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Set<String> getRoles() {
+	        return new HashSet<String>();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String getScreenName() {
+	        return "Anonymous";
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addSession(HttpSession s)  {
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void removeSession(HttpSession s)  {
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Set getSessions()  {
+            return sessions;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void incrementFailedLoginCount() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isAnonymous() {
+	        return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isEnabled() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isExpired() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isInRole(String role) {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isLocked() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isLoggedIn() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isSessionAbsoluteTimeout() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isSessionTimeout() {
+	        return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void lock() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void loginWithPassword(String password)
+                throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void logout() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void removeRole(String role) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String resetCSRFToken() throws AuthenticationException {
+    		csrfToken = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+    		return csrfToken;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void setAccountName(String accountName) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+    	public void setExpirationTime(Date expirationTime) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+        
+    	/**
+         * {@inheritDoc}
+         */
+        public void setRoles(Set roles) throws AuthenticationException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void setScreenName(String screenName) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void unlock() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean verifyPassword(String password) throws EncryptionException {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void setLastFailedLoginTime(Date lastFailedLoginTime) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+    	public void setLastLoginTime(Date lastLoginTime) {
+    		throw new RuntimeException("Invalid operation for the anonymous user");
+    	}
+
+    	/**
+         * {@inheritDoc}
+         */
+     	public void setLastHostAddress(String remoteHost) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+                
+     	/**
+         * {@inheritDoc}
+         */
+        public void setLastPasswordChangeTime(Date lastPasswordChangeTime) {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+        
+        /**
+         *  {@inheritDoc}
+         */
+        public HashMap getEventMap() {
+        	throw new RuntimeException("Invalid operation for the anonymous user");
+        }
+         /**
+    	 * @return the locale
+    	 */
+    	public Locale getLocale() {
+    		return locale;
+    	}
+
+    	/**
+    	 * @param locale the locale to set
+    	 */
+    	public void setLocale(Locale locale) {
+    		this.locale = locale;
+    	}
+    };
+}
diff --git a/src/main/java/org/owasp/esapi/ValidationErrorList.java b/src/main/java/org/owasp/esapi/ValidationErrorList.java
new file mode 100644
index 0000000..df19444
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/ValidationErrorList.java
@@ -0,0 +1,137 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * 
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * The ValidationErrorList class defines a well-formed collection of 
+ * ValidationExceptions so that groups of validation functions can be 
+ * called in a non-blocking fashion.
+ * <P>
+ * To use the ValidationErrorList to execute groups of validation 
+ * attempts, your controller code would look something like:
+ * 
+ * <PRE>
+ * ValidationErrorList() errorList = new ValidationErrorList();.
+ * String name  = getValidInput("Name", form.getName(), "SomeESAPIRegExName1", 255, false, errorList);
+ * String address = getValidInput("Address", form.getAddress(), "SomeESAPIRegExName2", 255, false, errorList);
+ * Integer weight = getValidInteger("Weight", form.getWeight(), 1, 1000000000, false, errorList);
+ * Integer sortOrder = getValidInteger("Sort Order", form.getSortOrder(), -100000, +100000, false, errorList);
+ * request.setAttribute( "ERROR_LIST", errorList );
+ * </PRE>
+ * 
+ * The at your view layer you would be able to retrieve all
+ * of your error messages via a helper function like:
+ * 
+ * <PRE>
+ * public static ValidationErrorList getErrors() {          
+ *     HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+ *     ValidationErrorList errors = new ValidationErrorList();
+ *     if (request.getAttribute(Constants.ERROR_LIST) != null) {
+ *        errors = (ValidationErrorList)request.getAttribute("ERROR_LIST");
+ *     }
+ * 	   return errors;
+ * }
+ * </PRE>
+ * 
+ * You can list all errors like:
+ * 
+ * <PRE>
+ * <%
+ *      for (Object vo : errorList.errors()) {
+ *         ValidationException ve = (ValidationException)vo;
+ * %>
+ * <%= ESAPI.encoder().encodeForHTML(ve.getMessage()) %><br/>
+ * <%
+ *     }
+ * %>
+ * </PRE>
+ * 
+ * And even check if a specific UI component is in error via calls like:
+ * 
+ * <PRE>
+ * ValidationException e = errorList.getError("Name");
+ * </PRE>
+ * 
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @since August 15, 2008
+ */
+public class ValidationErrorList {
+
+	/**
+	 * Error list of ValidationException's
+	 */
+	private HashMap<String, ValidationException> errorList = new HashMap<String, ValidationException>();
+
+	/**
+	 * Adds a new error to list with a unique named context.
+	 * No action taken if either element is null. 
+	 * Existing contexts will be overwritten.
+	 * 
+	 * @param context Unique named context for this {@code ValidationErrorList}.
+	 * @param vex	A {@code ValidationException}.
+	 */
+	public void addError(String context, ValidationException vex) {
+		if ( context == null ) throw new RuntimeException( "Context for cannot be null: " + vex.getLogMessage(), vex );
+		if ( vex == null ) throw new RuntimeException( "Context (" + context + ") cannot be null" );
+		if (getError(context) != null) throw new RuntimeException("Context (" + context + ") already exists, must be unique");
+		errorList.put(context, vex);
+	}
+
+	/**
+	 * Returns list of ValidationException, or empty list of no errors exist.
+	 * 
+	 * @return List
+	 */
+	public List<ValidationException> errors() {
+		return new ArrayList<ValidationException>( errorList.values() );
+	}
+
+	/**
+	 * Retrieves ValidationException for given context if one exists.
+	 * 
+	 * @param context unique name for each error
+	 * @return ValidationException or null for given context
+	 */
+	public ValidationException getError(String context) {
+		if (context == null) return null;		
+		return errorList.get(context);
+	}
+
+	/**
+	 * Returns true if no error are present.
+	 * 
+	 * @return boolean
+	 */
+	public boolean isEmpty() {
+		return errorList.isEmpty();
+	}
+	
+	/**
+	 * Returns the numbers of errors present.
+	 * 
+	 * @return boolean
+	 */
+	public int size() {
+		return errorList.size();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/ValidationRule.java b/src/main/java/org/owasp/esapi/ValidationRule.java
new file mode 100644
index 0000000..b9b6835
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/ValidationRule.java
@@ -0,0 +1,81 @@
+package org.owasp.esapi;
+
+import java.util.Set;
+
+import org.owasp.esapi.errors.ValidationException;
+
+public interface ValidationRule {
+
+	/**
+	 * Parse the input, throw exceptions if validation fails
+	 * 
+	 * @param context
+	 *            for logging
+	 * @param input
+	 *            the value to be parsed
+	 * @return a validated value
+	 * @throws ValidationException
+	 *             if any validation rules fail
+	 */
+	Object getValid(String context, String input)
+			throws ValidationException;
+
+	/**
+	 * Whether or not a valid valid can be null. getValid will throw an
+	 * Exception and getSafe will return the default value if flag is set to
+	 * true
+	 * 
+	 * @param flag
+	 *            whether or not null values are valid/safe
+	 */
+	void setAllowNull(boolean flag);
+
+	/**
+	 * Programmatically supplied name for the validator
+	 * @return a name, describing the validator
+	 */
+	String getTypeName();
+
+	/**
+	 * @param typeName a name, describing the validator
+	 */
+	void setTypeName(String typeName);
+
+	/**
+	 * @param encoder the encoder to use
+	 */
+	void setEncoder(Encoder encoder);
+
+	/**
+	 * Check if the input is valid, throw an Exception otherwise 
+	 */
+	void assertValid(String context, String input)
+			throws ValidationException;
+
+	/**
+	 * Get a validated value, add the errors to an existing error list
+	 */
+	Object getValid(String context, String input,
+			ValidationErrorList errorList) throws ValidationException;
+
+	/**
+	 * Try to call get valid, then call sanitize, finally return a default value
+	 */
+	Object getSafe(String context, String input);
+	
+	/**
+	 * @return true if the input passes validation
+	 */
+	boolean isValid(String context, String input);
+
+	/**
+	 * String the input of all chars contained in the list
+	 */
+	String whitelist(String input, char[] list);
+	
+	/**
+	 * String the input of all chars contained in the list
+	 */
+	String whitelist(String input, Set<Character> list);
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/Validator.java b/src/main/java/org/owasp/esapi/Validator.java
new file mode 100644
index 0000000..951d8c4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/Validator.java
@@ -0,0 +1,691 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import java.io.File;
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * The Validator interface defines a set of methods for canonicalizing and
+ * validating untrusted input. Implementors should feel free to extend this
+ * interface to accommodate their own data formats. Rather than throw exceptions,
+ * this interface returns boolean results because not all validation problems
+ * are security issues. Boolean returns allow developers to handle both valid
+ * and invalid results more cleanly than exceptions.
+ * <P>
+ * Implementations must adopt a "whitelist" approach to validation where a
+ * specific pattern or character set is matched. "Blacklist" approaches that
+ * attempt to identify the invalid or disallowed characters are much more likely
+ * to allow a bypass with encoding or other tricks.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public interface Validator {
+
+	void addRule( ValidationRule rule );
+
+	ValidationRule getRule( String name );
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws IntrusionException;
+
+	/**
+	 * Calls isValidInput and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated input as a String. Invalid input will generate a descriptive ValidationException,
+	 * and input that is clearly an attack will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param type
+	 * 		The regular expression name that maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength
+	 * 		The maximum post-canonicalized String length allowed.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return The canonicalized user input.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Returns validated input as a String with optional canonicalization. Invalid input will generate a descriptive ValidationException,
+	 * and input that is clearly an attack will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param type
+	 * 		The regular expression name that maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength
+	 * 		The maximum post-canonicalized String length allowed.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param canonicalize
+	 *      If canonicalize is true then input will be canonicalized before validation
+	 *
+	 * @return The canonicalized user input.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidInput with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidInput with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls isValidDate and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDate(String context, String input, DateFormat format, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls isValidDate and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a valid date as a Date. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param format
+	 * 		Required formatting of date inputted.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A valid date as a Date
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	Date getValidDate(String context, String input, DateFormat format, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidDate with the supplied errorList to capture ValidationExceptions
+	 */
+	Date getValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML and returns true if no exceptions are thrown.
+	 */
+	boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML and returns true if no exceptions are thrown.
+	 */
+	boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated "safe" HTML that does not contain unwanted scripts in the body, attributes, CSS, URLs, or anywhere else.
+	 * Implementors should reference the OWASP AntiSamy project for ideas
+	 * on how to do HTML validation in a whitelist way, as this is an extremely difficult problem.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param maxLength
+	 * 		The maximum String length allowed.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return Valid safe HTML
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidCreditCard and returns true if no exceptions are thrown.
+	 */
+	boolean isValidCreditCard(String context, String input, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidCreditCard and returns true if no exceptions are thrown.
+	 */
+	boolean isValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a canonicalized and validated credit card number as a String. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual user input data to validate.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A valid credit card number
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidCreditCard(String context, String input, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidCreditCard with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidDirectoryPath and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidDirectoryPath and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a canonicalized and validated directory path as a String, provided that the input
+	 * maps to an existing directory that is an existing subdirectory (at any level) of the specified parent. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException. Instead of throwing a ValidationException
+	 * on error, this variant will store the exception inside of the ValidationErrorList.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual input data to validate.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A valid directory path
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidDirectoryPath with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName with the default list of allowedExtensions
+	 */
+	boolean isValidFileName(String context, String input, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName with the default list of allowedExtensions
+	 */
+	boolean isValidFileName(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileName and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a canonicalized and validated file name as a String. Implementors should check for allowed file extensions here, as well as allowed file name characters, as declared in "ESAPI.properties". Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     *
+     * @return A valid file name
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	String getValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidFileName with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidNumber and returns true if no exceptions are thrown.
+	 */
+	boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidNumber and returns true if no exceptions are thrown.
+	 */
+	boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a validated number as a double within the range of minValue to maxValue. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     * @param minValue
+     * 		Lowest legal value for input.
+     * @param maxValue
+     * 		Highest legal value for input.
+     *
+     * @return A validated number as a double.
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidSafeHTML with the supplied errorList to capture ValidationExceptions
+	 */
+	Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidInteger and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidInteger and returns true if no exceptions are thrown.
+	 */
+	boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a validated integer. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     * @param minValue
+     * 		Lowest legal value for input.
+     * @param maxValue
+     * 		Highest legal value for input.
+     *
+     * @return A validated number as an integer.
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidInteger with the supplied errorList to capture ValidationExceptions
+	 */
+	Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidDouble and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidDouble and returns true if no exceptions are thrown.
+	 */
+	boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns a validated real number as a double. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+     * @param input
+     * 		The actual input data to validate.
+     * @param allowNull
+     * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+     * @param minValue
+     * 		Lowest legal value for input.
+     * @param maxValue
+     * 		Highest legal value for input.
+     *
+     * @return A validated real number as a double.
+     *
+     * @throws ValidationException
+     * @throws IntrusionException
+	 */
+	Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidDouble with the supplied errorList to capture ValidationExceptions
+	 */
+	Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileContent and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileContent and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns validated file content as a byte array. This is a good place to check for max file size, allowed character sets, and do virus scans.  Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The actual input data to validate.
+	 * @param maxBytes
+	 * 		The maximum number of bytes allowed in a legal file.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @return A byte array containing valid file content.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidFileContent with the supplied errorList to capture ValidationExceptions
+	 */
+	byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileUpload and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull) throws IntrusionException;
+
+	/**
+	 * Calls getValidFileUpload and returns true if no exceptions are thrown.
+	 */
+	boolean isValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Validates the filepath, filename, and content of a file. Invalid input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param filepath
+	 * 		The file path of the uploaded file.
+	 * @param filename
+	 * 		The filename of the uploaded file
+	 * @param content
+	 * 		A byte array containing the content of the uploaded file.
+	 * @param maxBytes
+	 * 		The max number of bytes allowed for a legal file upload.
+	 * @param allowNull
+	 * 		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidFileUpload with the supplied errorList to capture ValidationExceptions
+	 */
+	void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidListItem and returns true if no exceptions are thrown.
+	 */
+	boolean isValidListItem(String context, String input, List<String> list) throws IntrusionException;
+
+	/**
+	 * Calls getValidListItem and returns true if no exceptions are thrown.
+	 */
+	boolean isValidListItem(String context, String input, List<String> list, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns the list item that exactly matches the canonicalized input. Invalid or non-matching input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param input
+	 * 		The value to search 'list' for.
+	 * @param list
+	 * 		The list to search for 'input'.
+	 *
+	 * @return The list item that exactly matches the canonicalized input.
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	String getValidListItem(String context, String input, List<String> list) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidListItem with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidListItem(String context, String input, List<String> list, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls assertValidHTTPRequestParameterSet and returns true if no exceptions are thrown.
+	 */
+	boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional) throws IntrusionException;
+
+	/**
+	 * Calls assertValidHTTPRequestParameterSet and returns true if no exceptions are thrown.
+	 */
+	boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Validates that the parameters in the current request contain all required parameters and only optional ones in
+	 * addition. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * @param context
+	 * 		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 * @param required
+	 * 		parameters that are required to be in HTTP request
+	 * @param optional
+	 * 		additional parameters that may be in HTTP request
+	 *
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidHTTPRequestParameterSet with the supplied errorList to capture ValidationExceptions
+	 */
+	void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws IntrusionException;
+
+        /**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated printable characters as a byte array. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 *  @param context
+	 *  		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 *  @param input
+	 *  		data to be returned as valid and printable
+	 *  @param maxLength
+	 *  		Maximum number of bytes stored in 'input'
+	 *  @param allowNull
+	 *  		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 *  @return a byte array containing only printable characters, made up of data from 'input'
+	 *
+	 *  @throws ValidationException
+	 */
+	char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws ValidationException;
+
+	/**
+	 * Calls getValidPrintable with the supplied errorList to capture ValidationExceptions
+	 */
+	char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+
+	/**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull) throws IntrusionException;
+
+        /**
+	 * Calls getValidPrintable and returns true if no exceptions are thrown.
+	 */
+	boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Returns canonicalized and validated printable characters as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 *  @param context
+	 *  		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 *  @param input
+	 *  		data to be returned as valid and printable
+	 *  @param maxLength
+	 *  		Maximum number of bytes stored in 'input' after canonicalization
+	 *  @param allowNull
+	 *  		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 *  @return a String containing only printable characters, made up of data from 'input'
+	 *
+	 *  @throws ValidationException
+	 */
+	String getValidPrintable(String context, String input, int maxLength, boolean allowNull) throws ValidationException;
+
+	/**
+	 * Calls getValidPrintable with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Calls getValidRedirectLocation and returns true if no exceptions are thrown.
+	 */
+	boolean isValidRedirectLocation(String context, String input, boolean allowNull);
+
+        /**
+	 * Calls getValidRedirectLocation and returns true if no exceptions are thrown.
+	 */
+	boolean isValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errorList);
+
+	/**
+	 * Returns a canonicalized and validated redirect location as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 *  @param context
+	 *  		A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in.
+	 *  @param input
+	 *  		redirect location to be returned as valid, according to encoding rules set in "ESAPI.properties"
+	 *  @param allowNull
+	 *  		If allowNull is true then an input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 *
+	 *  @return A canonicalized and validated redirect location, as defined in "ESAPI.properties"
+	 *
+	 *  @throws ValidationException
+	 *  @throws IntrusionException
+	 */
+	String getValidRedirectLocation(String context, String input, boolean allowNull) throws ValidationException, IntrusionException;
+
+	/**
+	 * Calls getValidRedirectLocation with the supplied errorList to capture ValidationExceptions
+	 */
+	String getValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
+
+	/**
+	 * Reads from an input stream until end-of-line or a maximum number of
+	 * characters. This method protects against the inherent denial of service
+	 * attack in reading until the end of a line. If an attacker doesn't ever
+	 * send a newline character, then a normal input stream reader will read
+	 * until all memory is exhausted and the platform throws an OutOfMemoryError
+	 * and probably terminates.
+	 *
+	 * @param inputStream
+	 * 		The InputStream from which to read data
+	 * @param maxLength
+	 * 		Maximum characters allowed to be read in per line
+	 *
+	 * @return a String containing the current line of inputStream
+	 *
+	 * @throws ValidationException
+	 */
+	String safeReadLine(InputStream inputStream, int maxLength) throws ValidationException;
+
+}
+
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/all-wcprops b/src/main/java/org/owasp/esapi/codecs/.svn/all-wcprops
new file mode 100644
index 0000000..a4d75db
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/all-wcprops
@@ -0,0 +1,113 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs
+END
+UnixCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/UnixCodec.java
+END
+Base64.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/Base64.java
+END
+Hex.java
+K 25
+svn:wc:ra_dav:version-url
+V 81
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/Hex.java
+END
+CSSCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/CSSCodec.java
+END
+PushbackString.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/PushbackString.java
+END
+WindowsCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java
+END
+VBScriptCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java
+END
+XMLEntityCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java
+END
+PercentCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/PercentCodec.java
+END
+HTMLEntityCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java
+END
+Trie.java
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/Trie.java
+END
+OracleCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/OracleCodec.java
+END
+MySQLCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java
+END
+JavaScriptCodec.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java
+END
+DB2Codec.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/DB2Codec.java
+END
+HashTrie.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/HashTrie.java
+END
+Codec.java
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/Codec.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs/package.html
+END
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/entries b/src/main/java/org/owasp/esapi/codecs/.svn/entries
new file mode 100644
index 0000000..d28481e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/entries
@@ -0,0 +1,640 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/codecs
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+Codec.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+1c02e42fd08f1f108d81458d9c9c8d74
+2009-12-09T16:56:39.559412Z
+908
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4367
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+a6906bf5b6d3075175829fd29ab3537f
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+884
+

+UnixCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+39cb72c28dc9e391279520ea46380b2b
+2008-12-27T03:50:43.857437Z
+413
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2115
+

+Base64.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+25857534f16eb6ccd566ce75fff381d9
+2010-02-05T23:52:58.669229Z
+1119
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+75869
+

+Hex.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+82cb929745358d12564c61d032008d4b
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3135
+

+CSSCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+3b7a6a88767c8ab9bd4a6d172f04bf32
+2010-05-02T18:45:32.893121Z
+1387
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5593
+

+PushbackString.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+fa8cf5bc1f67f6f598e882cc6d7e8f80
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4491
+

+WindowsCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+0ff4ae54e0bb57dcf18d71a6271e43b5
+2008-12-27T03:50:43.857437Z
+413
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2130
+

+VBScriptCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+f48f0a1a740493beadb6df7294396a1c
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3248
+

+XMLEntityCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+8ef27ac6368fd21f9a7b671cbc77a2ad
+2010-01-18T17:05:21.118384Z
+975
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7636
+

+PercentCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.033968Z
+82c8cc7f45fda6239f9453fc1f6c3d55
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4958
+

+HTMLEntityCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+29afc099fd49422528f5f94089721fc2
+2010-09-09T05:09:38.976249Z
+1525
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+23614
+

+Trie.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+96c915166aafd968759414efc80f80b9
+2010-09-09T05:05:56.215387Z
+1524
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3178
+

+OracleCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+d5b0cd8cb37c1850b917a2e8aad044bf
+2010-01-30T01:55:20.556392Z
+1026
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2457
+

+MySQLCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+75f9a85c91912d1b00f3db811bb50d02
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7574
+

+JavaScriptCodec.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+e6eda3d6e73f5b581779b9efd12ce9ac
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6496
+

+DB2Codec.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+3feb3563eab84deb882919cc9948a235
+2010-10-26T03:12:37.465749Z
+1629
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1634
+

+HashTrie.java
+file
+
+
+
+
+2014-02-18T16:19:53.029967Z
+c1435aa54252e9f18638a99619649c84
+2010-09-09T05:05:56.215387Z
+1524
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+14064
+

diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/prop-base/Hex.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/prop-base/Hex.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/prop-base/Hex.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Base64.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Base64.java.svn-base
new file mode 100644
index 0000000..3f2d384
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Base64.java.svn-base
@@ -0,0 +1,1855 @@
+package org.owasp.esapi.codecs;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+// CHECKME: Version at http://iharder.net/base64 is up to v2.3.3. Some semantic changes
+// starting with v2.3. Should we upgrade and then add ESAPI logging or stay at 2.2.2 base?
+// I think that really depends on how much OWASP ESAPI plans on tracking changes to this
+// version vs. if the plan was just to fork from it and maintain OWASP's own version.
+// At this point, I think I prefer split from tracking Harder's original, but I'm easily
+// persuaded otherwise. - Kevin Wall
+
+/**
+ * <p>Encodes and decodes to and from Base64 notation.</p>
+ * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
+ *
+ * <p>The <tt>options</tt> parameter, which appears in a few places, is used to pass 
+ * several pieces of information to the encoder. In the "higher level" methods such as 
+ * encodeBytes( bytes, options ) the options parameter can be used to indicate such 
+ * things as first gzipping the bytes before encoding them, not inserting linefeeds 
+ * (though that breaks strict Base64 compatibility), and encoding using the URL-safe 
+ * and Ordered dialects.</p>
+ *
+ * <p>The constants defined in Base64 can be OR-ed together to combine options, so you 
+ * might make a call like this:</p>
+ *
+ * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES );</code>
+ *
+ * <p>to compress the data before encoding it and then making the output have no newline characters.</p>
+ *
+ *
+ * <p>
+ * Change Log:
+ * </p>
+ * <ul>
+ *  <li>v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the
+ *   Base64.InputStream class to encode and decode on the fly which uses
+ *   less memory than encoding/decoding an entire file into memory before writing.</li>
+ *  <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
+ *   when using very small files (~< 40 bytes).</li>
+ *  <li>v2.2 - Added some helper methods for encoding/decoding directly from
+ *   one file to the next. Also added a main() method to support command line
+ *   encoding/decoding from one file to the next. Also added these Base64 dialects:
+ *   <ol>
+ *   <li>The default is RFC3548 format.</li>
+ *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
+ *   URL and file name friendly format as described in Section 4 of RFC3548.
+ *   http://www.faqs.org/rfcs/rfc3548.html</li>
+ *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
+ *   URL and file name friendly format that preserves lexical ordering as described
+ *   in http://www.faqs.org/qa/rfcc-1940.html</li>
+ *   </ol>
+ *   Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a>
+ *   for contributing the new Base64 dialects.
+ *  </li>
+ * 
+ *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
+ *   some convenience methods for reading and writing to and from files.</li>
+ *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
+ *   with other encodings (like EBCDIC).</li>
+ *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
+ *   encoded data was a single byte.</li>
+ *  <li>v2.0 - I got rid of methods that used booleans to set options. 
+ *   Now everything is more consolidated and cleaner. The code now detects
+ *   when data that's being decoded is gzip-compressed and will decompress it
+ *   automatically. Generally things are cleaner. You'll probably have to
+ *   change some method calls that you were making to support the new
+ *   options format (<tt>int</tt>s that you "OR" together).</li>
+ *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
+ *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
+ *   Added the ability to "suspend" encoding in the Output Stream so        
+ *   you can turn on and off the encoding if you need to embed base64       
+ *   data in an otherwise "normal" stream (like an XML file).</li>  
+ *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
+ *      This helps when using GZIP streams.
+ *      Added the ability to GZip-compress objects before encoding them.</li>
+ *  <li>v1.4 - Added helper methods to read/write files.</li>
+ *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
+ *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
+ *      where last buffer being read, if not completely full, was not returned.</li>
+ *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
+ *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
+ * </ul>
+ *
+ * <p>
+ * I am placing this code in the Public Domain. Do with it as you will.
+ * This software comes with no guarantees or warranties but with
+ * plenty of well-wishing instead!
+ * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
+ * periodically to check for updates or to contribute improvements.
+ * </p>
+ *
+ * @author Robert Harder
+ * @author rob at iharder.net
+ * @version 2.2.2
+ */
+public class Base64
+{
+    
+/* ********  P U B L I C   F I E L D S  ******** */   
+    
+    
+    /** No options specified. Value is zero. */
+    public final static int NO_OPTIONS = 0;
+    
+    /** Specify encoding. */
+    public final static int ENCODE = 1;
+    
+    
+    /** Specify decoding. */
+    public final static int DECODE = 0;
+    
+    
+    /** Specify that data should be gzip-compressed. */
+    public final static int GZIP = 2;
+    
+    
+    /** Don't break lines when encoding (violates strict Base64 specification) */
+    public final static int DONT_BREAK_LINES = 8;
+	
+	/** 
+	 * Encode using Base64-like encoding that is URL- and Filename-safe as described
+	 * in Section 4 of RFC3548: 
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * It is important to note that data encoded this way is <em>not</em> officially valid Base64, 
+	 * or at the very least should not be called Base64 without also specifying that is
+	 * was encoded using the URL- and Filename-safe dialect.
+	 */
+	 public final static int URL_SAFE = 16;
+	 
+	 
+	 /**
+	  * Encode using the special "ordered" dialect of Base64 described here:
+	  * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	  */
+	 public final static int ORDERED = 32;
+    
+    
+/* ********  P R I V A T E   F I E L D S  ******** */  
+    
+    
+    /** Maximum line length (76) of Base64 output. */
+    private final static int MAX_LINE_LENGTH = 76;
+    
+    
+    /** The equals sign (=) as a byte. */
+    private final static byte EQUALS_SIGN = (byte)'=';
+    
+    
+    /** The new line character (\n) as a byte. */
+    private final static byte NEW_LINE = (byte)'\n';
+    
+    
+    /** Preferred encoding. */
+    private final static String PREFERRED_ENCODING = "UTF-8";
+    
+    /** End of line character. */
+    private final static String EOL = System.getProperty("line.separator", "\n");
+	
+	
+    // I think I end up not using the BAD_ENCODING indicator.
+    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
+    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
+    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
+	
+    private static final Logger logger = ESAPI.getLogger("Base64");
+    
+	
+/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */	
+    
+    /** The 64 valid Base64 values. */
+    //private final static byte[] ALPHABET;
+	/* Host platform me be something funny like EBCDIC, so we hard code these values. */
+	private final static byte[] _STANDARD_ALPHABET =
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
+        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
+        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
+    };
+	
+    
+    /** 
+     * Translates a Base64 value to either its 6-bit reconstruction value
+     * or a negative number indicating some other meaning.
+     **/
+    private final static byte[] _STANDARD_DECODABET =
+    {   
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+        -5,-5,                                      // Whitespace: Tab and Linefeed
+        -9,-9,                                      // Decimal 11 - 12
+        -5,                                         // Whitespace: Carriage Return
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+        -5,                                         // Whitespace: Space
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+        62,                                         // Plus sign at decimal 43
+        -9,-9,-9,                                   // Decimal 44 - 46
+        63,                                         // Slash at decimal 47
+        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+        -9,-9,-9,                                   // Decimal 58 - 60
+        -1,                                         // Equals sign at decimal 61
+        -9,-9,-9,                                      // Decimal 62 - 64
+        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
+        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+        -9,-9,-9,-9                                 // Decimal 123 - 126
+        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+	
+	
+/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
+	
+	/**
+	 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
+	 */
+    private final static byte[] _URL_SAFE_ALPHABET =
+    {
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
+      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
+    };
+	
+	/**
+	 * Used in decoding URL- and Filename-safe dialects of Base64.
+	 */
+    private final static byte[] _URL_SAFE_DECODABET =
+    {   
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      62,                                         // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      63,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+
+
+/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
+
+	/**
+	 * I don't get the point of this technique, but it is described here:
+	 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	 */
+    private final static byte[] _ORDERED_ALPHABET =
+    {
+      (byte)'-',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
+      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'_',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
+    };
+	
+	/**
+	 * Used in decoding the "ordered" dialect of Base64.
+	 */
+    private final static byte[] _ORDERED_DECODABET =
+    {   
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      0,                                          // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
+      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      37,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
+      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+	
+/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
+
+
+	/**
+	 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URLSAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getAlphabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;
+		else return _STANDARD_ALPHABET;
+		
+	}	// end getAlphabet
+	
+	
+	/**
+	 * Returns one of the _SOMETHING_DECODABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URL_SAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getDecodabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;
+		else return _STANDARD_DECODABET;
+		
+	}	// end getAlphabet
+        
+
+    
+    /** Defeats instantiation. */
+    private Base64(){}
+    
+
+    /**
+     * Encodes or decodes two files from the command line;
+     * <strong>feel free to delete this method (in fact you probably should)
+     * if you're embedding this code into a larger program</strong>.
+     * @param args
+     */
+    public final static void main( String[] args )
+    {
+        if( args.length < 3 ){
+            usage("Not enough arguments.");
+        }   // end if: args.length < 3
+        else {
+            String flag = args[0];
+            String infile = args[1];
+            String outfile = args[2];
+            if( flag.equals( "-e" ) ){
+                Base64.encodeFileToFile( infile, outfile );
+            }   // end if: encode
+            else if( flag.equals( "-d" ) ) {
+                Base64.decodeFileToFile( infile, outfile );
+            }   // end else if: decode    
+            else {
+                usage( "Unknown flag: " + flag );
+            }   // end else    
+        }   // end else
+    }   // end main
+
+    /**
+     * Prints command line usage.
+     *
+     * @param msg A message to include with usage info.
+     */
+    private final static void usage( String msg )
+    {
+        System.err.println( msg );
+        System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" );
+    }   // end usage
+    
+    
+/* ********  E N C O D I N G   M E T H O D S  ******** */    
+    
+    
+    /**
+     * Encodes up to the first three bytes of array <var>threeBytes</var>
+     * and returns a four-byte array in Base64 notation.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.
+     * The array <var>threeBytes</var> needs only be as big as
+     * <var>numSigBytes</var>.
+     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
+     *
+     * @param b4 A reusable byte array to reduce array instantiation
+     * @param threeBytes the array to convert
+     * @param numSigBytes the number of significant bytes in your array
+     * @return four byte array in Base64 notation.
+     * @since 1.5.1
+     */
+    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )
+    {
+        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
+        return b4;
+    }   // end encode3to4
+
+    
+    /**
+     * <p>Encodes up to three bytes of the array <var>source</var>
+     * and writes the resulting four Base64 bytes to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 3 for
+     * the <var>source</var> array or <var>destOffset</var> + 4 for
+     * the <var>destination</var> array.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.</p>
+	 * <p>This is the lowest level of the encoding methods with
+	 * all possible parameters.</p>
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param numSigBytes the number of significant bytes in your array
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the <var>destination</var> array
+     * @since 1.3
+     */
+    private static byte[] encode3to4( 
+     byte[] source, int srcOffset, int numSigBytes,
+     byte[] destination, int destOffset, int options )
+    {
+		byte[] ALPHABET = getAlphabet( options ); 
+	
+        //           1         2         3  
+        // 01234567890123456789012345678901 Bit position
+        // --------000000001111111122222222 Array position from threeBytes
+        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
+        //          >>18  >>12  >> 6  >> 0  Right shift necessary
+        //                0x3f  0x3f  0x3f  Additional AND
+        
+        // Create buffer with zero-padding if there are only one or two
+        // significant bytes passed in the array.
+        // We have to shift left 24 in order to flush out the 1's that appear
+        // when Java treats a value as negative that is cast from a byte to an int.
+        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
+                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
+                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
+
+        switch( numSigBytes )
+        {
+            case 3:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
+                return destination;
+                
+            case 2:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            case 1:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = EQUALS_SIGN;
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            default:
+                return destination;
+        }   // end switch
+    }   // end encode3to4
+    
+    
+    
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * The object is not GZip-compressed before being encoded.
+     *
+     * @param serializableObject The object to encode
+     * @return The Base64-encoded object
+     * @since 1.4
+     */
+    public static String encodeObject( java.io.Serializable serializableObject )
+    {
+        return encodeObject( serializableObject, NO_OPTIONS );
+    }   // end encodeObject
+    
+
+
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     * @param serializableObject The object to encode
+     * @param options Specified options
+     * @return The Base64-encoded object
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeObject( java.io.Serializable serializableObject, int options )
+    {
+        // Streams
+        java.io.ByteArrayOutputStream  baos  = null; 
+        java.io.OutputStream           b64os = null; 
+        java.io.ObjectOutputStream     oos   = null; 
+        java.util.zip.GZIPOutputStream gzos  = null;
+        
+        // Isolate options
+        int gzip           = (options & GZIP);
+        //int dontBreakLines = (options & DONT_BREAK_LINES);
+        
+        try
+        {
+            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+            baos  = new java.io.ByteArrayOutputStream();
+            b64os = new Base64.OutputStream( baos, ENCODE | options );
+    
+            // GZip?
+            if( gzip == GZIP )
+            {
+                gzos = new java.util.zip.GZIPOutputStream( b64os );
+                oos  = new java.io.ObjectOutputStream( gzos );
+            }   // end if: gzip
+            else
+                oos   = new java.io.ObjectOutputStream( b64os );
+            
+            oos.writeObject( serializableObject );
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e );
+            return null;
+        }   // end catch
+        finally
+        {
+            try{ oos.close();   } catch( Exception e ){}
+            try{ gzos.close();  } catch( Exception e ){}
+            try{ b64os.close(); } catch( Exception e ){}
+            try{ baos.close();  } catch( Exception e ){}
+        }   // end finally
+        
+        // Return value according to relevant encoding.
+        try 
+        {
+            return new String( baos.toByteArray(), PREFERRED_ENCODING );
+        }   // end try
+        catch (java.io.UnsupportedEncodingException uue)
+        {
+            return new String( baos.toByteArray() );
+        }   // end catch
+        
+    }   // end encode
+    
+    
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @return The Base64-encoded resulting string
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source )
+    {
+        return encodeBytes( source, 0, source.length, NO_OPTIONS );
+    }   // end encodeBytes
+    
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param options Specified options
+     * @return The Base64-encoded resulting string
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int options )
+    {   
+        return encodeBytes( source, 0, source.length, options );
+    }   // end encodeBytes
+    
+    
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @return The Base64-encoded resulting string
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source, int off, int len )
+    {
+        return encodeBytes( source, off, len, NO_OPTIONS );
+    }   // end encodeBytes
+    
+    
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+     * @return The Base64-encoded resulting string
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int off, int len, int options )
+    {
+        // Isolate options
+        int dontBreakLines = ( options & DONT_BREAK_LINES );
+        int gzip           = ( options & GZIP   );
+        
+        // Compress?
+        if( gzip == GZIP )
+        {
+            java.io.ByteArrayOutputStream  baos  = null;
+            java.util.zip.GZIPOutputStream gzos  = null;
+            Base64.OutputStream            b64os = null;
+            
+    
+            try
+            {
+                // GZip -> Base64 -> ByteArray
+                baos = new java.io.ByteArrayOutputStream();
+                b64os = new Base64.OutputStream( baos, ENCODE | options );
+                gzos  = new java.util.zip.GZIPOutputStream( b64os ); 
+            
+                gzos.write( source, off, len );
+                gzos.close();
+            }   // end try
+            catch( java.io.IOException e )
+            {
+                logger.error( Logger.SECURITY_FAILURE, "Problem writing gzip stream", e );
+                return null;
+            }   // end catch
+            finally
+            {
+                try{ gzos.close();  } catch( Exception e ){}
+                try{ b64os.close(); } catch( Exception e ){}
+                try{ baos.close();  } catch( Exception e ){}
+            }   // end finally
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( baos.toByteArray(), PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( baos.toByteArray() );
+            }   // end catch
+        }   // end if: compress
+        
+        // Else, don't compress. Better not to use streams at all then.
+        else
+        {
+            // Convert option to boolean in way that code likes it.
+            boolean breakLines = dontBreakLines == 0;
+            
+            int    len43   = len * 4 / 3;
+            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
+                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
+                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      
+            int d = 0;
+            int e = 0;
+            int len2 = len - 2;
+            int lineLength = 0;
+            for( ; d < len2; d+=3, e+=4 )
+            {
+                encode3to4( source, d+off, 3, outBuff, e, options );
+
+                lineLength += 4;
+                if( breakLines && lineLength == MAX_LINE_LENGTH )
+                {   
+                    outBuff[e+4] = NEW_LINE;
+                    e++;
+                    lineLength = 0;
+                }   // end if: end of line
+            }   // en dfor: each piece of array
+
+            if( d < len )
+            {
+                encode3to4( source, d+off, len - d, outBuff, e, options );
+                e += 4;
+            }   // end if: some padding needed
+
+            
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( outBuff, 0, e, PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( outBuff, 0, e );
+            }   // end catch
+            
+        }   // end else: don't compress
+        
+    }   // end encodeBytes
+    
+
+    
+    
+    
+/* ********  D E C O D I N G   M E T H O D S  ******** */
+    
+    
+    /**
+     * Decodes four bytes from array <var>source</var>
+     * and writes the resulting bytes (up to three of them)
+     * to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 4 for
+     * the <var>source</var> array or <var>destOffset</var> + 3 for
+     * the <var>destination</var> array.
+     * This method returns the actual number of bytes that 
+     * were converted from the Base64 encoding.
+	 * <p>This is the lowest level of the decoding methods with
+	 * all possible parameters.</p>
+     * 
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+	 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+     * @return the number of decoded bytes converted
+     * @since 1.3
+     */
+    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options )
+    {
+		byte[] DECODABET = getDecodabet( options ); 
+	
+        // Example: Dk==
+        if( source[ srcOffset + 2] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
+            
+            destination[ destOffset ] = (byte)( outBuff >>> 16 );
+            return 1;
+        }
+        
+        // Example: DkL=
+        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
+            
+            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
+            return 2;
+        }
+        
+        // Example: DkLE
+        else
+        {
+            try{
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
+                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
+
+            
+            destination[ destOffset     ] = (byte)( outBuff >> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
+            destination[ destOffset + 2 ] = (byte)( outBuff       );
+
+            return 3;
+            }catch( Exception e){
+            	
+        // Remove these after checking -- for context only.
+                // logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
+            	
+            	// CHECKME: I replaced the 5 separate logger.error() calls above with a single logger.error() call so they can't
+            	// become interleaved with other log entries from other threads. Normally this would have placed log entries
+            	// on separate lines, so I also added line terminators here as well. (Probably don't want it all on one single
+            	// really long log entry, do we?) Anyhow, somebody should check the formatting to ensure that it's
+            	// esthetically pleasing, etc. But this works for me. I'm also OK if you want to remove all the line terminators
+            	// in which case the declaration for EOL should be removed as well.		- Kevin Wall
+                
+                StringBuilder sb = new StringBuilder("Problem writing object:");
+                sb.append(EOL);
+                sb.append( source[srcOffset]   ).append(": ").append( ( DECODABET[ source[ srcOffset     ] ]  ) ).append(EOL);
+                sb.append( source[srcOffset+1] ).append(": ").append( ( DECODABET[ source[ srcOffset + 1 ] ]  ) ).append(EOL);
+                sb.append( source[srcOffset+2] ).append(": ").append( ( DECODABET[ source[ srcOffset + 2 ] ]  ) ).append(EOL);
+                sb.append( source[srcOffset+3] ).append(": ").append( ( DECODABET[ source[ srcOffset + 3 ] ]  ) ).append(EOL);
+                
+                logger.error( Logger.SECURITY_FAILURE, sb.toString(), e );
+                return -1;
+            }   // end catch
+        }
+    }   // end decodeToBytes
+    
+    
+    
+    
+    /**
+     * Very low-level access to decoding ASCII characters in
+     * the form of a byte array. Does not support automatically
+     * gunzipping or any other "fancy" features.
+     *
+     * @param source The Base64 encoded data
+     * @param off    The offset of where to begin decoding
+     * @param len    The length of characters to decode
+     * @param options
+     * @return decoded data
+     * @since 1.3
+     */
+    public static byte[] decode( byte[] source, int off, int len, int options )
+    {
+		byte[] DECODABET = getDecodabet( options );
+	
+        int    len34   = len * 3 / 4;
+        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+        int    outBuffPosn = 0;
+        
+        byte[] b4        = new byte[4];
+        int    b4Posn    = 0;
+        int    i         = 0;
+        byte   sbiCrop   = 0;
+        byte   sbiDecode = 0;
+        for( i = off; i < off+len; i++ )
+        {
+            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
+            sbiDecode = DECODABET[ sbiCrop ];
+            
+            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
+            {
+                if( sbiDecode >= EQUALS_SIGN_ENC )
+                {
+                    b4[ b4Posn++ ] = sbiCrop;
+                    if( b4Posn > 3 )
+                    {
+                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
+                        b4Posn = 0;
+                        
+                        // If that was the equals sign, break out of 'for' loop
+                        if( sbiCrop == EQUALS_SIGN )
+                            break;
+                    }   // end if: quartet built
+                    
+                }   // end if: equals sign or better
+                
+            }   // end if: white space, equals sign or better
+            else
+            {
+            	logger.error( Logger.SECURITY_FAILURE, "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
+                return null;
+            }   // end else: 
+        }   // each input character
+                                   
+        byte[] out = new byte[ outBuffPosn ];
+        System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
+        return out;
+    }   // end decode
+    
+    
+	
+	
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s )
+	{
+		return decode( s, NO_OPTIONS );
+	}
+    
+    
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+	 * @param options encode options such as URL_SAFE
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s, int options )
+    {   
+        byte[] bytes;
+        try
+        {
+            bytes = s.getBytes( PREFERRED_ENCODING );
+        }
+        catch( java.io.UnsupportedEncodingException uee )
+        {
+            bytes = s.getBytes();	// Uses native encoding
+            // CHECKME: Is this correct? I think it should be a warning instead of an error since nothing
+            // is re-thrown. I do think that *some* sort of logging is in order here  especially since UTF-8 should
+            // always be available on all platforms. If it's not, then all bets are off on your runtime env. - Kevin Wall
+            logger.warning( Logger.SECURITY_FAILURE, "Problem decoding string using " +
+            			  PREFERRED_ENCODING + "; substituting native platform encoding instead", uee );
+        }
+        
+        // Decode
+        bytes = decode( bytes, 0, bytes.length, options );
+        
+        
+        // Check to see if it's gzip-compressed
+        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+        if( bytes != null && bytes.length >= 4 )
+        {
+            
+            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);       
+            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) 
+            {
+                java.io.ByteArrayInputStream  bais = null;
+                java.util.zip.GZIPInputStream gzis = null;
+                java.io.ByteArrayOutputStream baos = null;
+                byte[] buffer = new byte[2048];
+                int    length = 0;
+
+                try
+                {
+                    baos = new java.io.ByteArrayOutputStream();
+                    bais = new java.io.ByteArrayInputStream( bytes );
+                    gzis = new java.util.zip.GZIPInputStream( bais );
+
+                    while( ( length = gzis.read( buffer ) ) >= 0 )
+                    {
+                        baos.write(buffer,0,length);
+                    }   // end while: reading input
+
+                    // No error? Get new bytes.
+                    bytes = baos.toByteArray();
+
+                }   // end try
+                catch( java.io.IOException e )
+                {
+                    // Just return originally-decoded bytes
+                }   // end catch
+                finally
+                {
+                    try{ baos.close(); } catch( Exception e ){}
+                    try{ gzis.close(); } catch( Exception e ){}
+                    try{ bais.close(); } catch( Exception e ){}
+                }   // end finally
+
+            }   // end if: gzipped
+        }   // end if: bytes.length >= 2
+        
+        return bytes;
+    }   // end decode
+
+
+    
+
+    /**
+     * Attempts to decode Base64 data and deserialize a Java
+     * Object within. Returns <tt>null</tt> if there was an error.
+     *
+     * @param encodedObject The Base64 data to decode
+     * @return The decoded and deserialized object
+     * @since 1.5
+     */
+    public static Object decodeToObject( String encodedObject )
+    {
+        // Decode and gunzip if necessary
+        byte[] objBytes = decode( encodedObject );
+        
+        java.io.ByteArrayInputStream  bais = null;
+        java.io.ObjectInputStream     ois  = null;
+        Object obj = null;
+        
+        try
+        {
+            bais = new java.io.ByteArrayInputStream( objBytes );
+            ois  = new java.io.ObjectInputStream( bais );
+        
+            obj = ois.readObject();
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e );
+            obj = null;
+        }   // end catch
+        catch( java.lang.ClassNotFoundException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e );
+            obj = null;
+        }   // end catch
+        finally
+        {
+            try{ bais.close(); } catch( Exception e ){}
+            try{ ois.close();  } catch( Exception e ){}
+        }   // end finally
+        
+        return obj;
+    }   // end decodeObject
+    
+    
+    
+    /**
+     * Convenience method for encoding data to a file.
+     *
+     * @param dataToEncode byte array of data to encode in base64 form
+     * @param filename Filename for saving encoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean encodeToFile( byte[] dataToEncode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+            bos = new Base64.OutputStream( 
+                      new java.io.FileOutputStream( filename ), Base64.ENCODE );
+            bos.write( dataToEncode );
+            success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+            try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+        
+        return success;
+    }   // end encodeToFile
+    
+    
+    /**
+     * Convenience method for decoding data to a file.
+     *
+     * @param dataToDecode Base64-encoded data as a string
+     * @param filename Filename for saving decoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean decodeToFile( String dataToDecode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+                bos = new Base64.OutputStream( 
+                          new java.io.FileOutputStream( filename ), Base64.DECODE );
+                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
+                success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+                try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+        
+        return success;
+    }   // end decodeToFile
+    
+    
+    
+    
+    /**
+     * Convenience method for reading a base64-encoded
+     * file and decoding it.
+     *
+     * @param filename Filename for reading encoded data
+     * @return decoded byte array or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static byte[] decodeFromFile( String filename )
+    {
+        byte[] decodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = null;
+            int length   = 0;
+            int numBytes = 0;
+            
+            // Check for size of file
+            if( file.length() > Integer.MAX_VALUE )
+            {
+                logger.error( Logger.SECURITY_FAILURE, "File is too big for this convenience method (" + file.length() + " bytes)." );
+                return null;
+            }   // end if: file too big for int index
+            buffer = new byte[ (int)file.length() ];
+            
+            // Open a stream
+            bis = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( file ) ), Base64.DECODE );
+            
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+            
+            // Save in a variable to return
+            decodedData = new byte[ length ];
+            System.arraycopy( buffer, 0, decodedData, 0, length );
+            
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Error decoding from file " + filename, e );
+        }   // end catch: IOException
+        finally
+        {
+            try{ if (bis != null ) bis.close(); } catch( Exception e) {}
+        }   // end finally
+        
+        return decodedData;
+    }   // end decodeFromFile
+    
+    
+    
+    /**
+     * Convenience method for reading a binary file
+     * and base64-encoding it.
+     *
+     * @param filename Filename for reading binary data
+     * @return base64-encoded string or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static String encodeFromFile( String filename )
+    {
+        String encodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)
+            int length   = 0;
+            int numBytes = 0;
+            
+            // Open a stream
+            bis = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( file ) ), Base64.ENCODE );
+            
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+            
+            // Save in a variable to return
+            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
+                
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Error encoding from file " + filename, e );
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {}
+        }   // end finally
+        
+        return encodedData;
+        }   // end encodeFromFile
+    
+    
+    
+    
+    /**
+     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean encodeFileToFile( String infile, String outfile )
+    {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try{
+            in  = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( infile ) ), 
+                      Base64.ENCODE );
+            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while( ( read = in.read(buffer) ) >= 0 ){
+                out.write( buffer,0,read );
+            }   // end while: through file
+            success = true;
+        } catch( java.io.IOException exc ){
+            logger.error( Logger.SECURITY_FAILURE, "Problem encoding file to file", exc );
+        } finally{
+            try{ in.close();  } catch( Exception exc ){}
+            try{ out.close(); } catch( Exception exc ){}
+        }   // end finally
+        
+        return success;
+    }   // end encodeFileToFile
+    
+    
+    
+    /**
+     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean decodeFileToFile( String infile, String outfile )
+    {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try{
+            in  = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( infile ) ), 
+                      Base64.DECODE );
+            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while( ( read = in.read(buffer) ) >= 0 ){
+                out.write( buffer,0,read );
+            }   // end while: through file
+            success = true;
+        } catch( java.io.IOException exc ){
+            logger.error( Logger.SECURITY_FAILURE, "Problem decoding file to file", exc );
+        } finally{
+            try{ in.close();  } catch( Exception exc ){}
+            try{ out.close(); } catch( Exception exc ){}
+        }   // end finally
+        
+        return success;
+    }   // end decodeFileToFile
+    
+    
+    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
+    
+    
+    
+    /**
+     * A {@link Base64.InputStream} will read data from another
+     * <tt>java.io.InputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class InputStream extends java.io.FilterInputStream
+    {
+        private boolean encode;         // Encoding or decoding
+        private int     position;       // Current position in the buffer
+        private byte[]  buffer;         // Small buffer holding converted data
+        private int     bufferLength;   // Length of buffer (3 or 4)
+        private int     numSigBytes;    // Number of meaningful bytes in the buffer
+        private int     lineLength;
+        private boolean breakLines;     // Break lines at less than 80 characters
+		private int     options;        // Record options used to create the stream.
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+        
+        
+        /**
+         * Constructs a {@link Base64.InputStream} in DECODE mode.
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @since 1.3
+         */
+        public InputStream( java.io.InputStream in )
+        {   
+            this( in, DECODE );
+        }   // end constructor
+        
+        
+        /**
+         * Constructs a {@link Base64.InputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
+         *
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @param options Specified options
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 2.0
+         */
+        public InputStream( java.io.InputStream in, int options )
+        {   
+            super( in );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 4 : 3;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = -1;
+            this.lineLength   = 0;
+			this.options      = options; // Record for later, mostly to determine which alphabet to use
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+        
+        /**
+         * Reads enough of the input stream to convert
+         * to/from Base64 and returns the next byte.
+         *
+         * @return next byte
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public int read() throws java.io.IOException 
+        { 
+            // Do we need to get data?
+            if( position < 0 )
+            {
+                if( encode )
+                {
+                    byte[] b3 = new byte[3];
+                    int numBinaryBytes = 0;
+                    for( int i = 0; i < 3; i++ )
+                    {
+                        try
+                        { 
+                            int b = in.read();
+                            
+                            // If end of stream, b is -1.
+                            if( b >= 0 )
+                            {
+                                b3[i] = (byte)b;
+                                numBinaryBytes++;
+                            }   // end if: not end of stream
+                            
+                        }   // end try: read
+                        catch( java.io.IOException e )
+                        {   
+                            // Only a problem if we got no data at all.
+                            if( i == 0 )
+                                throw e;
+                            
+                        }   // end catch
+                    }   // end for: each needed input byte
+                    
+                    if( numBinaryBytes > 0 )
+                    {
+                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
+                        position = 0;
+                        numSigBytes = 4;
+                    }   // end if: got data
+                    else
+                    {
+                        return -1;
+                    }   // end else
+                }   // end if: encoding
+                
+                // Else decoding
+                else
+                {
+                    byte[] b4 = new byte[4];
+                    int i = 0;
+                    for( i = 0; i < 4; i++ )
+                    {
+                        // Read four "meaningful" bytes:
+                        int b = 0;
+                        do{ b = in.read(); }
+                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
+                        
+                        if( b < 0 )
+                            break; // Reads a -1 if end of stream
+                        
+                        b4[i] = (byte)b;
+                    }   // end for: each needed input byte
+                    
+                    if( i == 4 )
+                    {
+                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );
+                        position = 0;
+                    }   // end if: got four characters
+                    else if( i == 0 ){
+                        return -1;
+                    }   // end else if: also padded correctly
+                    else
+                    {
+                        // Must have broken out from above.
+                        throw new java.io.IOException( "Improperly padded Base64 input." );
+                    }   // end 
+                    
+                }   // end else: decode
+            }   // end else: get data
+            
+            // Got data?
+            if( position >= 0 )
+            {
+                // End of relevant data?
+                if( /*!encode &&*/ position >= numSigBytes )
+                    return -1;
+                
+                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
+                {
+                    lineLength = 0;
+                    return '\n';
+                }   // end if
+                else
+                {
+                    lineLength++;   // This isn't important when decoding
+                                    // but throwing an extra "if" seems
+                                    // just as wasteful.
+                    
+                    int b = buffer[ position++ ];
+
+                    if( position >= bufferLength )
+                        position = -1;
+
+                    return b & 0xFF; // This is how you "cast" a byte that's
+                                     // intended to be unsigned.
+                }   // end else
+            }   // end if: position >= 0
+            
+            // Else error
+            else
+            {   
+                // When JDK1.4 is more accepted, use an assertion here.
+                throw new java.io.IOException( "Error in Base64 code reading stream." );
+            }   // end else
+        }   // end read
+        
+        
+        /**
+         * Calls {@link #read()} repeatedly until the end of stream
+         * is reached or <var>len</var> bytes are read.
+         * Returns number of bytes read into array or -1 if
+         * end of stream is encountered.
+         *
+         * @param dest array to hold values
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @return bytes read into array or -1 if end of stream is encountered.
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public int read( byte[] dest, int off, int len ) throws java.io.IOException
+        {
+            int i;
+            int b;
+            for( i = 0; i < len; i++ )
+            {
+                b = read();
+                
+                //if( b < 0 && i == 0 )
+                //    return -1;
+                
+                if( b >= 0 )
+                    dest[off + i] = (byte)b;
+                else if( i == 0 )
+                    return -1;
+                else
+                    break; // Out of 'for' loop
+            }   // end for: each byte read
+            return i;
+        }   // end read
+        
+    }   // end inner class InputStream
+    
+    
+    
+    
+    
+    
+    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
+    
+    
+    
+    /**
+     * A {@link Base64.OutputStream} will write data to another
+     * <tt>java.io.OutputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class OutputStream extends java.io.FilterOutputStream
+    {
+        private boolean encode;
+        private int     position;
+        private byte[]  buffer;
+        private int     bufferLength;
+        private int     lineLength;
+        private boolean breakLines;
+        private byte[]  b4; // Scratch used in a few places
+        private boolean suspendEncoding;
+		private int options; // Record for later
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+        
+        /**
+         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out )
+        {   
+            this( out, ENCODE );
+        }   // end constructor
+        
+        
+        /**
+         * Constructs a {@link Base64.OutputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @param options Specified options.
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out, int options )
+        {   
+            super( out );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 3 : 4;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = 0;
+            this.lineLength   = 0;
+            this.suspendEncoding = false;
+            this.b4           = new byte[4];
+			this.options      = options;
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+        
+        
+        /**
+         * Writes the byte to the output stream after
+         * converting to/from Base64 notation.
+         * When encoding, bytes are buffered three
+         * at a time before the output stream actually
+         * gets a write() call.
+         * When decoding, bytes are buffered four
+         * at a time.
+         *
+         * @param theByte the byte to write
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public void write(int theByte) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theByte );
+                return;
+            }   // end if: supsended
+            
+            // Encode?
+            if( encode )
+            {
+                buffer[ position++ ] = (byte)theByte;
+                if( position >= bufferLength )  // Enough to encode.
+                {
+                    out.write( encode3to4( b4, buffer, bufferLength, options ) );
+
+                    lineLength += 4;
+                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
+                    {
+                        out.write( NEW_LINE );
+                        lineLength = 0;
+                    }   // end if: end of line
+
+                    position = 0;
+                }   // end if: enough to output
+            }   // end if: encoding
+
+            // Else, Decoding
+            else
+            {
+                // Meaningful Base64 character?
+                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )
+                {
+                    buffer[ position++ ] = (byte)theByte;
+                    if( position >= bufferLength )  // Enough to output.
+                    {
+                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );
+                        out.write( b4, 0, len );
+                        //out.write( Base64.decode4to3( buffer ) );
+                        position = 0;
+                    }   // end if: enough to output
+                }   // end if: meaningful base64 character
+                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )
+                {
+                    throw new java.io.IOException( "Invalid character in Base64 data." );
+                }   // end else: not white space either
+            }   // end else: decoding
+        }   // end write
+        
+        
+        
+        /**
+         * Calls {@link #write(int)} repeatedly until <var>len</var> 
+         * bytes are written.
+         *
+         * @param theBytes array from which to read bytes
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theBytes, off, len );
+                return;
+            }   // end if: supsended
+            
+            for( int i = 0; i < len; i++ )
+            {
+                write( theBytes[ off + i ] );
+            }   // end for: each byte written
+            
+        }   // end write
+        
+        
+        
+        /**
+         * Method added by PHIL. [Thanks, PHIL. -Rob]
+         * This pads the buffer without closing the stream.
+         * @throws java.io.IOException
+         */
+        public void flushBase64() throws java.io.IOException 
+        {
+            if( position > 0 )
+            {
+                if( encode )
+                {
+                    out.write( encode3to4( b4, buffer, position, options ) );
+                    position = 0;
+                }   // end if: encoding
+                else
+                {
+                    throw new java.io.IOException( "Base64 input not properly padded." );
+                }   // end else: decoding
+            }   // end if: buffer partially full
+
+        }   // end flush
+
+        
+        /** 
+         * Flushes and closes (I think, in the superclass) the stream. 
+         *
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public void close() throws java.io.IOException
+        {
+            // 1. Ensure that pending characters are written
+            flushBase64();
+
+            // 2. Actually close the stream
+            // Base class both flushes and closes.
+            super.close();
+            
+            buffer = null;
+            out    = null;
+        }   // end close
+        
+        
+        
+        /**
+         * Suspends encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @throws java.io.IOException
+         * @since 1.5.1
+         */
+        public void suspendEncoding() throws java.io.IOException 
+        {
+            flushBase64();
+            this.suspendEncoding = true;
+        }   // end suspendEncoding
+        
+        
+        /**
+         * Resumes encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void resumeEncoding()
+        {
+            this.suspendEncoding = false;
+        }   // end resumeEncoding
+        
+        
+        
+    }   // end inner class OutputStream
+    
+    
+}   // end class Base64
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/CSSCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/CSSCodec.java.svn-base
new file mode 100644
index 0000000..9e5d0af
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/CSSCodec.java.svn-base
@@ -0,0 +1,181 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP) Enterprise Security API
+ * (ESAPI) project. For details, please see <a
+ * href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ * 
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the LICENSE
+ * before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+/**
+ * Implementation of the Codec interface for backslash encoding used in CSS.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class CSSCodec extends Codec
+{
+	private static final Character REPLACEMENT = '\ufffd';
+
+
+    /**
+	 * {@inheritDoc}
+	 *
+     * Returns backslash encoded character.
+     *
+     * @param immune
+     */
+    public String encodeCharacter(char[] immune, Character c) {
+		// check for immune characters
+		if ( containsCharacter(c, immune ) ) {
+			return ""+c;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric(c);
+		if ( hex == null ) {
+			return ""+c;
+		}
+		
+		// return the hex and end in whitespace to terminate
+        return "\\" + hex + " ";
+    }
+
+    
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index,
+	 * or null if no decoding is possible.
+	 */
+	public Character decodeCharacter(PushbackString input)
+	{
+		input.mark();
+		Character first = input.next();
+		if (first == null || first != '\\')
+		{
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if (second == null) {
+			input.reset();
+			return null;
+		}
+
+		/* From css 2.1 spec:
+		 * http://www.w3.org/TR/CSS21/syndata.html#characters
+		 *
+		 * First, inside a string, a backslash followed by a
+		 * newline is ignored (i.e., the string is deemed not
+		 * to contain either the backslash or the newline).
+		 *
+		 * Second, it cancels the meaning of special CSS
+		 * characters. Except within CSS comments, any character
+		 * (except a hexadecimal digit, linefeed, carriage return,
+		 * or form feed) can be escaped with a backslash to
+		 * remove its special meaning. For example, "\"" is a string
+		 * consisting of one double quote. Style sheet
+		 * preprocessors must not remove these backslashes
+		 * from a style sheet since that would change the style
+		 * sheet's meaning.
+		 *
+		 * Third, backslash escapes allow authors to refer to
+		 * characters they cannot easily put in a document. In
+		 * this case, the backslash is followed by at most six
+		 * hexadecimal digits (0..9A..F), which stand for the ISO
+		 * 10646 ([ISO10646]) character with that number, which
+		 * must not be zero. (It is undefined in CSS 2.1 what
+		 * happens if a style sheet does contain a character with
+		 * Unicode codepoint zero.) If a character in the range
+		 * [0-9a-fA-F] follows the hexadecimal number, the end
+		 * of the number needs to be made clear. There are two
+		 * ways to do that:
+		 *
+		 *	1. with a space (or other white space character):
+		 *	"\26 B" ("&B"). In this case, user agents should
+		 *	treat a "CR/LF" pair (U+000D/U+000A) as a single
+		 *	white space character.
+		 *
+		 *	2. by providing exactly 6 hexadecimal digits:
+		 *	"\000026B" ("&B")
+		 *
+		 * In fact, these two methods may be combined. Only one
+		 * white space character is ignored after a hexadecimal
+		 * escape. Note that this means that a "real" space
+		 * after the escape sequence must itself either be
+		 * escaped or doubled.
+		 *
+		 * If the number is outside the range allowed by Unicode
+		 * (e.g., "\110000" is above the maximum 10FFFF allowed in
+		 * current Unicode), the UA may replace the escape with
+		 * the "replacement character" (U+FFFD). If the character
+		 * is to be displayed, the UA should show a visible
+		 * symbol, such as a "missing character" glyph (cf. 15.2,
+		 * point 5).
+		 */
+
+		switch(second)
+		{	// special whitespace cases. I assume they mean
+			// for all of these to qualify as a "new
+			// line." Otherwise there is no specification
+			// of what to do for \f
+			case '\r':
+				if(input.peek('\n'))
+					input.next();
+				// fall through
+			case '\n':
+			case '\f':
+				// bs follwed by new line replaced by nothing
+			case '\u0000':	// skip NUL for now too
+				return decodeCharacter(input);
+		}
+
+		if (!PushbackString.isHexDigit(second))
+		{	// non hex digit
+			return second;
+		}
+
+		// Search for up to 6 hex digits following until a space
+		StringBuilder sb = new StringBuilder();
+		sb.append(second);
+		for (int i = 0; i < 5; i++)
+		{
+			Character c = input.next();
+			if(c == null || Character.isWhitespace(c))
+				break;
+			if(PushbackString.isHexDigit(c))
+				sb.append(c);
+			else
+			{
+				input.pushback(c);
+				break;
+			}
+		}
+		try
+		{
+			// parse the hex digit and create a character
+			int i = Integer.parseInt(sb.toString(), 16);
+	
+			if (Character.isValidCodePoint(i))
+				return (char)i;
+			return REPLACEMENT;
+		}
+		catch (NumberFormatException e)
+		{
+			throw new IllegalStateException("Received a NumberFormateException parsing a string verified to be hex", e);
+        	}
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Codec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Codec.java.svn-base
new file mode 100644
index 0000000..8e5fac8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Codec.java.svn-base
@@ -0,0 +1,159 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * The Codec interface defines a set of methods for encoding and decoding application level encoding schemes,
+ * such as HTML entity encoding and percent encoding (aka URL encoding). Codecs are used in output encoding
+ * and canonicalization.  The design of these codecs allows for character-by-character decoding, which is
+ * necessary to detect double-encoding and the use of multiple encoding schemes, both of which are techniques
+ * used by attackers to bypass validation and bury encoded attacks in data.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public abstract class Codec {
+
+	/**
+	 * Initialize an array to mark which characters are to be encoded. Store the hex
+	 * string for that character to save time later. If the character shouldn't be
+	 * encoded, then store null.
+	 */
+	private static final String[] hex = new String[256];
+
+	static {
+		for ( char c = 0; c < 0xFF; c++ ) {
+			if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ) {
+				hex[c] = null;
+			} else {
+				hex[c] = toHex(c).intern();
+			}
+		}
+	}
+
+
+	/**
+	 * Default constructor
+	 */
+	public Codec() {
+	}
+
+	/**
+	 * Encode a String so that it can be safely used in a specific context.
+	 * 
+	 * @param immune
+	 * @param input
+	 * 		the String to encode
+	 * @return the encoded String
+	 */
+	public String encode(char[] immune, String input) {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			sb.append(encodeCharacter(immune, c));
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Default implementation that should be overridden in specific codecs.
+	 * 
+	 * @param immune
+	 * @param c
+	 * 		the Character to encode
+	 * @return
+	 * 		the encoded Character
+	 */
+	public String encodeCharacter( char[] immune, Character c ) {
+		return ""+c;
+	}
+
+	/**
+	 * Decode a String that was encoded using the encode method in this Class
+	 * 
+	 * @param input
+	 * 		the String to decode
+	 * @return
+	 *		the decoded String
+	 */
+	public String decode(String input) {
+		StringBuilder sb = new StringBuilder();
+		PushbackString pbs = new PushbackString(input);
+		while (pbs.hasNext()) {
+			Character c = decodeCharacter(pbs);
+			if (c != null) {
+				sb.append(c);
+			} else {
+				sb.append(pbs.next());
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Returns the decoded version of the next character from the input string and advances the
+	 * current character in the PushbackString.  If the current character is not encoded, this 
+	 * method MUST reset the PushbackString.
+	 * 
+	 * @param input	the Character to decode
+	 * 
+	 * @return the decoded Character
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		return input.next();
+	}
+
+	/**
+	 * Lookup the hex value of any character that is not alphanumeric.
+	 * @param c The character to lookup.
+	 * @return, return null if alphanumeric or the character code
+	 * 	in hex.
+	 */
+	public static String getHexForNonAlphanumeric(char c)
+	{
+		if(c<0xFF)
+			return hex[c];
+		return toHex(c);
+	}
+
+	public static String toOctal(char c)
+	{
+		return Integer.toOctalString(c);
+	}
+
+	public static String toHex(char c)
+	{
+		return Integer.toHexString(c);
+	}
+
+	/**
+	 * Utility to search a char[] for a specific char.
+	 * 
+	 * @param c
+	 * @param array
+	 * @return
+	 */
+	public static boolean containsCharacter( char c, char[] array ) {
+		for (char ch : array) {
+			if (c == ch) return true;
+		}
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/DB2Codec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/DB2Codec.java.svn-base
new file mode 100644
index 0000000..a99818c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/DB2Codec.java.svn-base
@@ -0,0 +1,68 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for DB2 strings. This function will only protect you from SQLi in limited situations.
+ * 
+ * @author Sivasankar Tanakala (stanakal at TRS.NYC.NY.US)
+ * @since October 26, 2010
+ * @see org.owasp.esapi.Encoder
+ */
+public class DB2Codec extends Codec {
+
+	public String encodeCharacter(char[] immune, Character c) {
+
+		if (c.charValue() == '\'')
+			return "\'\'";
+
+		if (c.charValue() == ';')
+			return ".";
+
+		return "" + c;
+	}
+
+	public Character decodeCharacter(PushbackString input) {
+
+		input.mark();
+		Character first = input.next();
+
+		if (first == null) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+
+		if (first.charValue() != '\'') {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+
+		if (second == null) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+		if (second.charValue() != '\'') {
+			input.reset();
+			return null;
+		}
+
+		return (Character.valueOf('\''));
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/HTMLEntityCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/HTMLEntityCodec.java.svn-base
new file mode 100644
index 0000000..36d5876
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/HTMLEntityCodec.java.svn-base
@@ -0,0 +1,550 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Implementation of the Codec interface for HTML entity encoding.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class HTMLEntityCodec extends Codec
+{
+	private static final char REPLACEMENT_CHAR = '\ufffd';
+	private static final String REPLACEMENT_HEX = "fffd";
+	private static final String REPLACEMENT_STR = "" + REPLACEMENT_CHAR;
+	private static final Map<Character,String> characterToEntityMap = mkCharacterToEntityMap();
+
+	private static final Trie<Character> entityToCharacterTrie = mkEntityToCharacterTrie();
+
+    /**
+     *
+     */
+    public HTMLEntityCodec() {
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+     * Encodes a Character for safe use in an HTML entity field.
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+
+		// check for immune characters
+		if ( containsCharacter(c, immune ) ) {
+			return ""+c;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric(c);
+		if ( hex == null ) {
+			return ""+c;
+		}
+		
+		// check for illegal characters
+		if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) )
+		{
+			hex = REPLACEMENT_HEX;	// Let's entity encode this instead of returning it
+			c = REPLACEMENT_CHAR;
+		}
+		
+		// check if there's a defined entity
+		String entityName = (String) characterToEntityMap.get(c);
+		if (entityName != null) {
+			return "&" + entityName + ";";
+		}
+		
+		// return the hex entity as suggested in the spec
+		return "&#x" + hex + ";";
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal both with and without semi-colon, upper/lower case:
+	 *   &#dddd;
+	 *   &#xhhhh;
+	 *   &name;
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if (first != '&' ) {
+			input.reset();
+			return null;
+		}
+		
+		// test for numeric encodings
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		if (second == '#' ) {
+			// handle numbers
+			Character c = getNumericEntity( input );
+			if ( c != null ) return c;
+		} else if ( Character.isLetter( second.charValue() ) ) {
+			// handle entities
+			input.pushback( second );
+			Character c = getNamedEntity( input );
+			if ( c != null ) return c;
+		}
+		input.reset();
+		return null;
+	}
+	
+	/**
+	 * getNumericEntry checks input to see if it is a numeric entity
+	 * 
+	 * @param input
+	 * 			The input to test for being a numeric entity
+	 *  
+	 * @return
+	 * 			null if input is null, the character of input after decoding
+	 */
+	private Character getNumericEntity( PushbackString input ) {
+		Character first = input.peek();
+		if ( first == null ) return null;
+
+		if (first == 'x' || first == 'X' ) {
+			input.next();
+			return parseHex( input );
+		}
+		return parseNumber( input );
+	}
+
+	/**
+	 * Parse a decimal number, such as those from JavaScript's String.fromCharCode(value)
+	 * 
+	 * @param input
+	 * 			decimal encoded string, such as 65
+	 * @return
+	 * 			character representation of this decimal value, e.g. A 
+	 * @throws NumberFormatException
+	 */
+	private Character parseNumber( PushbackString input ) {
+		StringBuilder sb = new StringBuilder();
+		while( input.hasNext() ) {
+			Character c = input.peek();
+			
+			// if character is a digit then add it on and keep going
+			if ( Character.isDigit( c.charValue() ) ) {
+				sb.append( c );
+				input.next();
+				
+			// if character is a semi-colon, eat it and quit
+			} else if (c == ';' ) {
+				input.next();
+				break;
+				
+			// otherwise just quit
+			} else {
+				break;
+			}
+		}
+		try {
+			int i = Integer.parseInt(sb.toString());
+            if (Character.isValidCodePoint(i)) {
+                return (char) i;
+            }
+		} catch( NumberFormatException e ) {
+			// throw an exception for malformed entity?
+		}
+			return null;
+		}
+	
+	/**
+	 * Parse a hex encoded entity
+	 * 
+	 * @param input
+	 * 			Hex encoded input (such as 437ae;)
+	 * @return
+	 * 			A single character from the string
+	 * @throws NumberFormatException
+	 */
+	private Character parseHex( PushbackString input ) {
+		StringBuilder sb = new StringBuilder();
+		while( input.hasNext() ) {
+			Character c = input.peek();
+			
+			// if character is a hex digit then add it on and keep going
+			if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) {
+				sb.append( c );
+				input.next();
+				
+			// if character is a semi-colon, eat it and quit
+			} else if (c == ';' ) {
+				input.next();
+				break;
+				
+			// otherwise just quit
+			} else {
+				break;
+			}
+		}
+		try {
+			int i = Integer.parseInt(sb.toString(), 16);
+            if (Character.isValidCodePoint(i)) {
+                return (char) i;
+            }
+		} catch( NumberFormatException e ) {
+			// throw an exception for malformed entity?
+		}
+			return null;
+		}
+	
+	/**
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal both with and without semi-colon, upper/lower case:
+	 *   &aa;
+	 *   &aaa;
+	 *   &aaaa;
+	 *   &aaaaa;
+	 *   &aaaaaa;
+	 *   &aaaaaaa;
+	 *
+	 * @param input
+	 * 		A string containing a named entity like "
+	 * @return
+	 * 		Returns the decoded version of the character starting at index, or null if no decoding is possible.
+	 */
+	private Character getNamedEntity( PushbackString input ) {
+		StringBuilder possible = new StringBuilder();
+		Map.Entry<CharSequence,Character> entry;
+		int len;
+		
+		// kludge around PushbackString....
+		len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength());
+		for(int i=0;i<len;i++)
+			possible.append(Character.toLowerCase(input.next()));
+
+		// look up the longest match
+		entry = entityToCharacterTrie.getLongestMatch(possible);
+		if(entry == null)
+			return null;	// no match, caller will reset input
+
+		// fixup input
+		input.reset();
+		input.next();	// read &
+		len = entry.getKey().length();	// what matched's length
+		for(int i=0;i<len;i++)
+			input.next();
+
+		// check for a trailing semicolen
+		if(input.peek(';'))
+			input.next();
+
+		return entry.getValue();
+	}
+
+	/**
+	 * Build a unmodifiable Map from entity Character to Name.
+	 * @return Unmodifiable map.
+	 */
+	private static synchronized Map<Character,String> mkCharacterToEntityMap()
+	{
+		Map<Character, String> map = new HashMap<Character,String>(252);
+
+		map.put((char)34,	"quot");	/* quotation mark */
+		map.put((char)38,	"amp");		/* ampersand */
+		map.put((char)60,	"lt");		/* less-than sign */
+		map.put((char)62,	"gt");		/* greater-than sign */
+		map.put((char)160,	"nbsp");	/* no-break space */
+		map.put((char)161,	"iexcl");	/* inverted exclamation mark */
+		map.put((char)162,	"cent");	/* cent sign */
+		map.put((char)163,	"pound");	/* pound sign */
+		map.put((char)164,	"curren");	/* currency sign */
+		map.put((char)165,	"yen");		/* yen sign */
+		map.put((char)166,	"brvbar");	/* broken bar */
+		map.put((char)167,	"sect");	/* section sign */
+		map.put((char)168,	"uml");		/* diaeresis */
+		map.put((char)169,	"copy");	/* copyright sign */
+		map.put((char)170,	"ordf");	/* feminine ordinal indicator */
+		map.put((char)171,	"laquo");	/* left-pointing double angle quotation mark */
+		map.put((char)172,	"not");		/* not sign */
+		map.put((char)173,	"shy");		/* soft hyphen */
+		map.put((char)174,	"reg");		/* registered sign */
+		map.put((char)175,	"macr");	/* macron */
+		map.put((char)176,	"deg");		/* degree sign */
+		map.put((char)177,	"plusmn");	/* plus-minus sign */
+		map.put((char)178,	"sup2");	/* superscript two */
+		map.put((char)179,	"sup3");	/* superscript three */
+		map.put((char)180,	"acute");	/* acute accent */
+		map.put((char)181,	"micro");	/* micro sign */
+		map.put((char)182,	"para");	/* pilcrow sign */
+		map.put((char)183,	"middot");	/* middle dot */
+		map.put((char)184,	"cedil");	/* cedilla */
+		map.put((char)185,	"sup1");	/* superscript one */
+		map.put((char)186,	"ordm");	/* masculine ordinal indicator */
+		map.put((char)187,	"raquo");	/* right-pointing double angle quotation mark */
+		map.put((char)188,	"frac14");	/* vulgar fraction one quarter */
+		map.put((char)189,	"frac12");	/* vulgar fraction one half */
+		map.put((char)190,	"frac34");	/* vulgar fraction three quarters */
+		map.put((char)191,	"iquest");	/* inverted question mark */
+		map.put((char)192,	"Agrave");	/* Latin capital letter a with grave */
+		map.put((char)193,	"Aacute");	/* Latin capital letter a with acute */
+		map.put((char)194,	"Acirc");	/* Latin capital letter a with circumflex */
+		map.put((char)195,	"Atilde");	/* Latin capital letter a with tilde */
+		map.put((char)196,	"Auml");	/* Latin capital letter a with diaeresis */
+		map.put((char)197,	"Aring");	/* Latin capital letter a with ring above */
+		map.put((char)198,	"AElig");	/* Latin capital letter ae */
+		map.put((char)199,	"Ccedil");	/* Latin capital letter c with cedilla */
+		map.put((char)200,	"Egrave");	/* Latin capital letter e with grave */
+		map.put((char)201,	"Eacute");	/* Latin capital letter e with acute */
+		map.put((char)202,	"Ecirc");	/* Latin capital letter e with circumflex */
+		map.put((char)203,	"Euml");	/* Latin capital letter e with diaeresis */
+		map.put((char)204,	"Igrave");	/* Latin capital letter i with grave */
+		map.put((char)205,	"Iacute");	/* Latin capital letter i with acute */
+		map.put((char)206,	"Icirc");	/* Latin capital letter i with circumflex */
+		map.put((char)207,	"Iuml");	/* Latin capital letter i with diaeresis */
+		map.put((char)208,	"ETH");		/* Latin capital letter eth */
+		map.put((char)209,	"Ntilde");	/* Latin capital letter n with tilde */
+		map.put((char)210,	"Ograve");	/* Latin capital letter o with grave */
+		map.put((char)211,	"Oacute");	/* Latin capital letter o with acute */
+		map.put((char)212,	"Ocirc");	/* Latin capital letter o with circumflex */
+		map.put((char)213,	"Otilde");	/* Latin capital letter o with tilde */
+		map.put((char)214,	"Ouml");	/* Latin capital letter o with diaeresis */
+		map.put((char)215,	"times");	/* multiplication sign */
+		map.put((char)216,	"Oslash");	/* Latin capital letter o with stroke */
+		map.put((char)217,	"Ugrave");	/* Latin capital letter u with grave */
+		map.put((char)218,	"Uacute");	/* Latin capital letter u with acute */
+		map.put((char)219,	"Ucirc");	/* Latin capital letter u with circumflex */
+		map.put((char)220,	"Uuml");	/* Latin capital letter u with diaeresis */
+		map.put((char)221,	"Yacute");	/* Latin capital letter y with acute */
+		map.put((char)222,	"THORN");	/* Latin capital letter thorn */
+		map.put((char)223,	"szlig");	/* Latin small letter sharp sXCOMMAX German Eszett */
+		map.put((char)224,	"agrave");	/* Latin small letter a with grave */
+		map.put((char)225,	"aacute");	/* Latin small letter a with acute */
+		map.put((char)226,	"acirc");	/* Latin small letter a with circumflex */
+		map.put((char)227,	"atilde");	/* Latin small letter a with tilde */
+		map.put((char)228,	"auml");	/* Latin small letter a with diaeresis */
+		map.put((char)229,	"aring");	/* Latin small letter a with ring above */
+		map.put((char)230,	"aelig");	/* Latin lowercase ligature ae */
+		map.put((char)231,	"ccedil");	/* Latin small letter c with cedilla */
+		map.put((char)232,	"egrave");	/* Latin small letter e with grave */
+		map.put((char)233,	"eacute");	/* Latin small letter e with acute */
+		map.put((char)234,	"ecirc");	/* Latin small letter e with circumflex */
+		map.put((char)235,	"euml");	/* Latin small letter e with diaeresis */
+		map.put((char)236,	"igrave");	/* Latin small letter i with grave */
+		map.put((char)237,	"iacute");	/* Latin small letter i with acute */
+		map.put((char)238,	"icirc");	/* Latin small letter i with circumflex */
+		map.put((char)239,	"iuml");	/* Latin small letter i with diaeresis */
+		map.put((char)240,	"eth");		/* Latin small letter eth */
+		map.put((char)241,	"ntilde");	/* Latin small letter n with tilde */
+		map.put((char)242,	"ograve");	/* Latin small letter o with grave */
+		map.put((char)243,	"oacute");	/* Latin small letter o with acute */
+		map.put((char)244,	"ocirc");	/* Latin small letter o with circumflex */
+		map.put((char)245,	"otilde");	/* Latin small letter o with tilde */
+		map.put((char)246,	"ouml");	/* Latin small letter o with diaeresis */
+		map.put((char)247,	"divide");	/* division sign */
+		map.put((char)248,	"oslash");	/* Latin small letter o with stroke */
+		map.put((char)249,	"ugrave");	/* Latin small letter u with grave */
+		map.put((char)250,	"uacute");	/* Latin small letter u with acute */
+		map.put((char)251,	"ucirc");	/* Latin small letter u with circumflex */
+		map.put((char)252,	"uuml");	/* Latin small letter u with diaeresis */
+		map.put((char)253,	"yacute");	/* Latin small letter y with acute */
+		map.put((char)254,	"thorn");	/* Latin small letter thorn */
+		map.put((char)255,	"yuml");	/* Latin small letter y with diaeresis */
+		map.put((char)338,	"OElig");	/* Latin capital ligature oe */
+		map.put((char)339,	"oelig");	/* Latin small ligature oe */
+		map.put((char)352,	"Scaron");	/* Latin capital letter s with caron */
+		map.put((char)353,	"scaron");	/* Latin small letter s with caron */
+		map.put((char)376,	"Yuml");	/* Latin capital letter y with diaeresis */
+		map.put((char)402,	"fnof");	/* Latin small letter f with hook */
+		map.put((char)710,	"circ");	/* modifier letter circumflex accent */
+		map.put((char)732,	"tilde");	/* small tilde */
+		map.put((char)913,	"Alpha");	/* Greek capital letter alpha */
+		map.put((char)914,	"Beta");	/* Greek capital letter beta */
+		map.put((char)915,	"Gamma");	/* Greek capital letter gamma */
+		map.put((char)916,	"Delta");	/* Greek capital letter delta */
+		map.put((char)917,	"Epsilon");	/* Greek capital letter epsilon */
+		map.put((char)918,	"Zeta");	/* Greek capital letter zeta */
+		map.put((char)919,	"Eta");		/* Greek capital letter eta */
+		map.put((char)920,	"Theta");	/* Greek capital letter theta */
+		map.put((char)921,	"Iota");	/* Greek capital letter iota */
+		map.put((char)922,	"Kappa");	/* Greek capital letter kappa */
+		map.put((char)923,	"Lambda");	/* Greek capital letter lambda */
+		map.put((char)924,	"Mu");		/* Greek capital letter mu */
+		map.put((char)925,	"Nu");		/* Greek capital letter nu */
+		map.put((char)926,	"Xi");		/* Greek capital letter xi */
+		map.put((char)927,	"Omicron");	/* Greek capital letter omicron */
+		map.put((char)928,	"Pi");		/* Greek capital letter pi */
+		map.put((char)929,	"Rho");		/* Greek capital letter rho */
+		map.put((char)931,	"Sigma");	/* Greek capital letter sigma */
+		map.put((char)932,	"Tau");		/* Greek capital letter tau */
+		map.put((char)933,	"Upsilon");	/* Greek capital letter upsilon */
+		map.put((char)934,	"Phi");		/* Greek capital letter phi */
+		map.put((char)935,	"Chi");		/* Greek capital letter chi */
+		map.put((char)936,	"Psi");		/* Greek capital letter psi */
+		map.put((char)937,	"Omega");	/* Greek capital letter omega */
+		map.put((char)945,	"alpha");	/* Greek small letter alpha */
+		map.put((char)946,	"beta");	/* Greek small letter beta */
+		map.put((char)947,	"gamma");	/* Greek small letter gamma */
+		map.put((char)948,	"delta");	/* Greek small letter delta */
+		map.put((char)949,	"epsilon");	/* Greek small letter epsilon */
+		map.put((char)950,	"zeta");	/* Greek small letter zeta */
+		map.put((char)951,	"eta");		/* Greek small letter eta */
+		map.put((char)952,	"theta");	/* Greek small letter theta */
+		map.put((char)953,	"iota");	/* Greek small letter iota */
+		map.put((char)954,	"kappa");	/* Greek small letter kappa */
+		map.put((char)955,	"lambda");	/* Greek small letter lambda */
+		map.put((char)956,	"mu");		/* Greek small letter mu */
+		map.put((char)957,	"nu");		/* Greek small letter nu */
+		map.put((char)958,	"xi");		/* Greek small letter xi */
+		map.put((char)959,	"omicron");	/* Greek small letter omicron */
+		map.put((char)960,	"pi");		/* Greek small letter pi */
+		map.put((char)961,	"rho");		/* Greek small letter rho */
+		map.put((char)962,	"sigmaf");	/* Greek small letter final sigma */
+		map.put((char)963,	"sigma");	/* Greek small letter sigma */
+		map.put((char)964,	"tau");		/* Greek small letter tau */
+		map.put((char)965,	"upsilon");	/* Greek small letter upsilon */
+		map.put((char)966,	"phi");		/* Greek small letter phi */
+		map.put((char)967,	"chi");		/* Greek small letter chi */
+		map.put((char)968,	"psi");		/* Greek small letter psi */
+		map.put((char)969,	"omega");	/* Greek small letter omega */
+		map.put((char)977,	"thetasym");	/* Greek theta symbol */
+		map.put((char)978,	"upsih");	/* Greek upsilon with hook symbol */
+		map.put((char)982,	"piv");		/* Greek pi symbol */
+		map.put((char)8194,	"ensp");	/* en space */
+		map.put((char)8195,	"emsp");	/* em space */
+		map.put((char)8201,	"thinsp");	/* thin space */
+		map.put((char)8204,	"zwnj");	/* zero width non-joiner */
+		map.put((char)8205,	"zwj");		/* zero width joiner */
+		map.put((char)8206,	"lrm");		/* left-to-right mark */
+		map.put((char)8207,	"rlm");		/* right-to-left mark */
+		map.put((char)8211,	"ndash");	/* en dash */
+		map.put((char)8212,	"mdash");	/* em dash */
+		map.put((char)8216,	"lsquo");	/* left single quotation mark */
+		map.put((char)8217,	"rsquo");	/* right single quotation mark */
+		map.put((char)8218,	"sbquo");	/* single low-9 quotation mark */
+		map.put((char)8220,	"ldquo");	/* left double quotation mark */
+		map.put((char)8221,	"rdquo");	/* right double quotation mark */
+		map.put((char)8222,	"bdquo");	/* double low-9 quotation mark */
+		map.put((char)8224,	"dagger");	/* dagger */
+		map.put((char)8225,	"Dagger");	/* double dagger */
+		map.put((char)8226,	"bull");	/* bullet */
+		map.put((char)8230,	"hellip");	/* horizontal ellipsis */
+		map.put((char)8240,	"permil");	/* per mille sign */
+		map.put((char)8242,	"prime");	/* prime */
+		map.put((char)8243,	"Prime");	/* double prime */
+		map.put((char)8249,	"lsaquo");	/* single left-pointing angle quotation mark */
+		map.put((char)8250,	"rsaquo");	/* single right-pointing angle quotation mark */
+		map.put((char)8254,	"oline");	/* overline */
+		map.put((char)8260,	"frasl");	/* fraction slash */
+		map.put((char)8364,	"euro");	/* euro sign */
+		map.put((char)8465,	"image");	/* black-letter capital i */
+		map.put((char)8472,	"weierp");	/* script capital pXCOMMAX Weierstrass p */
+		map.put((char)8476,	"real");	/* black-letter capital r */
+		map.put((char)8482,	"trade");	/* trademark sign */
+		map.put((char)8501,	"alefsym");	/* alef symbol */
+		map.put((char)8592,	"larr");	/* leftwards arrow */
+		map.put((char)8593,	"uarr");	/* upwards arrow */
+		map.put((char)8594,	"rarr");	/* rightwards arrow */
+		map.put((char)8595,	"darr");	/* downwards arrow */
+		map.put((char)8596,	"harr");	/* left right arrow */
+		map.put((char)8629,	"crarr");	/* downwards arrow with corner leftwards */
+		map.put((char)8656,	"lArr");	/* leftwards double arrow */
+		map.put((char)8657,	"uArr");	/* upwards double arrow */
+		map.put((char)8658,	"rArr");	/* rightwards double arrow */
+		map.put((char)8659,	"dArr");	/* downwards double arrow */
+		map.put((char)8660,	"hArr");	/* left right double arrow */
+		map.put((char)8704,	"forall");	/* for all */
+		map.put((char)8706,	"part");	/* partial differential */
+		map.put((char)8707,	"exist");	/* there exists */
+		map.put((char)8709,	"empty");	/* empty set */
+		map.put((char)8711,	"nabla");	/* nabla */
+		map.put((char)8712,	"isin");	/* element of */
+		map.put((char)8713,	"notin");	/* not an element of */
+		map.put((char)8715,	"ni");		/* contains as member */
+		map.put((char)8719,	"prod");	/* n-ary product */
+		map.put((char)8721,	"sum");		/* n-ary summation */
+		map.put((char)8722,	"minus");	/* minus sign */
+		map.put((char)8727,	"lowast");	/* asterisk operator */
+		map.put((char)8730,	"radic");	/* square root */
+		map.put((char)8733,	"prop");	/* proportional to */
+		map.put((char)8734,	"infin");	/* infinity */
+		map.put((char)8736,	"ang");		/* angle */
+		map.put((char)8743,	"and");		/* logical and */
+		map.put((char)8744,	"or");		/* logical or */
+		map.put((char)8745,	"cap");		/* intersection */
+		map.put((char)8746,	"cup");		/* union */
+		map.put((char)8747,	"int");		/* integral */
+		map.put((char)8756,	"there4");	/* therefore */
+		map.put((char)8764,	"sim");		/* tilde operator */
+		map.put((char)8773,	"cong");	/* congruent to */
+		map.put((char)8776,	"asymp");	/* almost equal to */
+		map.put((char)8800,	"ne");		/* not equal to */
+		map.put((char)8801,	"equiv");	/* identical toXCOMMAX equivalent to */
+		map.put((char)8804,	"le");		/* less-than or equal to */
+		map.put((char)8805,	"ge");		/* greater-than or equal to */
+		map.put((char)8834,	"sub");		/* subset of */
+		map.put((char)8835,	"sup");		/* superset of */
+		map.put((char)8836,	"nsub");	/* not a subset of */
+		map.put((char)8838,	"sube");	/* subset of or equal to */
+		map.put((char)8839,	"supe");	/* superset of or equal to */
+		map.put((char)8853,	"oplus");	/* circled plus */
+		map.put((char)8855,	"otimes");	/* circled times */
+		map.put((char)8869,	"perp");	/* up tack */
+		map.put((char)8901,	"sdot");	/* dot operator */
+		map.put((char)8968,	"lceil");	/* left ceiling */
+		map.put((char)8969,	"rceil");	/* right ceiling */
+		map.put((char)8970,	"lfloor");	/* left floor */
+		map.put((char)8971,	"rfloor");	/* right floor */
+		map.put((char)9001,	"lang");	/* left-pointing angle bracket */
+		map.put((char)9002,	"rang");	/* right-pointing angle bracket */
+		map.put((char)9674,	"loz");		/* lozenge */
+		map.put((char)9824,	"spades");	/* black spade suit */
+		map.put((char)9827,	"clubs");	/* black club suit */
+		map.put((char)9829,	"hearts");	/* black heart suit */
+		map.put((char)9830,	"diams");	/* black diamond suit */
+
+		return Collections.unmodifiableMap(map);
+	}
+
+	/**
+	 * Build a unmodifiable Trie from entitiy Name to Character
+	 * @return Unmodifiable trie.
+	 */
+	private static synchronized Trie<Character> mkEntityToCharacterTrie()
+	{
+		Trie<Character> trie = new HashTrie<Character>();
+
+		for(Map.Entry<Character,String> entry : characterToEntityMap.entrySet())
+			trie.put(entry.getValue(),entry.getKey());
+		return Trie.Util.unmodifiable(trie);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/HashTrie.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/HashTrie.java.svn-base
new file mode 100644
index 0000000..ba515d3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/HashTrie.java.svn-base
@@ -0,0 +1,612 @@
+package org.owasp.esapi.codecs;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.owasp.esapi.util.NullSafe;
+
+/**
+ * Trie implementation for CharSequence keys. This uses HashMaps for each
+ * level instead of the traditional array. This is done as with unicode,
+ * each level's array would be 64k entries.
+ *
+ * <b>NOTE:</b><br>
+ * <ul>
+ *	<li>{@link Map.remove(Object)} is not supported.</li>
+ *	<li>
+ *		If deletion support is added the max key length will
+ *		need work or removal.
+ *	</li>
+ *	<li>Null values are not supported.</li>
+ * </ul>
+ *
+ * @author Ed Schaller
+ */
+public class HashTrie<T> implements Trie<T>
+{
+	private static class Entry<T> implements Map.Entry<CharSequence,T>
+	{
+		private CharSequence key;
+		private T value;
+
+		Entry(CharSequence key, T value)
+		{
+			this.key = key;
+			this.value = value;
+		}
+
+		/**
+		 * Convinence instantiator.
+		 * @param key The key for the new instance
+		 * @param keyLength The length of the key to use
+		 * @param value The value for the new instance
+		 * @return null if key or value is null
+		 *	new Entry(key,value) if {@link CharSequence#length()} == keyLength
+		 *	new Entry(key.subSequence(0,keyLength),value) otherwise
+		 */
+		static <T> Entry<T> newInstanceIfNeeded(CharSequence key, int keyLength, T value)
+		{
+			if(value == null || key == null)
+				return null;
+			if(key.length() > keyLength)
+				key = key.subSequence(0,keyLength);
+			return new Entry<T>(key,value);
+		}
+
+		/**
+		 * Convinence instantiator.
+		 * @param key The key for the new instance
+		 * @param value The value for the new instance
+		 * @return null if key or value is null
+		 *	new Entry(key,value) otherwise
+		 */
+		static <T> Entry<T> newInstanceIfNeeded(CharSequence key, T value)
+		{
+			if(value == null || key == null)
+				return null;
+			return new Entry<T>(key,value);
+		}
+
+                /*************/
+                /* Map.Entry */
+                /*************/
+
+		public CharSequence getKey()
+		{
+			return key;
+		}
+
+		public T getValue()
+		{
+			return value;
+		}
+
+		public T setValue(T value)
+		{
+			throw new UnsupportedOperationException();
+		}
+
+                /********************/
+                /* java.lang.Object */
+                /********************/
+
+		public boolean equals(Map.Entry other)
+		{
+			return (NullSafe.equals(key, other.getKey()) && NullSafe.equals(value, other.getValue()));
+		}
+
+		@Override
+		public boolean equals(Object o)
+		{
+			if(o instanceof Map.Entry)
+				return equals((Map.Entry)o);
+			return false;
+		}
+
+		@Override
+		public int hashCode()
+		{
+			return NullSafe.hashCode(key) ^ NullSafe.hashCode(value);
+		}
+
+		@Override
+		public String toString()
+		{
+			return NullSafe.toString(key) + " => " + NullSafe.toString(value);
+		}
+	}
+
+	/**
+	 * Node inside the trie.
+	 */
+	private static class Node<T>
+	{
+		private T value = null;
+		private Map<Character,Node<T>> nextMap;
+
+		/**
+		 * Create a new Map for a node level. This is here so
+		 * that if the underlying * Map implmentation needs to
+		 * be switched it is easily done.
+		 * @return A new Map for use.
+		 */
+		private static <T> Map<Character,Node<T>> newNodeMap()
+		{
+			return new HashMap<Character,Node<T>>();
+		}
+
+		/**
+		 * Create a new Map for a node level. This is here so
+		 * that if the underlying * Map implmentation needs to
+		 * be switched it is easily done.
+		 * @param prev Pervious map to use to populate the
+		 * new map.
+		 * @return A new Map for use.
+		 */
+		private static <T> Map<Character,Node<T>> newNodeMap(Map<Character,Node<T>> prev)
+		{
+			return new HashMap<Character,Node<T>>(prev);
+		}
+
+		/** 
+		 * Set the value for the key terminated at this node.
+		 * @param value The value for this key.
+		 */
+		void setValue(T value)
+		{
+			this.value = value;
+		}
+
+		/**
+		 * Get the node for the specified character.
+		 * @param ch The next character to look for.
+		 * @return The node requested or null if it is not
+		 *	present.
+		 */
+		Node<T> getNextNode(Character ch)
+		{
+			if(nextMap == null)
+				return null;
+			return nextMap.get(ch);
+		}
+
+		/**
+		 * Recursively add a key.
+		 * @param key The key being added.
+		 * @param pos The position in key that is being handled
+		 *	at this level.
+		 */
+		T put(CharSequence key, int pos, T addValue)
+		{
+			Node<T> nextNode;
+			Character ch;
+			T old;
+
+			if(key.length() == pos)
+			{	// at terminating node
+				old = value;
+				setValue(addValue);
+				return old;
+			}
+			ch = key.charAt(pos);
+			if(nextMap == null)
+			{
+				nextMap = newNodeMap();
+				nextNode = new Node();
+				nextMap.put(ch, nextNode);
+			}
+			else if((nextNode = nextMap.get(ch))==null)
+			{
+				nextNode = new Node();
+				nextMap.put(ch,nextNode);
+			}
+			return nextNode.put(key,pos+1,addValue);
+		}
+
+		/**
+		 * Recursively lookup a key's value.
+		 * @param key The key being looked up.
+		 * @param pos The position in the key that is being
+		 *	looked up at this level.
+		 * @return The value assocatied with the key or null if
+		 *	none exists.
+		 */
+		T get(CharSequence key, int pos)
+		{
+			Node<T> nextNode;
+
+			if(key.length() <= pos)	// <= instead of == just in case
+				return value;	// no value is null which is also not found
+			if((nextNode = getNextNode(key.charAt(pos)))==null)
+				return null;
+			return nextNode.get(key,pos+1);
+		}
+			
+		/**
+		 * Recursively lookup the longest key match.
+		 * @param key The key being looked up.
+		 * @param pos The position in the key that is being
+		 *	looked up at this level.
+		 * @return The Entry assocatied with the longest key
+		 *	match or null if none exists.
+		 */
+		Entry<T> getLongestMatch(CharSequence key, int pos)
+		{
+			Node<T> nextNode;
+			Entry<T> ret;
+
+			if(key.length() <= pos)	// <= instead of == just in case
+				return Entry.newInstanceIfNeeded(key,value);
+			if((nextNode = getNextNode(key.charAt(pos)))==null)
+			{	// last in trie... return ourselves
+				return Entry.newInstanceIfNeeded(key,pos,value);
+			}
+			if((ret = nextNode.getLongestMatch(key, pos+1))!=null)
+				return ret;
+			return Entry.newInstanceIfNeeded(key,pos,value);
+		}
+
+		/**
+		 * Recursively lookup the longest key match.
+		 * @param keyIn Where to read the key from
+		 * @param pos The position in the key that is being
+		 *	looked up at this level.
+		 * @return The Entry assocatied with the longest key
+		 *	match or null if none exists.
+		 */
+		Entry<T> getLongestMatch(PushbackReader keyIn, StringBuilder key) throws IOException
+		{
+			Node<T> nextNode;
+			Entry<T> ret;
+			int c;
+			char ch;
+			int prevLen;
+
+			// read next key char and append to key...
+			if((c = keyIn.read())<0)
+				// end of input, return what we have currently
+				return Entry.newInstanceIfNeeded(key,value);
+			ch = (char)c;
+			prevLen = key.length();
+			key.append(ch);
+
+			if((nextNode = getNextNode(ch))==null)
+			{	// last in trie... return ourselves
+				return Entry.newInstanceIfNeeded(key,value);
+			}
+			if((ret = nextNode.getLongestMatch(keyIn, key))!=null)
+				return ret;
+
+			// undo reading of key char and appending to key...
+			key.setLength(prevLen);
+			keyIn.unread(c);
+
+			return Entry.newInstanceIfNeeded(key,value);
+		}
+
+		/**
+		 * Recursively rebuild the internal maps.
+		 */
+		void remap()
+		{
+			if(nextMap == null)
+				return;
+			nextMap = newNodeMap(nextMap);
+			for(Node<T> node : nextMap.values())
+				node.remap();
+		}
+
+		/**
+		 * Recursively search for a value.
+		 * @param toFind The value to search for
+		 * @return true if the value was found
+		 *	false otherwise
+		 */
+		boolean containsValue(Object toFind)
+		{
+			if(value != null && toFind.equals(value))
+				return true;
+			if(nextMap == null)
+				return false;
+			for(Node<T> node : nextMap.values())
+				if(node.containsValue(toFind))
+					return true;
+			return false;
+		}
+
+		/**
+		 * Recursively build values.
+		 * @param values List being built.
+		 * @return true if the value was found
+		 *	false otherwise
+		 */
+		Collection<T> values(Collection<T> values)
+		{
+			if(value != null)
+				values.add(value);
+			if(nextMap == null)
+				return values;
+			for(Node<T> node : nextMap.values())
+				node.values(values);
+			return values;
+		}
+
+		/**
+		 * Recursively build a key set.
+		 * @param key StringBuilder with our key.
+		 * @param keys Set to add to
+		 * @return keys with additions
+		 */
+		Set<CharSequence> keySet(StringBuilder key, Set<CharSequence> keys)
+		{
+			int len = key.length();
+
+			if(value != null)
+				// MUST toString here
+				keys.add(key.toString());
+			if(nextMap != null && nextMap.size() > 0)
+			{
+				key.append('X');
+				for(Map.Entry<Character,Node<T>> entry : nextMap.entrySet())
+				{
+					key.setCharAt(len,entry.getKey());
+					entry.getValue().keySet(key,keys);
+				}
+				key.setLength(len);
+			}
+			return keys;
+		}
+
+		/**
+		 * Recursively build a entry set.
+		 * @param key StringBuilder with our key.
+		 * @param entries Set to add to
+		 * @return entries with additions
+		 */
+		Set<Map.Entry<CharSequence,T>> entrySet(StringBuilder key, Set<Map.Entry<CharSequence,T>> entries)
+		{
+			int len = key.length();
+
+			if(value != null)
+				// MUST toString here
+				entries.add(new Entry(key.toString(),value));
+			if(nextMap != null && nextMap.size() > 0)
+			{
+				key.append('X');
+				for(Map.Entry<Character,Node<T>> entry : nextMap.entrySet())
+				{
+					key.setCharAt(len,entry.getKey());
+					entry.getValue().entrySet(key,entries);
+				}
+				key.setLength(len);
+			}
+			return entries;
+		}
+	}
+
+	private Node<T> root;
+	private int maxKeyLen;
+	private int size;
+
+	public HashTrie()
+	{
+		clear();
+	}
+
+	/**
+	 * Get the key value entry who's key is the longest prefix match.
+	 * @param key The key to lookup
+	 * @return Entry with the longest matching key.
+	 */
+	public Map.Entry<CharSequence,T> getLongestMatch(CharSequence key)
+	{
+		if(root == null || key == null)
+			return null;
+		return root.getLongestMatch(key, 0);
+	}
+
+	/**
+	 * Get the key value entry who's key is the longest prefix match.
+	 * @param keyIn Pushback reader to read the key from. This should
+	 * have a buffer at least as large as {@link #getMaxKeyLength()}
+	 * or an IOException may be thrown backing up.
+	 * @return Entry with the longest matching key.
+	 * @throws IOException if keyIn.read() or keyIn.unread() does.
+	 */
+	public Map.Entry<CharSequence,T> getLongestMatch(PushbackReader keyIn) throws IOException
+	{
+		if(root == null || keyIn == null)
+			return null;
+		return root.getLongestMatch(keyIn, new StringBuilder());
+	}
+
+	/**
+	 * Get the maximum key length.
+	 * @return max key length.
+	 */
+	public int getMaxKeyLength()
+	{
+		return maxKeyLen;
+	}
+
+        /*****************/
+        /* java.util.Map */
+        /*****************/
+
+	/**
+	 * Clear all entries.
+	 */
+	public void clear()
+	{
+		root = null;
+		maxKeyLen = -1;
+		size = 0;
+	}
+
+	/** {@inheritDoc} */
+	public boolean containsKey(Object key)
+	{
+		return (get(key) != null);
+	}
+
+	/** {@inheritDoc} */
+	public boolean containsValue(Object value)
+	{
+		if(root == null)
+			return false;
+		return root.containsValue(value);
+	}
+
+	/**
+	 * Add mapping.
+	 * @param key The mapping's key.
+	 * @value value The mapping's value
+	 * @throws NullPointerException if key or value is null.
+	 */
+	public T put(CharSequence key, T value) throws NullPointerException
+	{
+		int len;
+		T old;
+
+		if(key == null)
+			throw new NullPointerException("Null keys are not handled");
+		if(value == null)
+			throw new NullPointerException("Null values are not handled");
+		if(root == null)
+			root = new Node<T>();
+		if((old = root.put(key,0,value))!=null)
+			return old;
+
+		// after in case of replacement
+		if((len = key.length()) > maxKeyLen)
+			maxKeyLen = len;
+		size++;
+		return null;
+	}
+
+	/**
+	 * Remove a entry.
+	 * @return previous value
+	 * @throws UnsupportedOperationException always.
+	 */
+	public T remove(Object key) throws UnsupportedOperationException
+	{
+		throw new UnsupportedOperationException();
+	}
+
+	/** {@inheritDoc} */
+	public void putAll(Map<? extends CharSequence, ? extends T> map)
+	{
+		for(Map.Entry<? extends CharSequence, ? extends T> entry : map.entrySet())
+			put(entry.getKey(),entry.getValue());
+	}
+
+	/** {@inheritDoc} */
+	public Set<CharSequence> keySet()
+	{
+		Set<CharSequence> keys = new HashSet<CharSequence>(size);
+		
+		if(root == null)
+			return keys;
+		return root.keySet(new StringBuilder(), keys);
+	}
+
+	/** {@inheritDoc} */
+	public Collection<T> values()
+	{
+		ArrayList<T> values = new ArrayList<T>(size());
+
+		if(root == null)
+			return values;
+		return root.values(values);
+	}
+
+	/** {@inheritDoc} */
+	public Set<Map.Entry<CharSequence,T>> entrySet()
+	{
+		Set<Map.Entry<CharSequence,T>> entries = new HashSet<Map.Entry<CharSequence,T>>(size());
+
+		if(root == null)
+			return entries;
+		return root.entrySet(new StringBuilder(), entries);
+	}
+
+	/**
+	 * Get the value for a key.
+	 * @param key The key to look up.
+	 * @return The value for key or null if the key is not found.
+	 */
+	public T get(Object key)
+	{
+		if(root == null || key == null)
+			return null;
+		if(!(key instanceof CharSequence))
+			return null;
+		return root.get((CharSequence)key,0);
+	}
+
+	/**
+	 * Get the number of entries.
+	 * @return the number or entries.
+	 */
+	public int size()
+	{
+		return size;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean equals(Object other)
+	{
+		if(other == null)
+			return false;
+		if(!(other instanceof Map))
+			return false;
+		// per spec
+		return entrySet().equals(((Map)other).entrySet());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public int hashCode()
+	{
+		// per spec
+		return entrySet().hashCode();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public String toString()
+	{
+		StringBuilder sb;
+		boolean first;
+
+		if(isEmpty())
+			return "{}";
+		sb = new StringBuilder();
+		first = true;
+		sb.append("{ ");
+		for(Map.Entry<CharSequence,T> entry : entrySet())
+		{
+			if(first)
+				first = false;
+			else
+				sb.append(", ");
+			sb.append(entry.toString());
+		}
+		sb.append(" }");
+		return sb.toString();
+	}
+
+	/** {@inheritDoc} */
+	public boolean isEmpty()
+	{
+		return(size() == 0);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Hex.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Hex.java.svn-base
new file mode 100644
index 0000000..17ab4bf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Hex.java.svn-base
@@ -0,0 +1,84 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.codecs;
+
+/** Encode and decode to/from hexadecimal strings to byte arrays. */
+public class Hex {
+
+    /** Output byte representation as hexadecimal representation.
+     * 
+     * @param b				Bytes to encode to hexadecimal representation.
+     * @param leading0x		If true, return with leading "0x".
+     * @return				Hexadecimal representation of specified bytes.
+     */
+    public static String toHex(byte[] b, boolean leading0x) {
+        StringBuffer hexString = new StringBuffer();
+        if ( leading0x ) {
+            hexString.append("0x");
+        }
+        for ( int i = 0; i < b.length; i++ ) {
+            int j = b[i] & 0xff;        // Convert byte to int.
+            String hex = Integer.toHexString(j);
+            if (hex.length() == 1) {
+                hexString.append('0');  // Add leading zero.
+            }
+            hexString.append(hex);      // Hex digit(s).
+        }
+        return hexString.toString();
+    }
+
+    /**
+     * Output byte representation as hexadecimal representation.
+     * Alias for <code>toHex()</code> method.
+     * 
+     * @param b				Bytes to encode to hexadecimal representation.
+     * @param leading0x		If true, return with leading "0x".
+     * @return				Hexadecimal representation of specified bytes.
+     */
+    public static String encode(byte[] b, boolean leading0x) {
+        return toHex(b, leading0x);
+    }
+
+    /**
+     * Decode hexadecimal-encoded string and return raw byte array.
+     * Important note: This method preserves leading 0 filled bytes on the
+     * conversion process, which is important for cryptographic operations
+     * in dealing with things like keys, initialization vectors, etc. For
+     * example, the string "0x0000face" is going to return a byte array
+     * whose length is 4, not 2.
+     * 
+     * @param hexStr	Hexadecimal-encoded string, with or without leading "0x".
+     * @return			The equivalent byte array.
+     */
+    public static byte[] fromHex(String hexStr) {
+        String hexRep = hexStr;
+        if ( hexStr.startsWith("0x") ) {
+            // Then skip over the leading 0x.
+            hexRep = hexStr.substring(2);
+        }
+        int len = hexRep.length() / 2;
+        byte[] rawBytes = new byte[len];
+        for (int i = 0; i < len; i++) {
+            String substr = hexRep.substring(i * 2, (i * 2) + 2);
+            rawBytes[i] = (byte)(Integer.parseInt(substr, 16));
+        }
+        return rawBytes;
+    }
+
+    /** Decode hexadecimal-encoded string and return raw byte array.
+     * Alias for <code>fromHex()</code> method.
+     * 
+     * @param hexStr	Hexadecimal-encoded string, with or without leading "0x".
+     * @return			The equivalent byte array. 
+     */
+    public static byte[] decode(String hexStr) {
+        return fromHex(hexStr);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/JavaScriptCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/JavaScriptCodec.java.svn-base
new file mode 100644
index 0000000..6d128e4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/JavaScriptCodec.java.svn-base
@@ -0,0 +1,217 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for backslash encoding in JavaScript.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class JavaScriptCodec extends Codec {
+
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns backslash encoded numeric format. Does not use backslash character escapes
+	 * such as, \" or \' as these may cause parsing problems. For example, if a javascript
+	 * attribute, such as onmouseover, contains a \" that will close the entire attribute and
+	 * allow an attacker to inject another script attribute.
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+
+		// check for immune characters
+		if ( containsCharacter(c, immune ) ) {
+			return ""+c;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric(c);
+		if ( hex == null ) {
+			return ""+c;
+		}
+				
+		// Do not use these shortcuts as they can be used to break out of a context
+		// if ( ch == 0x00 ) return "\\0";
+		// if ( ch == 0x08 ) return "\\b";
+		// if ( ch == 0x09 ) return "\\t";
+		// if ( ch == 0x0a ) return "\\n";
+		// if ( ch == 0x0b ) return "\\v";
+		// if ( ch == 0x0c ) return "\\f";
+		// if ( ch == 0x0d ) return "\\r";
+		// if ( ch == 0x22 ) return "\\\"";
+		// if ( ch == 0x27 ) return "\\'";
+		// if ( ch == 0x5c ) return "\\\\";
+
+		// encode up to 256 with \\xHH
+        String temp = Integer.toHexString(c);
+		if ( c < 256 ) {
+	        String pad = "00".substring(temp.length() );
+	        return "\\x" + pad + temp.toUpperCase();
+		}
+
+		// otherwise encode with \\uHHHH
+        String pad = "0000".substring(temp.length() );
+        return "\\u" + pad + temp.toUpperCase();
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * See http://www.planetpdf.com/codecuts/pdfs/tutorial/jsspec.pdf 
+	 * Formats all are legal both upper/lower case:
+	 *   \\a - special characters
+	 *   \\xHH
+	 *   \\uHHHH
+	 *   \\OOO (1, 2, or 3 digits)
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if (first != '\\' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// \0 collides with the octal decoder and is non-standard
+		// if ( second.charValue() == '0' ) {
+		//	return Character.valueOf( (char)0x00 );
+		if (second == 'b' ) {
+			return 0x08;
+		} else if (second == 't' ) {
+			return 0x09;
+		} else if (second == 'n' ) {
+			return 0x0a;
+		} else if (second == 'v' ) {
+			return 0x0b;
+		} else if (second == 'f' ) {
+			return 0x0c;
+		} else if (second == 'r' ) {
+			return 0x0d;
+		} else if (second == '\"' ) {
+			return 0x22;
+		} else if (second == '\'' ) {
+			return 0x27;
+		} else if (second == '\\' ) {
+			return 0x5c;
+			
+		// look for \\xXX format
+		} else if ( Character.toLowerCase( second.charValue() ) == 'x' ) {
+			// Search for exactly 2 hex digits following
+			StringBuilder sb = new StringBuilder();
+			for ( int i=0; i<2; i++ ) {
+				Character c = input.nextHex();
+				if ( c != null ) sb.append( c );
+				else {
+					input.reset();
+					return null;
+				}
+			}
+			try {
+				// parse the hex digit and create a character
+				int i = Integer.parseInt(sb.toString(), 16);
+                if (Character.isValidCodePoint(i)) {
+                    return (char) i;
+                }
+			} catch( NumberFormatException e ) {
+				// throw an exception for malformed entity?
+				input.reset();
+				return null;
+			}
+			
+		// look for \\uXXXX format
+		} else if ( Character.toLowerCase( second.charValue() ) == 'u') {
+			// Search for exactly 4 hex digits following
+			StringBuilder sb = new StringBuilder();
+			for ( int i=0; i<4; i++ ) {
+				Character c = input.nextHex();
+				if ( c != null ) sb.append( c );
+				else {
+					input.reset();
+					return null;
+				}
+			}
+			try {
+				// parse the hex string and create a character
+				int i = Integer.parseInt(sb.toString(), 16);
+                if (Character.isValidCodePoint(i)) {
+                    return (char) i;
+                }
+			} catch( NumberFormatException e ) {
+				// throw an exception for malformed entity?
+				input.reset();
+				return null;
+			}
+			
+		// look for one, two, or three octal digits
+		} else if ( PushbackString.isOctalDigit(second) ) {
+			StringBuilder sb = new StringBuilder();
+            // get digit 1
+            sb.append(second);
+            
+            // get digit 2 if present
+            Character c2 = input.next();
+            if ( !PushbackString.isOctalDigit(c2) ) {
+            	input.pushback( c2 );
+            } else {
+            	sb.append( c2 );
+	            // get digit 3 if present
+	            Character c3 = input.next();
+	            if ( !PushbackString.isOctalDigit(c3) ) {
+	            	input.pushback( c3 );
+	            } else {
+	            	sb.append( c3 );
+	            }
+            }
+			try {
+				// parse the octal string and create a character
+				int i = Integer.parseInt(sb.toString(), 8);
+                if (Character.isValidCodePoint(i)) {
+                    return (char) i;
+                }
+			} catch( NumberFormatException e ) {
+				// throw an exception for malformed entity?
+				input.reset();
+				return null;
+			}
+		}
+		
+		// ignore the backslash and return the character
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/MySQLCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/MySQLCodec.java.svn-base
new file mode 100644
index 0000000..01db4fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/MySQLCodec.java.svn-base
@@ -0,0 +1,264 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+
+/**
+ * Implementation of the Codec interface for MySQL strings. See http://mirror.yandex.ru/mirrors/ftp.mysql.com/doc/refman/5.0/en/string-syntax.html
+ * for more information.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class MySQLCodec extends Codec {
+    /**
+     * Specifies the SQL Mode the target MySQL Server is running with. For details about MySQL Server Modes
+     * please see the Manual at {@link http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_ansi}
+     *
+     * Currently the only supported modes are:
+     * ANSI
+     * STANDARD
+     */
+    public static enum Mode {
+        ANSI(1),STANDARD(0);
+
+        private int key;
+        private Mode(int key) { this.key = key; }
+
+        static Mode findByKey(int key) {
+            for ( Mode m : values() ) {
+                if ( m.key == key )
+                    return m;
+            }
+            return null;
+        }
+    }
+
+    /** Target MySQL Server is running in Standard MySQL (Default) mode. */
+    public static final int MYSQL_MODE = 0;
+    /** Target MySQL Server is running in {@link "http://dev.mysql.com/doc/refman/5.0/en/ansi-mode.html"} ANSI Mode */
+    public static final int ANSI_MODE = 1;
+	
+	//private int mode = 0;
+    private Mode mode;
+	
+	/**
+	 * Instantiate the MySQL codec
+	 * 
+	 * @param mode
+	 * 			Mode has to be one of {MYSQL_MODE|ANSI_MODE} to allow correct encoding
+     * @deprecated
+     * @see #MySQLCodec(org.owasp.esapi.codecs.MySQLCodec.Mode)
+	 */
+	public MySQLCodec( int mode ) {
+		this.mode = Mode.findByKey(mode);
+	}
+
+    /**
+     * Instantiate the MySQL Codec with the given SQL {@link Mode}.
+     * @param mode The mode the target server is running in
+     */
+    public MySQLCodec( Mode mode ) {
+        this.mode = mode;
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns quote-encoded character
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+		switch( mode ) {
+			case ANSI: return encodeCharacterANSI( c );
+			case STANDARD: return encodeCharacterMySQL( c );
+		}
+		return null;
+	}
+	
+	/**
+	 * encodeCharacterANSI encodes for ANSI SQL. 
+	 * 
+	 * Apostrophe is encoded
+     *
+     * Bug ###: In ANSI Mode Strings can also be passed in using the quotation. In ANSI_QUOTES mode a quotation
+     * is considered to be an identifier, thus cannot be used at all in a value and will be dropped completely.
+	 * 
+	 * @param c 
+	 * 			character to encode
+	 * @return
+	 * 			String encoded to standards of MySQL running in ANSI mode
+	 */
+	private String encodeCharacterANSI( Character c ) {
+		if ( c == '\'' )
+        	return "\'\'";
+        if ( c == '\"' )
+            return "";
+        return ""+c;
+	}
+
+	/**
+	 * Encode a character suitable for MySQL
+	 * 
+	 * @param c
+	 * 			Character to encode
+	 * @return
+	 * 			Encoded Character
+	 */
+	private String encodeCharacterMySQL( Character c ) {
+		char ch = c.charValue();
+		if ( ch == 0x00 ) return "\\0";
+		if ( ch == 0x08 ) return "\\b";
+		if ( ch == 0x09 ) return "\\t";
+		if ( ch == 0x0a ) return "\\n";
+		if ( ch == 0x0d ) return "\\r";
+		if ( ch == 0x1a ) return "\\Z";
+		if ( ch == 0x22 ) return "\\\"";
+		if ( ch == 0x25 ) return "\\%";
+		if ( ch == 0x27 ) return "\\'";
+		if ( ch == 0x5c ) return "\\\\";
+		if ( ch == 0x5f ) return "\\_";
+	    return "\\" + c;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal (case sensitive)
+	 *   In ANSI_MODE '' decodes to '
+	 *   In MYSQL_MODE \x decodes to x (or a small list of specials)
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		switch( mode ) {
+			case ANSI: return decodeCharacterANSI( input );
+			case STANDARD: return decodeCharacterMySQL( input );
+		}
+		return null;
+	}
+
+	/**
+	 * decodeCharacterANSI decodes the next character from ANSI SQL escaping
+	 *  
+	 * @param input
+	 * 			A PushBackString containing characters you'd like decoded
+	 * @return
+	 * 			A single character, decoded
+	 */
+	private Character decodeCharacterANSI( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( second.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+		return( Character.valueOf( '\'' ) );
+	}
+
+	/**
+	 * decodeCharacterMySQL decodes all the potential escaped characters that MySQL is prepared to escape
+	 * 
+	 * @param input
+	 * 			A string you'd like to be decoded
+	 * @return
+	 * 			A single character from that string, decoded.
+	 */
+	private Character decodeCharacterMySQL( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\\' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		if ( second.charValue() == '0' ) {
+			return Character.valueOf( (char)0x00 );
+		} else if ( second.charValue() == 'b' ) {
+			return Character.valueOf( (char)0x08 );
+		} else if ( second.charValue() == 't' ) {
+			return Character.valueOf( (char)0x09 );
+		} else if ( second.charValue() == 'n' ) {
+			return Character.valueOf( (char)0x0a );
+		} else if ( second.charValue() == 'r' ) {
+			return Character.valueOf( (char)0x0d );
+		} else if ( second.charValue() == 'z' ) {
+			return Character.valueOf( (char)0x1a );
+		} else if ( second.charValue() == '\"' ) {
+			return Character.valueOf( (char)0x22 );
+		} else if ( second.charValue() == '%' ) {
+			return Character.valueOf( (char)0x25 );
+		} else if ( second.charValue() == '\'' ) {
+			return Character.valueOf( (char)0x27 );
+		} else if ( second.charValue() == '\\' ) {
+			return Character.valueOf( (char)0x5c );
+		} else if ( second.charValue() == '_' ) {
+			return Character.valueOf( (char)0x5f );
+		} else {
+			return second;
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/OracleCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/OracleCodec.java.svn-base
new file mode 100644
index 0000000..953fece
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/OracleCodec.java.svn-base
@@ -0,0 +1,90 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+
+/**
+ * Implementation of the Codec interface for Oracle strings. This function will only protect you from SQLi in the case of user data
+ * bring placed within an Oracle quoted string such as:
+ * 
+ * select * from table where user_name='  USERDATA    ';
+ * 
+ * @see <a href="http://oraqa.com/2006/03/20/how-to-escape-single-quotes-in-strings/">how-to-escape-single-quotes-in-strings</a>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class OracleCodec extends Codec {
+
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Encodes ' to ''
+     *
+	 * Encodes ' to ''
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		if ( c.charValue() == '\'' )
+        	return "\'\'";
+        return ""+c;
+	}
+	
+
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 *
+	 * Formats all are legal
+	 *   '' decodes to '
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( second.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+		return( Character.valueOf( '\'' ) );
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/PercentCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/PercentCodec.java.svn-base
new file mode 100644
index 0000000..307da47
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/PercentCodec.java.svn-base
@@ -0,0 +1,154 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * Implementation of the Codec interface for percent encoding (aka URL encoding).
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class PercentCodec extends Codec
+{
+	private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+	@SuppressWarnings("unused")
+	private static final String RFC3986_RESERVED_STR = ":/?#[]@!$&'()*+,;=";
+	private static final String RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR = "-._~";
+		// rfc3986 2.3: For consistency, percent-encoded octets
+		// in the ranges of ALPHA (%41-%5A and %61-%7A), DIGIT
+		// (%30-%39), hyphen (%2D), period (%2E), underscore
+		// (%5F), or tilde (%7E) should not be created by URI
+		// producers
+	private static final boolean ENCODED_NON_ALPHA_NUMERIC_UNRESERVED = true;
+	private static final String UNENCODED_STR = ALPHA_NUMERIC_STR +
+		(ENCODED_NON_ALPHA_NUMERIC_UNRESERVED ? "" : RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR);
+	private static final Set<Character> UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR);
+
+	/**
+	 * Convinence method to encode a string into UTF-8. This
+	 * wraps the {@link UnsupportedEncodingException} that
+	 * {@link String#getBytes(String)} throws in a
+	 * {@link IllegalStateException} as UTF-8 support is required
+	 * by the Java spec and should never throw this exception.
+	 * @param str the string to encode
+	 * @return str encoded in UTF-8 as bytes.
+	 * @throws IllegalStateException wrapped {@link
+	 *	UnsupportedEncodingException} if
+	 *	{@link String.getBytes(String)} throws it.
+	 */
+	private static byte[] toUtf8Bytes(String str)
+	{
+		try
+		{
+			return str.getBytes("UTF-8");
+		}
+		catch(UnsupportedEncodingException e)
+		{
+			throw new IllegalStateException("The Java spec requires UTF-8 support.", e);
+		}
+	}
+
+	/**
+	 * Append the two upper case hex characters for a byte.
+	 * @param sb The string buffer to append to.
+	 * @param b The byte to hexify
+	 * @return sb with the hex characters appended.
+	 */
+	// rfc3986 2.1: For consistency, URI producers 
+	// should use uppercase hexadecimal digits for all percent-
+	// encodings.
+	private static StringBuilder appendTwoUpperHex(StringBuilder sb, int b)
+	{
+		if(b < Byte.MIN_VALUE || b > Byte.MAX_VALUE)
+			throw new IllegalArgumentException("b is not a byte (was " + b + ')');
+		b &= 0xFF;
+		if(b<0x10)
+			sb.append('0');
+		return sb.append(Integer.toHexString(b).toUpperCase());
+	}
+
+	/**
+	 * Encode a character for URLs
+	 * @param immune characters not to encode
+	 * @param c character to encode
+	 * @return the encoded string representing c
+	 */
+	public String encodeCharacter( char[] immune, Character c )
+	{
+		String cStr = String.valueOf(c.charValue());
+		byte[] bytes;
+		StringBuilder sb;
+
+		if(UNENCODED_SET.contains(c))
+			return cStr;
+
+		bytes = toUtf8Bytes(cStr);
+		sb = new StringBuilder(bytes.length * 3);
+		for(byte b : bytes)
+			appendTwoUpperHex(sb.append('%'), b);
+		return sb.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Formats all are legal both upper/lower case:
+	 *   %hh;
+	 *   
+	 * @param input
+	 * 			encoded character using percent characters (such as URL encoding)
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+		if (first != '%' ) {
+			input.reset();
+			return null;
+		}
+
+		// Search for exactly 2 hex digits following
+		StringBuilder sb = new StringBuilder();
+		for ( int i=0; i<2; i++ ) {
+			Character c = input.nextHex();
+			if ( c != null ) sb.append( c );
+		}
+		if ( sb.length() == 2 ) {
+			try {
+				// parse the hex digit and create a character
+				int i = Integer.parseInt(sb.toString(), 16);
+				if (Character.isValidCodePoint(i)) {
+					return (char) i;
+				}
+			} catch( NumberFormatException ignored ) { }
+		}
+		input.reset();
+		return null;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/PushbackString.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/PushbackString.java.svn-base
new file mode 100644
index 0000000..d218eb9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/PushbackString.java.svn-base
@@ -0,0 +1,185 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * The pushback string is used by Codecs to allow them to push decoded characters back onto a string
+ * for further decoding. This is necessary to detect double-encoding.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class PushbackString {
+
+	private String input;
+	private Character pushback;
+	private Character temp;
+	private int index = 0;
+	private int mark = 0;
+	
+    /**
+     *
+     * @param input
+     */
+    public PushbackString( String input ) {
+		this.input = input;
+	}
+
+    /**
+     *
+     * @param c
+     */
+    public void pushback( Character c ) {
+		pushback = c;
+	}
+	
+
+    /**
+     * Get the current index of the PushbackString. Typically used in error messages.
+     * @return The current index of the PushbackString.
+     */
+    public int index() {
+		return index;
+	}
+	
+    /**
+     *
+     * @return
+     */
+    public boolean hasNext() {
+		if ( pushback != null ) return true;
+		if ( input == null ) return false;
+		if ( input.length() == 0 ) return false;
+		if ( index >= input.length() ) return false;
+		return true;		
+	}
+	
+    /**
+     *
+     * @return
+     */
+    public Character next() {
+		if ( pushback != null ) {
+			Character save = pushback;
+			pushback = null;
+			return save;
+		}
+		if ( input == null ) return null;
+		if ( input.length() == 0 ) return null;
+		if ( index >= input.length() ) return null;		
+		return Character.valueOf( input.charAt(index++) );
+	}
+	
+    /**
+    *
+    * @return
+    */
+   public Character nextHex() {
+		Character c = next();
+		if ( c == null ) return null;
+		if ( isHexDigit( c ) ) return c;
+		return null;
+	}
+
+   /**
+   *
+   * @return
+   */
+  public Character nextOctal() {
+		Character c = next();
+		if ( c == null ) return null;
+		if ( isOctalDigit( c ) ) return c;
+		return null;
+	}
+
+  /**
+ * Returns true if the parameter character is a hexidecimal digit 0 through 9, a through f, or A through F.
+  * @param c
+  * @return
+  */
+ public static boolean isHexDigit( Character c ) {
+		if ( c == null ) return false;
+		char ch = c.charValue();
+		return (ch >= '0' && ch <= '9' ) || (ch >= 'a' && ch <= 'f' ) || (ch >= 'A' && ch <= 'F' );
+	}
+
+ /**
+ * Returns true if the parameter character is an octal digit 0 through 7.
+ * @param c
+ * @return
+ */
+public static boolean isOctalDigit( Character c ) {
+	if ( c == null ) return false;
+	char ch = c.charValue();
+	return ch >= '0' && ch <= '7';
+}
+
+    /**
+     * Return the next character without affecting the current index.
+     * @return
+     */
+    public Character peek() {
+		if ( pushback != null ) return pushback;
+		if ( input == null ) return null;
+		if ( input.length() == 0 ) return null;
+		if ( index >= input.length() ) return null;		
+		return Character.valueOf( input.charAt(index) );
+	}
+	
+    /**
+     * Test to see if the next character is a particular value without affecting the current index.
+     * @param c
+     * @return
+     */
+    public boolean peek( char c ) {
+		if ( pushback != null && pushback.charValue() == c ) return true;
+		if ( input == null ) return false;
+		if ( input.length() == 0 ) return false;
+		if ( index >= input.length() ) return false;		
+		return input.charAt(index) == c;
+	}	
+	
+    /**
+     *
+     */
+    public void mark() {
+		temp = pushback;
+		mark = index;
+	}
+
+    /**
+     *
+     */
+    public void reset() {
+		pushback = temp;
+		index = mark;
+	}
+	
+    /**
+     *
+     * @return
+     */
+    protected String remainder() {
+		String output = input.substring( index );
+		if ( pushback != null ) {
+			output = pushback + output;
+		}
+		return output;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Trie.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Trie.java.svn-base
new file mode 100644
index 0000000..da11268
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/Trie.java.svn-base
@@ -0,0 +1,172 @@
+package org.owasp.esapi.codecs;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public interface Trie<T> extends Map<CharSequence,T>
+{
+	public Map.Entry<CharSequence,T> getLongestMatch(CharSequence key);
+	public Map.Entry<CharSequence,T> getLongestMatch(PushbackReader keyIn) throws IOException;
+	public int getMaxKeyLength();
+
+	static class TrieProxy<T> implements Trie<T>
+	{
+		private Trie<T> wrapped;
+
+		TrieProxy(Trie<T> toWrap)
+		{
+			wrapped = toWrap;
+		}
+
+		protected Trie<T> getWrapped()
+		{
+			return wrapped;
+		}
+
+		public Map.Entry<CharSequence,T> getLongestMatch(CharSequence key)
+		{
+			return wrapped.getLongestMatch(key);
+		}
+
+		public Map.Entry<CharSequence,T> getLongestMatch(PushbackReader keyIn) throws IOException
+		{
+			return wrapped.getLongestMatch(keyIn);
+		}
+
+		public int getMaxKeyLength()
+		{
+			return wrapped.getMaxKeyLength();
+		}
+
+		/* java.util.Map: */
+
+    		public int size()
+		{
+			return wrapped.size();
+		}
+
+    		public boolean isEmpty()
+		{
+			return wrapped.isEmpty();
+		}
+
+    		public boolean containsKey(Object key)
+		{
+			return wrapped.containsKey(key);
+		}
+
+    		public boolean containsValue(Object val)
+		{
+			return wrapped.containsValue(val);
+		}
+
+    		public T get(Object key)
+		{
+			return wrapped.get(key);
+		}
+
+    		public T put(CharSequence key, T value)
+		{
+			return wrapped.put(key, value);
+		}
+
+    		public T remove(Object key)
+		{
+			return wrapped.remove(key);
+		}
+
+    		public void putAll(Map<? extends CharSequence,? extends T> t)
+		{
+			wrapped.putAll(t);
+		}
+
+    		public void clear()
+		{
+			wrapped.clear();
+		}
+
+    		public Set<CharSequence> keySet()
+		{
+			return wrapped.keySet();
+		}
+
+    		public Collection<T> values()
+		{
+			return wrapped.values();
+		}
+
+    		public Set<Map.Entry<CharSequence,T>> entrySet()
+		{
+			return wrapped.entrySet();
+		}
+
+    		public boolean equals(Object other)
+		{
+			return wrapped.equals(other);
+		}
+
+    		public int hashCode()
+		{
+			return wrapped.hashCode();
+		}
+	}
+
+	static class Unmodifiable<T> extends TrieProxy<T>
+	{
+		Unmodifiable(Trie<T> toWrap)
+		{
+			super(toWrap);
+		}
+
+    		public T put(CharSequence key, T value)
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public T remove(CharSequence key)
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public void putAll(Map<? extends CharSequence,? extends T> t)
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public void clear()
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public Set<CharSequence> keySet()
+		{
+			return Collections.unmodifiableSet(super.keySet());
+		}
+
+    		public Collection<T> values()
+		{
+			return Collections.unmodifiableCollection(super.values());
+		}
+
+    		public Set<Map.Entry<CharSequence,T>> entrySet()
+		{
+			return Collections.unmodifiableSet(super.entrySet());
+		}
+	}
+
+	public static class Util
+	{
+		private Util()
+		{
+		}
+
+		static <T> Trie<T> unmodifiable(Trie<T> toWrap)
+		{
+			return new Unmodifiable<T>(toWrap);
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/UnixCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/UnixCodec.java.svn-base
new file mode 100644
index 0000000..19ecc80
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/UnixCodec.java.svn-base
@@ -0,0 +1,82 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for '\' encoding from Unix command shell.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class UnixCodec extends Codec {
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns backslash-encoded character
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+        return "\\" + c;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * <p>
+	 * Formats all are legal both upper/lower case:
+	 *   \x - all special characters
+	 *   
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\\' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/VBScriptCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/VBScriptCodec.java.svn-base
new file mode 100644
index 0000000..2dccbbd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/VBScriptCodec.java.svn-base
@@ -0,0 +1,117 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import org.owasp.esapi.EncoderConstants;
+
+
+/**
+ * Implementation of the Codec interface for 'quote' encoding from VBScript.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class VBScriptCodec extends Codec {
+
+	/**
+	 * Encode a String so that it can be safely used in a specific context.
+	 * 
+     * @param immune
+     * @param input
+	 * 		the String to encode
+	 * @return the encoded String
+	 */
+    public String encode(char[] immune, String input) {
+    	StringBuilder sb = new StringBuilder();
+		boolean encoding = false;
+		boolean inquotes = false;
+		for ( int i=0; i<input.length(); i++ ) {
+			char c = input.charAt(i);
+			
+			// handle normal characters and surround them with quotes
+			if (containsCharacter(c, EncoderConstants.CHAR_ALPHANUMERICS) || containsCharacter(c, immune)) {
+				if ( encoding && i > 0 ) sb.append( "&" );
+				if ( !inquotes && i > 0 ) sb.append( "\"" );
+				sb.append( c );
+				inquotes = true;
+				encoding = false;
+				
+			// handle characters that need encoding
+			} else {
+				if ( inquotes && i < input.length() ) sb.append( "\"" );
+				if ( i > 0 ) sb.append( "&" );
+				sb.append( encodeCharacter( immune, Character.valueOf( c ) ) );
+				inquotes = false;
+				encoding = true;
+			}
+		}
+		return sb.toString();
+    }
+
+
+	/**
+	 * Returns quote-encoded character
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+        return "chrw(" + (int)c.charValue() + ")";
+	}
+	
+	
+	
+	/**
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal both upper/lower case:
+	 *   "x - all special characters
+	 *   " + chr(x) + "  - not supported yet
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\"' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/WindowsCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/WindowsCodec.java.svn-base
new file mode 100644
index 0000000..719f009
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/WindowsCodec.java.svn-base
@@ -0,0 +1,82 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for '^' encoding from Windows command shell.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class WindowsCodec extends Codec {
+
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns Windows shell encoded character (which is ^)
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+        return "^" + c;
+	}
+	
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * <p>
+	 * Formats all are legal both upper/lower case:
+	 *   ^x - all special characters
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '^' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/XMLEntityCodec.java.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/XMLEntityCodec.java.svn-base
new file mode 100644
index 0000000..e248392
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/XMLEntityCodec.java.svn-base
@@ -0,0 +1,298 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ */
+package org.owasp.esapi.codecs;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * Implementation of the Codec interface for XML entity encoding.
+ * This differes from HTML entity encoding in that only the following
+ * named entities are predefined:
+ * <ul>
+ * 	<li>lt</li>
+ * 	<li>gt</li>
+ * 	<li>amp</li>
+ * 	<li>apos</li>
+ * 	<li>quot</li>
+ * </ul>
+ * However, the XML Specification 1.0 states in section 4.6 "Predefined
+ * Entities" that these should still be declared for interoperability
+ * purposes. As such, encoding in this class will not use them.
+ *
+ * It's also worth noting that unlike the HTMLEntityCodec, a trailing
+ * semicolon is required and all valid codepoints are accepted.
+ *
+ * Note that it is a REALLY bad idea to use this for decoding as an XML
+ * document can declare arbitrary entities that this Codec has no way
+ * of knowing about. Decoding is included for completeness but it's use
+ * is not recommended. Use a XML parser instead!
+ */
+public class XMLEntityCodec extends Codec
+{
+	private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+	private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t";
+	private static final Set<Character> UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR);
+	private static final HashTrie<Character> entityToCharacterMap;
+
+	static
+	{	// populate entitites
+		entityToCharacterMap = new HashTrie<Character>();
+		entityToCharacterMap.put("lt", '<');
+		entityToCharacterMap.put("gt", '>');
+		entityToCharacterMap.put("amp", '&');
+		entityToCharacterMap.put("apos", '\'');
+		entityToCharacterMap.put("quot", '"');
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Encodes a Character using XML entities as necessary.
+	 *
+	 * @param immune characters that should not be encoded as entities
+	 */
+	public String encodeCharacter(char[] immune, Character c)
+	{
+		// check for immune characters
+		if(containsCharacter(c, immune))
+			return c.toString();
+
+		// check for unencoded characters
+		if(UNENCODED_SET.contains(c))
+			return c.toString();
+
+		return "&#x" + Integer.toHexString(c.charValue()) + ";";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Legal formats:
+	 * <ul>
+	 * 	<li>&#dddd;</li>
+	 * 	<li>&#xhhhh;</li>
+	 * 	<li>&name;</li>
+	 * </ul>
+	 */
+	public Character decodeCharacter(PushbackString input)
+	{
+		Character ret = null;
+		Character first;
+		Character second;
+
+		input.mark();
+		try
+		{
+			first = input.next();
+			if(first == null)
+				return null;
+
+			// if this is not an encoded character, return null
+			if(first != '&')
+				return null;
+
+			// test for numeric encodings
+			second = input.next();
+			if(second==null)
+				return null;
+
+			if(second=='#')
+			{	// handle numbers
+				ret = getNumericEntity(input);
+			}
+			else if(Character.isLetter(second.charValue()))
+			{	// handle entities
+				input.pushback(second);
+				ret = getNamedEntity(input);
+			}
+		}
+		finally
+		{
+			if(ret == null)
+				input.reset();
+		}
+		return ret;
+	}
+
+	/**
+	 * Converts the rest of a numeric entity to a character.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &#
+	 * @return The character decoded or null on failure.
+	 */
+	private static Character getNumericEntity(PushbackString input)
+	{
+		Character first = input.peek();
+
+		if(first == null)
+			return null;
+
+		if(first=='x'||first=='X')
+		{
+			input.next();	// nuke X
+			return parseHex(input);
+		}
+		return parseNumber(input);
+	}
+
+	/**
+	 * Convert a integer code point to a Character.
+	 * @param i the integer
+	 * @return i as a Character or null if i is a invalid code point
+	 * 	or outside of the Java char range.
+	 */
+	private static Character int2char(int i)
+	{
+		if(!Character.isValidCodePoint(i))
+			return null;
+		if(!(Character.MIN_VALUE <= i && i <= Character.MAX_VALUE))
+			return null;	// we can't 0x010000-0x100000 currently
+		return (char)i;
+	}
+
+	/**
+	 * Converts the rest of a decimal numeric entity to a character.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &# and that
+	 *	the next char is not a 'x' or 'X'.
+	 * @return The character decoded or null on failutre.
+	 */
+	private static Character parseNumber(PushbackString input)
+	{
+		StringBuilder sb = new StringBuilder();
+		Character c;
+		while((c=input.next())!=null)
+		{
+			// end of entity?
+			if(c==';')
+				break;
+
+			// check for digit
+			if(!Character.isDigit(c.charValue()))
+				return null;
+			sb.append(c);
+		}
+		if(c==null)
+			return null;	// not ';' termintated
+		if(sb.length()<=0)	// no digits
+			return null;
+		try
+		{
+			return int2char(Integer.parseInt(sb.toString()));
+		}
+		catch(NumberFormatException e)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * Converts the rest of a hexidecimal numeric entity to a character.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &#[xX]
+	 * @return The character decoded or null on failutre.
+	 */
+	private static Character parseHex(PushbackString input)
+	{
+		Character c;
+		StringBuilder sb = new StringBuilder();
+		input_loop: while((c=input.next())!=null)
+		{
+			switch(c.charValue())
+			{
+				case 'a':
+				case 'b':
+				case 'c':
+				case 'd':
+				case 'e':
+				case 'f':
+				case 'A':
+				case 'B':
+				case 'C':
+				case 'D':
+				case 'E':
+				case '0':
+				case '1':
+				case '2':
+				case '3':
+				case '4':
+				case '5':
+				case '6':
+				case '7':
+				case '8':
+				case '9':
+					sb.append(c);
+					break;
+				case ';':
+					break input_loop;
+				default:
+					return null;
+			}
+		}
+		if(c==null)
+			return null;	// not ';' termintated
+		if(sb.length()<=0)	// no digits
+			return null;
+		try
+		{
+			return int2char(Integer.parseInt(sb.toString(),16));
+		}
+		catch(NumberFormatException e)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * 
+	 * Converts the rest of a named entity to a character.
+	 * null if no decoding is possible.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &.
+	 * @return The character decoded or null on failutre.
+	 */
+	private Character getNamedEntity(PushbackString input)
+	{
+		StringBuilder possible = new StringBuilder();
+		Map.Entry<CharSequence,Character> entry;
+		int len;
+
+		// kludge around PushbackString....
+		len = Math.min(input.remainder().length(), entityToCharacterMap.getMaxKeyLength()+1);
+		for(int i=0;i<len;i++)
+			possible.append(Character.toLowerCase(input.next()));
+
+		// look up the longest match
+		entry = entityToCharacterMap.getLongestMatch(possible);
+		if(entry == null)
+			return null;	// no match, caller will reset input
+		len = entry.getKey().length();	// what matched's length
+		if(possible.length() <= len || possible.charAt(len)!=';')
+			return null;	// not semicolon
+
+		// fixup input
+		input.reset();
+		input.next();	// read &
+		for(int i=0;i<len;i++)
+			input.next();
+		input.next();	// read semicolen
+		return entry.getValue();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..0951a73
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/.svn/text-base/package.html.svn-base
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains codecs for application layer encoding/escaping schemes that can be used for
+both canonicalization and output encoding. By using the codecs to decode (canonicalize) input
+before validation, many attacks can be detected and handled.  By using the codecs to encode
+untrusted data before sending it to an interpreter, a wide variety of 'injection' attacks can
+be stopped. However,
+this package does not currently address issues related to converting between byte-streams and 
+internal character representations, such as overlong UTF-8 issues. Those are left to the platform.
+The codecs cover protocol encodings such as HTML entity encoding and percent encoding, but also
+common product escaping schemes, such as Unix, Windows, MySQL, and Oracle.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/codecs/Base64.java b/src/main/java/org/owasp/esapi/codecs/Base64.java
new file mode 100644
index 0000000..3f2d384
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/Base64.java
@@ -0,0 +1,1855 @@
+package org.owasp.esapi.codecs;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+// CHECKME: Version at http://iharder.net/base64 is up to v2.3.3. Some semantic changes
+// starting with v2.3. Should we upgrade and then add ESAPI logging or stay at 2.2.2 base?
+// I think that really depends on how much OWASP ESAPI plans on tracking changes to this
+// version vs. if the plan was just to fork from it and maintain OWASP's own version.
+// At this point, I think I prefer split from tracking Harder's original, but I'm easily
+// persuaded otherwise. - Kevin Wall
+
+/**
+ * <p>Encodes and decodes to and from Base64 notation.</p>
+ * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
+ *
+ * <p>The <tt>options</tt> parameter, which appears in a few places, is used to pass 
+ * several pieces of information to the encoder. In the "higher level" methods such as 
+ * encodeBytes( bytes, options ) the options parameter can be used to indicate such 
+ * things as first gzipping the bytes before encoding them, not inserting linefeeds 
+ * (though that breaks strict Base64 compatibility), and encoding using the URL-safe 
+ * and Ordered dialects.</p>
+ *
+ * <p>The constants defined in Base64 can be OR-ed together to combine options, so you 
+ * might make a call like this:</p>
+ *
+ * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES );</code>
+ *
+ * <p>to compress the data before encoding it and then making the output have no newline characters.</p>
+ *
+ *
+ * <p>
+ * Change Log:
+ * </p>
+ * <ul>
+ *  <li>v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the
+ *   Base64.InputStream class to encode and decode on the fly which uses
+ *   less memory than encoding/decoding an entire file into memory before writing.</li>
+ *  <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
+ *   when using very small files (~< 40 bytes).</li>
+ *  <li>v2.2 - Added some helper methods for encoding/decoding directly from
+ *   one file to the next. Also added a main() method to support command line
+ *   encoding/decoding from one file to the next. Also added these Base64 dialects:
+ *   <ol>
+ *   <li>The default is RFC3548 format.</li>
+ *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
+ *   URL and file name friendly format as described in Section 4 of RFC3548.
+ *   http://www.faqs.org/rfcs/rfc3548.html</li>
+ *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
+ *   URL and file name friendly format that preserves lexical ordering as described
+ *   in http://www.faqs.org/qa/rfcc-1940.html</li>
+ *   </ol>
+ *   Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a>
+ *   for contributing the new Base64 dialects.
+ *  </li>
+ * 
+ *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
+ *   some convenience methods for reading and writing to and from files.</li>
+ *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
+ *   with other encodings (like EBCDIC).</li>
+ *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
+ *   encoded data was a single byte.</li>
+ *  <li>v2.0 - I got rid of methods that used booleans to set options. 
+ *   Now everything is more consolidated and cleaner. The code now detects
+ *   when data that's being decoded is gzip-compressed and will decompress it
+ *   automatically. Generally things are cleaner. You'll probably have to
+ *   change some method calls that you were making to support the new
+ *   options format (<tt>int</tt>s that you "OR" together).</li>
+ *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
+ *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
+ *   Added the ability to "suspend" encoding in the Output Stream so        
+ *   you can turn on and off the encoding if you need to embed base64       
+ *   data in an otherwise "normal" stream (like an XML file).</li>  
+ *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
+ *      This helps when using GZIP streams.
+ *      Added the ability to GZip-compress objects before encoding them.</li>
+ *  <li>v1.4 - Added helper methods to read/write files.</li>
+ *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
+ *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
+ *      where last buffer being read, if not completely full, was not returned.</li>
+ *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
+ *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
+ * </ul>
+ *
+ * <p>
+ * I am placing this code in the Public Domain. Do with it as you will.
+ * This software comes with no guarantees or warranties but with
+ * plenty of well-wishing instead!
+ * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
+ * periodically to check for updates or to contribute improvements.
+ * </p>
+ *
+ * @author Robert Harder
+ * @author rob at iharder.net
+ * @version 2.2.2
+ */
+public class Base64
+{
+    
+/* ********  P U B L I C   F I E L D S  ******** */   
+    
+    
+    /** No options specified. Value is zero. */
+    public final static int NO_OPTIONS = 0;
+    
+    /** Specify encoding. */
+    public final static int ENCODE = 1;
+    
+    
+    /** Specify decoding. */
+    public final static int DECODE = 0;
+    
+    
+    /** Specify that data should be gzip-compressed. */
+    public final static int GZIP = 2;
+    
+    
+    /** Don't break lines when encoding (violates strict Base64 specification) */
+    public final static int DONT_BREAK_LINES = 8;
+	
+	/** 
+	 * Encode using Base64-like encoding that is URL- and Filename-safe as described
+	 * in Section 4 of RFC3548: 
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * It is important to note that data encoded this way is <em>not</em> officially valid Base64, 
+	 * or at the very least should not be called Base64 without also specifying that is
+	 * was encoded using the URL- and Filename-safe dialect.
+	 */
+	 public final static int URL_SAFE = 16;
+	 
+	 
+	 /**
+	  * Encode using the special "ordered" dialect of Base64 described here:
+	  * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	  */
+	 public final static int ORDERED = 32;
+    
+    
+/* ********  P R I V A T E   F I E L D S  ******** */  
+    
+    
+    /** Maximum line length (76) of Base64 output. */
+    private final static int MAX_LINE_LENGTH = 76;
+    
+    
+    /** The equals sign (=) as a byte. */
+    private final static byte EQUALS_SIGN = (byte)'=';
+    
+    
+    /** The new line character (\n) as a byte. */
+    private final static byte NEW_LINE = (byte)'\n';
+    
+    
+    /** Preferred encoding. */
+    private final static String PREFERRED_ENCODING = "UTF-8";
+    
+    /** End of line character. */
+    private final static String EOL = System.getProperty("line.separator", "\n");
+	
+	
+    // I think I end up not using the BAD_ENCODING indicator.
+    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
+    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
+    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
+	
+    private static final Logger logger = ESAPI.getLogger("Base64");
+    
+	
+/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */	
+    
+    /** The 64 valid Base64 values. */
+    //private final static byte[] ALPHABET;
+	/* Host platform me be something funny like EBCDIC, so we hard code these values. */
+	private final static byte[] _STANDARD_ALPHABET =
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
+        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
+        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
+    };
+	
+    
+    /** 
+     * Translates a Base64 value to either its 6-bit reconstruction value
+     * or a negative number indicating some other meaning.
+     **/
+    private final static byte[] _STANDARD_DECODABET =
+    {   
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+        -5,-5,                                      // Whitespace: Tab and Linefeed
+        -9,-9,                                      // Decimal 11 - 12
+        -5,                                         // Whitespace: Carriage Return
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+        -5,                                         // Whitespace: Space
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+        62,                                         // Plus sign at decimal 43
+        -9,-9,-9,                                   // Decimal 44 - 46
+        63,                                         // Slash at decimal 47
+        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+        -9,-9,-9,                                   // Decimal 58 - 60
+        -1,                                         // Equals sign at decimal 61
+        -9,-9,-9,                                      // Decimal 62 - 64
+        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
+        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+        -9,-9,-9,-9                                 // Decimal 123 - 126
+        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+	
+	
+/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
+	
+	/**
+	 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
+	 */
+    private final static byte[] _URL_SAFE_ALPHABET =
+    {
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
+      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
+    };
+	
+	/**
+	 * Used in decoding URL- and Filename-safe dialects of Base64.
+	 */
+    private final static byte[] _URL_SAFE_DECODABET =
+    {   
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      62,                                         // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      63,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+
+
+/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
+
+	/**
+	 * I don't get the point of this technique, but it is described here:
+	 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	 */
+    private final static byte[] _ORDERED_ALPHABET =
+    {
+      (byte)'-',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
+      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'_',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
+    };
+	
+	/**
+	 * Used in decoding the "ordered" dialect of Base64.
+	 */
+    private final static byte[] _ORDERED_DECODABET =
+    {   
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      0,                                          // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
+      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      37,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
+      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+	
+/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
+
+
+	/**
+	 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URLSAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getAlphabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;
+		else return _STANDARD_ALPHABET;
+		
+	}	// end getAlphabet
+	
+	
+	/**
+	 * Returns one of the _SOMETHING_DECODABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URL_SAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getDecodabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;
+		else return _STANDARD_DECODABET;
+		
+	}	// end getAlphabet
+        
+
+    
+    /** Defeats instantiation. */
+    private Base64(){}
+    
+
+    /**
+     * Encodes or decodes two files from the command line;
+     * <strong>feel free to delete this method (in fact you probably should)
+     * if you're embedding this code into a larger program</strong>.
+     * @param args
+     */
+    public final static void main( String[] args )
+    {
+        if( args.length < 3 ){
+            usage("Not enough arguments.");
+        }   // end if: args.length < 3
+        else {
+            String flag = args[0];
+            String infile = args[1];
+            String outfile = args[2];
+            if( flag.equals( "-e" ) ){
+                Base64.encodeFileToFile( infile, outfile );
+            }   // end if: encode
+            else if( flag.equals( "-d" ) ) {
+                Base64.decodeFileToFile( infile, outfile );
+            }   // end else if: decode    
+            else {
+                usage( "Unknown flag: " + flag );
+            }   // end else    
+        }   // end else
+    }   // end main
+
+    /**
+     * Prints command line usage.
+     *
+     * @param msg A message to include with usage info.
+     */
+    private final static void usage( String msg )
+    {
+        System.err.println( msg );
+        System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" );
+    }   // end usage
+    
+    
+/* ********  E N C O D I N G   M E T H O D S  ******** */    
+    
+    
+    /**
+     * Encodes up to the first three bytes of array <var>threeBytes</var>
+     * and returns a four-byte array in Base64 notation.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.
+     * The array <var>threeBytes</var> needs only be as big as
+     * <var>numSigBytes</var>.
+     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
+     *
+     * @param b4 A reusable byte array to reduce array instantiation
+     * @param threeBytes the array to convert
+     * @param numSigBytes the number of significant bytes in your array
+     * @return four byte array in Base64 notation.
+     * @since 1.5.1
+     */
+    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )
+    {
+        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
+        return b4;
+    }   // end encode3to4
+
+    
+    /**
+     * <p>Encodes up to three bytes of the array <var>source</var>
+     * and writes the resulting four Base64 bytes to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 3 for
+     * the <var>source</var> array or <var>destOffset</var> + 4 for
+     * the <var>destination</var> array.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.</p>
+	 * <p>This is the lowest level of the encoding methods with
+	 * all possible parameters.</p>
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param numSigBytes the number of significant bytes in your array
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the <var>destination</var> array
+     * @since 1.3
+     */
+    private static byte[] encode3to4( 
+     byte[] source, int srcOffset, int numSigBytes,
+     byte[] destination, int destOffset, int options )
+    {
+		byte[] ALPHABET = getAlphabet( options ); 
+	
+        //           1         2         3  
+        // 01234567890123456789012345678901 Bit position
+        // --------000000001111111122222222 Array position from threeBytes
+        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
+        //          >>18  >>12  >> 6  >> 0  Right shift necessary
+        //                0x3f  0x3f  0x3f  Additional AND
+        
+        // Create buffer with zero-padding if there are only one or two
+        // significant bytes passed in the array.
+        // We have to shift left 24 in order to flush out the 1's that appear
+        // when Java treats a value as negative that is cast from a byte to an int.
+        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
+                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
+                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
+
+        switch( numSigBytes )
+        {
+            case 3:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
+                return destination;
+                
+            case 2:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            case 1:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = EQUALS_SIGN;
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            default:
+                return destination;
+        }   // end switch
+    }   // end encode3to4
+    
+    
+    
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * The object is not GZip-compressed before being encoded.
+     *
+     * @param serializableObject The object to encode
+     * @return The Base64-encoded object
+     * @since 1.4
+     */
+    public static String encodeObject( java.io.Serializable serializableObject )
+    {
+        return encodeObject( serializableObject, NO_OPTIONS );
+    }   // end encodeObject
+    
+
+
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     * @param serializableObject The object to encode
+     * @param options Specified options
+     * @return The Base64-encoded object
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeObject( java.io.Serializable serializableObject, int options )
+    {
+        // Streams
+        java.io.ByteArrayOutputStream  baos  = null; 
+        java.io.OutputStream           b64os = null; 
+        java.io.ObjectOutputStream     oos   = null; 
+        java.util.zip.GZIPOutputStream gzos  = null;
+        
+        // Isolate options
+        int gzip           = (options & GZIP);
+        //int dontBreakLines = (options & DONT_BREAK_LINES);
+        
+        try
+        {
+            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+            baos  = new java.io.ByteArrayOutputStream();
+            b64os = new Base64.OutputStream( baos, ENCODE | options );
+    
+            // GZip?
+            if( gzip == GZIP )
+            {
+                gzos = new java.util.zip.GZIPOutputStream( b64os );
+                oos  = new java.io.ObjectOutputStream( gzos );
+            }   // end if: gzip
+            else
+                oos   = new java.io.ObjectOutputStream( b64os );
+            
+            oos.writeObject( serializableObject );
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e );
+            return null;
+        }   // end catch
+        finally
+        {
+            try{ oos.close();   } catch( Exception e ){}
+            try{ gzos.close();  } catch( Exception e ){}
+            try{ b64os.close(); } catch( Exception e ){}
+            try{ baos.close();  } catch( Exception e ){}
+        }   // end finally
+        
+        // Return value according to relevant encoding.
+        try 
+        {
+            return new String( baos.toByteArray(), PREFERRED_ENCODING );
+        }   // end try
+        catch (java.io.UnsupportedEncodingException uue)
+        {
+            return new String( baos.toByteArray() );
+        }   // end catch
+        
+    }   // end encode
+    
+    
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @return The Base64-encoded resulting string
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source )
+    {
+        return encodeBytes( source, 0, source.length, NO_OPTIONS );
+    }   // end encodeBytes
+    
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param options Specified options
+     * @return The Base64-encoded resulting string
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int options )
+    {   
+        return encodeBytes( source, 0, source.length, options );
+    }   // end encodeBytes
+    
+    
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @return The Base64-encoded resulting string
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source, int off, int len )
+    {
+        return encodeBytes( source, off, len, NO_OPTIONS );
+    }   // end encodeBytes
+    
+    
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+     * @return The Base64-encoded resulting string
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int off, int len, int options )
+    {
+        // Isolate options
+        int dontBreakLines = ( options & DONT_BREAK_LINES );
+        int gzip           = ( options & GZIP   );
+        
+        // Compress?
+        if( gzip == GZIP )
+        {
+            java.io.ByteArrayOutputStream  baos  = null;
+            java.util.zip.GZIPOutputStream gzos  = null;
+            Base64.OutputStream            b64os = null;
+            
+    
+            try
+            {
+                // GZip -> Base64 -> ByteArray
+                baos = new java.io.ByteArrayOutputStream();
+                b64os = new Base64.OutputStream( baos, ENCODE | options );
+                gzos  = new java.util.zip.GZIPOutputStream( b64os ); 
+            
+                gzos.write( source, off, len );
+                gzos.close();
+            }   // end try
+            catch( java.io.IOException e )
+            {
+                logger.error( Logger.SECURITY_FAILURE, "Problem writing gzip stream", e );
+                return null;
+            }   // end catch
+            finally
+            {
+                try{ gzos.close();  } catch( Exception e ){}
+                try{ b64os.close(); } catch( Exception e ){}
+                try{ baos.close();  } catch( Exception e ){}
+            }   // end finally
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( baos.toByteArray(), PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( baos.toByteArray() );
+            }   // end catch
+        }   // end if: compress
+        
+        // Else, don't compress. Better not to use streams at all then.
+        else
+        {
+            // Convert option to boolean in way that code likes it.
+            boolean breakLines = dontBreakLines == 0;
+            
+            int    len43   = len * 4 / 3;
+            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
+                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
+                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      
+            int d = 0;
+            int e = 0;
+            int len2 = len - 2;
+            int lineLength = 0;
+            for( ; d < len2; d+=3, e+=4 )
+            {
+                encode3to4( source, d+off, 3, outBuff, e, options );
+
+                lineLength += 4;
+                if( breakLines && lineLength == MAX_LINE_LENGTH )
+                {   
+                    outBuff[e+4] = NEW_LINE;
+                    e++;
+                    lineLength = 0;
+                }   // end if: end of line
+            }   // en dfor: each piece of array
+
+            if( d < len )
+            {
+                encode3to4( source, d+off, len - d, outBuff, e, options );
+                e += 4;
+            }   // end if: some padding needed
+
+            
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( outBuff, 0, e, PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( outBuff, 0, e );
+            }   // end catch
+            
+        }   // end else: don't compress
+        
+    }   // end encodeBytes
+    
+
+    
+    
+    
+/* ********  D E C O D I N G   M E T H O D S  ******** */
+    
+    
+    /**
+     * Decodes four bytes from array <var>source</var>
+     * and writes the resulting bytes (up to three of them)
+     * to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 4 for
+     * the <var>source</var> array or <var>destOffset</var> + 3 for
+     * the <var>destination</var> array.
+     * This method returns the actual number of bytes that 
+     * were converted from the Base64 encoding.
+	 * <p>This is the lowest level of the decoding methods with
+	 * all possible parameters.</p>
+     * 
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+	 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+     * @return the number of decoded bytes converted
+     * @since 1.3
+     */
+    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options )
+    {
+		byte[] DECODABET = getDecodabet( options ); 
+	
+        // Example: Dk==
+        if( source[ srcOffset + 2] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
+            
+            destination[ destOffset ] = (byte)( outBuff >>> 16 );
+            return 1;
+        }
+        
+        // Example: DkL=
+        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
+            
+            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
+            return 2;
+        }
+        
+        // Example: DkLE
+        else
+        {
+            try{
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
+                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
+
+            
+            destination[ destOffset     ] = (byte)( outBuff >> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
+            destination[ destOffset + 2 ] = (byte)( outBuff       );
+
+            return 3;
+            }catch( Exception e){
+            	
+        // Remove these after checking -- for context only.
+                // logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
+                // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
+            	
+            	// CHECKME: I replaced the 5 separate logger.error() calls above with a single logger.error() call so they can't
+            	// become interleaved with other log entries from other threads. Normally this would have placed log entries
+            	// on separate lines, so I also added line terminators here as well. (Probably don't want it all on one single
+            	// really long log entry, do we?) Anyhow, somebody should check the formatting to ensure that it's
+            	// esthetically pleasing, etc. But this works for me. I'm also OK if you want to remove all the line terminators
+            	// in which case the declaration for EOL should be removed as well.		- Kevin Wall
+                
+                StringBuilder sb = new StringBuilder("Problem writing object:");
+                sb.append(EOL);
+                sb.append( source[srcOffset]   ).append(": ").append( ( DECODABET[ source[ srcOffset     ] ]  ) ).append(EOL);
+                sb.append( source[srcOffset+1] ).append(": ").append( ( DECODABET[ source[ srcOffset + 1 ] ]  ) ).append(EOL);
+                sb.append( source[srcOffset+2] ).append(": ").append( ( DECODABET[ source[ srcOffset + 2 ] ]  ) ).append(EOL);
+                sb.append( source[srcOffset+3] ).append(": ").append( ( DECODABET[ source[ srcOffset + 3 ] ]  ) ).append(EOL);
+                
+                logger.error( Logger.SECURITY_FAILURE, sb.toString(), e );
+                return -1;
+            }   // end catch
+        }
+    }   // end decodeToBytes
+    
+    
+    
+    
+    /**
+     * Very low-level access to decoding ASCII characters in
+     * the form of a byte array. Does not support automatically
+     * gunzipping or any other "fancy" features.
+     *
+     * @param source The Base64 encoded data
+     * @param off    The offset of where to begin decoding
+     * @param len    The length of characters to decode
+     * @param options
+     * @return decoded data
+     * @since 1.3
+     */
+    public static byte[] decode( byte[] source, int off, int len, int options )
+    {
+		byte[] DECODABET = getDecodabet( options );
+	
+        int    len34   = len * 3 / 4;
+        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+        int    outBuffPosn = 0;
+        
+        byte[] b4        = new byte[4];
+        int    b4Posn    = 0;
+        int    i         = 0;
+        byte   sbiCrop   = 0;
+        byte   sbiDecode = 0;
+        for( i = off; i < off+len; i++ )
+        {
+            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
+            sbiDecode = DECODABET[ sbiCrop ];
+            
+            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
+            {
+                if( sbiDecode >= EQUALS_SIGN_ENC )
+                {
+                    b4[ b4Posn++ ] = sbiCrop;
+                    if( b4Posn > 3 )
+                    {
+                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
+                        b4Posn = 0;
+                        
+                        // If that was the equals sign, break out of 'for' loop
+                        if( sbiCrop == EQUALS_SIGN )
+                            break;
+                    }   // end if: quartet built
+                    
+                }   // end if: equals sign or better
+                
+            }   // end if: white space, equals sign or better
+            else
+            {
+            	logger.error( Logger.SECURITY_FAILURE, "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
+                return null;
+            }   // end else: 
+        }   // each input character
+                                   
+        byte[] out = new byte[ outBuffPosn ];
+        System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
+        return out;
+    }   // end decode
+    
+    
+	
+	
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s )
+	{
+		return decode( s, NO_OPTIONS );
+	}
+    
+    
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+	 * @param options encode options such as URL_SAFE
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s, int options )
+    {   
+        byte[] bytes;
+        try
+        {
+            bytes = s.getBytes( PREFERRED_ENCODING );
+        }
+        catch( java.io.UnsupportedEncodingException uee )
+        {
+            bytes = s.getBytes();	// Uses native encoding
+            // CHECKME: Is this correct? I think it should be a warning instead of an error since nothing
+            // is re-thrown. I do think that *some* sort of logging is in order here  especially since UTF-8 should
+            // always be available on all platforms. If it's not, then all bets are off on your runtime env. - Kevin Wall
+            logger.warning( Logger.SECURITY_FAILURE, "Problem decoding string using " +
+            			  PREFERRED_ENCODING + "; substituting native platform encoding instead", uee );
+        }
+        
+        // Decode
+        bytes = decode( bytes, 0, bytes.length, options );
+        
+        
+        // Check to see if it's gzip-compressed
+        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+        if( bytes != null && bytes.length >= 4 )
+        {
+            
+            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);       
+            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) 
+            {
+                java.io.ByteArrayInputStream  bais = null;
+                java.util.zip.GZIPInputStream gzis = null;
+                java.io.ByteArrayOutputStream baos = null;
+                byte[] buffer = new byte[2048];
+                int    length = 0;
+
+                try
+                {
+                    baos = new java.io.ByteArrayOutputStream();
+                    bais = new java.io.ByteArrayInputStream( bytes );
+                    gzis = new java.util.zip.GZIPInputStream( bais );
+
+                    while( ( length = gzis.read( buffer ) ) >= 0 )
+                    {
+                        baos.write(buffer,0,length);
+                    }   // end while: reading input
+
+                    // No error? Get new bytes.
+                    bytes = baos.toByteArray();
+
+                }   // end try
+                catch( java.io.IOException e )
+                {
+                    // Just return originally-decoded bytes
+                }   // end catch
+                finally
+                {
+                    try{ baos.close(); } catch( Exception e ){}
+                    try{ gzis.close(); } catch( Exception e ){}
+                    try{ bais.close(); } catch( Exception e ){}
+                }   // end finally
+
+            }   // end if: gzipped
+        }   // end if: bytes.length >= 2
+        
+        return bytes;
+    }   // end decode
+
+
+    
+
+    /**
+     * Attempts to decode Base64 data and deserialize a Java
+     * Object within. Returns <tt>null</tt> if there was an error.
+     *
+     * @param encodedObject The Base64 data to decode
+     * @return The decoded and deserialized object
+     * @since 1.5
+     */
+    public static Object decodeToObject( String encodedObject )
+    {
+        // Decode and gunzip if necessary
+        byte[] objBytes = decode( encodedObject );
+        
+        java.io.ByteArrayInputStream  bais = null;
+        java.io.ObjectInputStream     ois  = null;
+        Object obj = null;
+        
+        try
+        {
+            bais = new java.io.ByteArrayInputStream( objBytes );
+            ois  = new java.io.ObjectInputStream( bais );
+        
+            obj = ois.readObject();
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e );
+            obj = null;
+        }   // end catch
+        catch( java.lang.ClassNotFoundException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e );
+            obj = null;
+        }   // end catch
+        finally
+        {
+            try{ bais.close(); } catch( Exception e ){}
+            try{ ois.close();  } catch( Exception e ){}
+        }   // end finally
+        
+        return obj;
+    }   // end decodeObject
+    
+    
+    
+    /**
+     * Convenience method for encoding data to a file.
+     *
+     * @param dataToEncode byte array of data to encode in base64 form
+     * @param filename Filename for saving encoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean encodeToFile( byte[] dataToEncode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+            bos = new Base64.OutputStream( 
+                      new java.io.FileOutputStream( filename ), Base64.ENCODE );
+            bos.write( dataToEncode );
+            success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+            try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+        
+        return success;
+    }   // end encodeToFile
+    
+    
+    /**
+     * Convenience method for decoding data to a file.
+     *
+     * @param dataToDecode Base64-encoded data as a string
+     * @param filename Filename for saving decoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean decodeToFile( String dataToDecode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+                bos = new Base64.OutputStream( 
+                          new java.io.FileOutputStream( filename ), Base64.DECODE );
+                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
+                success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+                try{ bos.close(); } catch( Exception e ){}
+        }   // end finally
+        
+        return success;
+    }   // end decodeToFile
+    
+    
+    
+    
+    /**
+     * Convenience method for reading a base64-encoded
+     * file and decoding it.
+     *
+     * @param filename Filename for reading encoded data
+     * @return decoded byte array or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static byte[] decodeFromFile( String filename )
+    {
+        byte[] decodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = null;
+            int length   = 0;
+            int numBytes = 0;
+            
+            // Check for size of file
+            if( file.length() > Integer.MAX_VALUE )
+            {
+                logger.error( Logger.SECURITY_FAILURE, "File is too big for this convenience method (" + file.length() + " bytes)." );
+                return null;
+            }   // end if: file too big for int index
+            buffer = new byte[ (int)file.length() ];
+            
+            // Open a stream
+            bis = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( file ) ), Base64.DECODE );
+            
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+            
+            // Save in a variable to return
+            decodedData = new byte[ length ];
+            System.arraycopy( buffer, 0, decodedData, 0, length );
+            
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Error decoding from file " + filename, e );
+        }   // end catch: IOException
+        finally
+        {
+            try{ if (bis != null ) bis.close(); } catch( Exception e) {}
+        }   // end finally
+        
+        return decodedData;
+    }   // end decodeFromFile
+    
+    
+    
+    /**
+     * Convenience method for reading a binary file
+     * and base64-encoding it.
+     *
+     * @param filename Filename for reading binary data
+     * @return base64-encoded string or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static String encodeFromFile( String filename )
+    {
+        String encodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)
+            int length   = 0;
+            int numBytes = 0;
+            
+            // Open a stream
+            bis = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( file ) ), Base64.ENCODE );
+            
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+            
+            // Save in a variable to return
+            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
+                
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( Logger.SECURITY_FAILURE, "Error encoding from file " + filename, e );
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {}
+        }   // end finally
+        
+        return encodedData;
+        }   // end encodeFromFile
+    
+    
+    
+    
+    /**
+     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean encodeFileToFile( String infile, String outfile )
+    {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try{
+            in  = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( infile ) ), 
+                      Base64.ENCODE );
+            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while( ( read = in.read(buffer) ) >= 0 ){
+                out.write( buffer,0,read );
+            }   // end while: through file
+            success = true;
+        } catch( java.io.IOException exc ){
+            logger.error( Logger.SECURITY_FAILURE, "Problem encoding file to file", exc );
+        } finally{
+            try{ in.close();  } catch( Exception exc ){}
+            try{ out.close(); } catch( Exception exc ){}
+        }   // end finally
+        
+        return success;
+    }   // end encodeFileToFile
+    
+    
+    
+    /**
+     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean decodeFileToFile( String infile, String outfile )
+    {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try{
+            in  = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( infile ) ), 
+                      Base64.DECODE );
+            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while( ( read = in.read(buffer) ) >= 0 ){
+                out.write( buffer,0,read );
+            }   // end while: through file
+            success = true;
+        } catch( java.io.IOException exc ){
+            logger.error( Logger.SECURITY_FAILURE, "Problem decoding file to file", exc );
+        } finally{
+            try{ in.close();  } catch( Exception exc ){}
+            try{ out.close(); } catch( Exception exc ){}
+        }   // end finally
+        
+        return success;
+    }   // end decodeFileToFile
+    
+    
+    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
+    
+    
+    
+    /**
+     * A {@link Base64.InputStream} will read data from another
+     * <tt>java.io.InputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class InputStream extends java.io.FilterInputStream
+    {
+        private boolean encode;         // Encoding or decoding
+        private int     position;       // Current position in the buffer
+        private byte[]  buffer;         // Small buffer holding converted data
+        private int     bufferLength;   // Length of buffer (3 or 4)
+        private int     numSigBytes;    // Number of meaningful bytes in the buffer
+        private int     lineLength;
+        private boolean breakLines;     // Break lines at less than 80 characters
+		private int     options;        // Record options used to create the stream.
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+        
+        
+        /**
+         * Constructs a {@link Base64.InputStream} in DECODE mode.
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @since 1.3
+         */
+        public InputStream( java.io.InputStream in )
+        {   
+            this( in, DECODE );
+        }   // end constructor
+        
+        
+        /**
+         * Constructs a {@link Base64.InputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
+         *
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @param options Specified options
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 2.0
+         */
+        public InputStream( java.io.InputStream in, int options )
+        {   
+            super( in );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 4 : 3;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = -1;
+            this.lineLength   = 0;
+			this.options      = options; // Record for later, mostly to determine which alphabet to use
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+        
+        /**
+         * Reads enough of the input stream to convert
+         * to/from Base64 and returns the next byte.
+         *
+         * @return next byte
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public int read() throws java.io.IOException 
+        { 
+            // Do we need to get data?
+            if( position < 0 )
+            {
+                if( encode )
+                {
+                    byte[] b3 = new byte[3];
+                    int numBinaryBytes = 0;
+                    for( int i = 0; i < 3; i++ )
+                    {
+                        try
+                        { 
+                            int b = in.read();
+                            
+                            // If end of stream, b is -1.
+                            if( b >= 0 )
+                            {
+                                b3[i] = (byte)b;
+                                numBinaryBytes++;
+                            }   // end if: not end of stream
+                            
+                        }   // end try: read
+                        catch( java.io.IOException e )
+                        {   
+                            // Only a problem if we got no data at all.
+                            if( i == 0 )
+                                throw e;
+                            
+                        }   // end catch
+                    }   // end for: each needed input byte
+                    
+                    if( numBinaryBytes > 0 )
+                    {
+                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
+                        position = 0;
+                        numSigBytes = 4;
+                    }   // end if: got data
+                    else
+                    {
+                        return -1;
+                    }   // end else
+                }   // end if: encoding
+                
+                // Else decoding
+                else
+                {
+                    byte[] b4 = new byte[4];
+                    int i = 0;
+                    for( i = 0; i < 4; i++ )
+                    {
+                        // Read four "meaningful" bytes:
+                        int b = 0;
+                        do{ b = in.read(); }
+                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
+                        
+                        if( b < 0 )
+                            break; // Reads a -1 if end of stream
+                        
+                        b4[i] = (byte)b;
+                    }   // end for: each needed input byte
+                    
+                    if( i == 4 )
+                    {
+                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );
+                        position = 0;
+                    }   // end if: got four characters
+                    else if( i == 0 ){
+                        return -1;
+                    }   // end else if: also padded correctly
+                    else
+                    {
+                        // Must have broken out from above.
+                        throw new java.io.IOException( "Improperly padded Base64 input." );
+                    }   // end 
+                    
+                }   // end else: decode
+            }   // end else: get data
+            
+            // Got data?
+            if( position >= 0 )
+            {
+                // End of relevant data?
+                if( /*!encode &&*/ position >= numSigBytes )
+                    return -1;
+                
+                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
+                {
+                    lineLength = 0;
+                    return '\n';
+                }   // end if
+                else
+                {
+                    lineLength++;   // This isn't important when decoding
+                                    // but throwing an extra "if" seems
+                                    // just as wasteful.
+                    
+                    int b = buffer[ position++ ];
+
+                    if( position >= bufferLength )
+                        position = -1;
+
+                    return b & 0xFF; // This is how you "cast" a byte that's
+                                     // intended to be unsigned.
+                }   // end else
+            }   // end if: position >= 0
+            
+            // Else error
+            else
+            {   
+                // When JDK1.4 is more accepted, use an assertion here.
+                throw new java.io.IOException( "Error in Base64 code reading stream." );
+            }   // end else
+        }   // end read
+        
+        
+        /**
+         * Calls {@link #read()} repeatedly until the end of stream
+         * is reached or <var>len</var> bytes are read.
+         * Returns number of bytes read into array or -1 if
+         * end of stream is encountered.
+         *
+         * @param dest array to hold values
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @return bytes read into array or -1 if end of stream is encountered.
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public int read( byte[] dest, int off, int len ) throws java.io.IOException
+        {
+            int i;
+            int b;
+            for( i = 0; i < len; i++ )
+            {
+                b = read();
+                
+                //if( b < 0 && i == 0 )
+                //    return -1;
+                
+                if( b >= 0 )
+                    dest[off + i] = (byte)b;
+                else if( i == 0 )
+                    return -1;
+                else
+                    break; // Out of 'for' loop
+            }   // end for: each byte read
+            return i;
+        }   // end read
+        
+    }   // end inner class InputStream
+    
+    
+    
+    
+    
+    
+    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
+    
+    
+    
+    /**
+     * A {@link Base64.OutputStream} will write data to another
+     * <tt>java.io.OutputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class OutputStream extends java.io.FilterOutputStream
+    {
+        private boolean encode;
+        private int     position;
+        private byte[]  buffer;
+        private int     bufferLength;
+        private int     lineLength;
+        private boolean breakLines;
+        private byte[]  b4; // Scratch used in a few places
+        private boolean suspendEncoding;
+		private int options; // Record for later
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+        
+        /**
+         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out )
+        {   
+            this( out, ENCODE );
+        }   // end constructor
+        
+        
+        /**
+         * Constructs a {@link Base64.OutputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @param options Specified options.
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out, int options )
+        {   
+            super( out );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 3 : 4;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = 0;
+            this.lineLength   = 0;
+            this.suspendEncoding = false;
+            this.b4           = new byte[4];
+			this.options      = options;
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+        
+        
+        /**
+         * Writes the byte to the output stream after
+         * converting to/from Base64 notation.
+         * When encoding, bytes are buffered three
+         * at a time before the output stream actually
+         * gets a write() call.
+         * When decoding, bytes are buffered four
+         * at a time.
+         *
+         * @param theByte the byte to write
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public void write(int theByte) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theByte );
+                return;
+            }   // end if: supsended
+            
+            // Encode?
+            if( encode )
+            {
+                buffer[ position++ ] = (byte)theByte;
+                if( position >= bufferLength )  // Enough to encode.
+                {
+                    out.write( encode3to4( b4, buffer, bufferLength, options ) );
+
+                    lineLength += 4;
+                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
+                    {
+                        out.write( NEW_LINE );
+                        lineLength = 0;
+                    }   // end if: end of line
+
+                    position = 0;
+                }   // end if: enough to output
+            }   // end if: encoding
+
+            // Else, Decoding
+            else
+            {
+                // Meaningful Base64 character?
+                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )
+                {
+                    buffer[ position++ ] = (byte)theByte;
+                    if( position >= bufferLength )  // Enough to output.
+                    {
+                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );
+                        out.write( b4, 0, len );
+                        //out.write( Base64.decode4to3( buffer ) );
+                        position = 0;
+                    }   // end if: enough to output
+                }   // end if: meaningful base64 character
+                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )
+                {
+                    throw new java.io.IOException( "Invalid character in Base64 data." );
+                }   // end else: not white space either
+            }   // end else: decoding
+        }   // end write
+        
+        
+        
+        /**
+         * Calls {@link #write(int)} repeatedly until <var>len</var> 
+         * bytes are written.
+         *
+         * @param theBytes array from which to read bytes
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theBytes, off, len );
+                return;
+            }   // end if: supsended
+            
+            for( int i = 0; i < len; i++ )
+            {
+                write( theBytes[ off + i ] );
+            }   // end for: each byte written
+            
+        }   // end write
+        
+        
+        
+        /**
+         * Method added by PHIL. [Thanks, PHIL. -Rob]
+         * This pads the buffer without closing the stream.
+         * @throws java.io.IOException
+         */
+        public void flushBase64() throws java.io.IOException 
+        {
+            if( position > 0 )
+            {
+                if( encode )
+                {
+                    out.write( encode3to4( b4, buffer, position, options ) );
+                    position = 0;
+                }   // end if: encoding
+                else
+                {
+                    throw new java.io.IOException( "Base64 input not properly padded." );
+                }   // end else: decoding
+            }   // end if: buffer partially full
+
+        }   // end flush
+
+        
+        /** 
+         * Flushes and closes (I think, in the superclass) the stream. 
+         *
+         * @throws java.io.IOException
+         * @since 1.3
+         */
+        public void close() throws java.io.IOException
+        {
+            // 1. Ensure that pending characters are written
+            flushBase64();
+
+            // 2. Actually close the stream
+            // Base class both flushes and closes.
+            super.close();
+            
+            buffer = null;
+            out    = null;
+        }   // end close
+        
+        
+        
+        /**
+         * Suspends encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @throws java.io.IOException
+         * @since 1.5.1
+         */
+        public void suspendEncoding() throws java.io.IOException 
+        {
+            flushBase64();
+            this.suspendEncoding = true;
+        }   // end suspendEncoding
+        
+        
+        /**
+         * Resumes encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void resumeEncoding()
+        {
+            this.suspendEncoding = false;
+        }   // end resumeEncoding
+        
+        
+        
+    }   // end inner class OutputStream
+    
+    
+}   // end class Base64
diff --git a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java
new file mode 100644
index 0000000..9e5d0af
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java
@@ -0,0 +1,181 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP) Enterprise Security API
+ * (ESAPI) project. For details, please see <a
+ * href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ * 
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the LICENSE
+ * before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+/**
+ * Implementation of the Codec interface for backslash encoding used in CSS.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class CSSCodec extends Codec
+{
+	private static final Character REPLACEMENT = '\ufffd';
+
+
+    /**
+	 * {@inheritDoc}
+	 *
+     * Returns backslash encoded character.
+     *
+     * @param immune
+     */
+    public String encodeCharacter(char[] immune, Character c) {
+		// check for immune characters
+		if ( containsCharacter(c, immune ) ) {
+			return ""+c;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric(c);
+		if ( hex == null ) {
+			return ""+c;
+		}
+		
+		// return the hex and end in whitespace to terminate
+        return "\\" + hex + " ";
+    }
+
+    
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index,
+	 * or null if no decoding is possible.
+	 */
+	public Character decodeCharacter(PushbackString input)
+	{
+		input.mark();
+		Character first = input.next();
+		if (first == null || first != '\\')
+		{
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if (second == null) {
+			input.reset();
+			return null;
+		}
+
+		/* From css 2.1 spec:
+		 * http://www.w3.org/TR/CSS21/syndata.html#characters
+		 *
+		 * First, inside a string, a backslash followed by a
+		 * newline is ignored (i.e., the string is deemed not
+		 * to contain either the backslash or the newline).
+		 *
+		 * Second, it cancels the meaning of special CSS
+		 * characters. Except within CSS comments, any character
+		 * (except a hexadecimal digit, linefeed, carriage return,
+		 * or form feed) can be escaped with a backslash to
+		 * remove its special meaning. For example, "\"" is a string
+		 * consisting of one double quote. Style sheet
+		 * preprocessors must not remove these backslashes
+		 * from a style sheet since that would change the style
+		 * sheet's meaning.
+		 *
+		 * Third, backslash escapes allow authors to refer to
+		 * characters they cannot easily put in a document. In
+		 * this case, the backslash is followed by at most six
+		 * hexadecimal digits (0..9A..F), which stand for the ISO
+		 * 10646 ([ISO10646]) character with that number, which
+		 * must not be zero. (It is undefined in CSS 2.1 what
+		 * happens if a style sheet does contain a character with
+		 * Unicode codepoint zero.) If a character in the range
+		 * [0-9a-fA-F] follows the hexadecimal number, the end
+		 * of the number needs to be made clear. There are two
+		 * ways to do that:
+		 *
+		 *	1. with a space (or other white space character):
+		 *	"\26 B" ("&B"). In this case, user agents should
+		 *	treat a "CR/LF" pair (U+000D/U+000A) as a single
+		 *	white space character.
+		 *
+		 *	2. by providing exactly 6 hexadecimal digits:
+		 *	"\000026B" ("&B")
+		 *
+		 * In fact, these two methods may be combined. Only one
+		 * white space character is ignored after a hexadecimal
+		 * escape. Note that this means that a "real" space
+		 * after the escape sequence must itself either be
+		 * escaped or doubled.
+		 *
+		 * If the number is outside the range allowed by Unicode
+		 * (e.g., "\110000" is above the maximum 10FFFF allowed in
+		 * current Unicode), the UA may replace the escape with
+		 * the "replacement character" (U+FFFD). If the character
+		 * is to be displayed, the UA should show a visible
+		 * symbol, such as a "missing character" glyph (cf. 15.2,
+		 * point 5).
+		 */
+
+		switch(second)
+		{	// special whitespace cases. I assume they mean
+			// for all of these to qualify as a "new
+			// line." Otherwise there is no specification
+			// of what to do for \f
+			case '\r':
+				if(input.peek('\n'))
+					input.next();
+				// fall through
+			case '\n':
+			case '\f':
+				// bs follwed by new line replaced by nothing
+			case '\u0000':	// skip NUL for now too
+				return decodeCharacter(input);
+		}
+
+		if (!PushbackString.isHexDigit(second))
+		{	// non hex digit
+			return second;
+		}
+
+		// Search for up to 6 hex digits following until a space
+		StringBuilder sb = new StringBuilder();
+		sb.append(second);
+		for (int i = 0; i < 5; i++)
+		{
+			Character c = input.next();
+			if(c == null || Character.isWhitespace(c))
+				break;
+			if(PushbackString.isHexDigit(c))
+				sb.append(c);
+			else
+			{
+				input.pushback(c);
+				break;
+			}
+		}
+		try
+		{
+			// parse the hex digit and create a character
+			int i = Integer.parseInt(sb.toString(), 16);
+	
+			if (Character.isValidCodePoint(i))
+				return (char)i;
+			return REPLACEMENT;
+		}
+		catch (NumberFormatException e)
+		{
+			throw new IllegalStateException("Received a NumberFormateException parsing a string verified to be hex", e);
+        	}
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/Codec.java b/src/main/java/org/owasp/esapi/codecs/Codec.java
new file mode 100644
index 0000000..8e5fac8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/Codec.java
@@ -0,0 +1,159 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * The Codec interface defines a set of methods for encoding and decoding application level encoding schemes,
+ * such as HTML entity encoding and percent encoding (aka URL encoding). Codecs are used in output encoding
+ * and canonicalization.  The design of these codecs allows for character-by-character decoding, which is
+ * necessary to detect double-encoding and the use of multiple encoding schemes, both of which are techniques
+ * used by attackers to bypass validation and bury encoded attacks in data.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public abstract class Codec {
+
+	/**
+	 * Initialize an array to mark which characters are to be encoded. Store the hex
+	 * string for that character to save time later. If the character shouldn't be
+	 * encoded, then store null.
+	 */
+	private static final String[] hex = new String[256];
+
+	static {
+		for ( char c = 0; c < 0xFF; c++ ) {
+			if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ) {
+				hex[c] = null;
+			} else {
+				hex[c] = toHex(c).intern();
+			}
+		}
+	}
+
+
+	/**
+	 * Default constructor
+	 */
+	public Codec() {
+	}
+
+	/**
+	 * Encode a String so that it can be safely used in a specific context.
+	 * 
+	 * @param immune
+	 * @param input
+	 * 		the String to encode
+	 * @return the encoded String
+	 */
+	public String encode(char[] immune, String input) {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			sb.append(encodeCharacter(immune, c));
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Default implementation that should be overridden in specific codecs.
+	 * 
+	 * @param immune
+	 * @param c
+	 * 		the Character to encode
+	 * @return
+	 * 		the encoded Character
+	 */
+	public String encodeCharacter( char[] immune, Character c ) {
+		return ""+c;
+	}
+
+	/**
+	 * Decode a String that was encoded using the encode method in this Class
+	 * 
+	 * @param input
+	 * 		the String to decode
+	 * @return
+	 *		the decoded String
+	 */
+	public String decode(String input) {
+		StringBuilder sb = new StringBuilder();
+		PushbackString pbs = new PushbackString(input);
+		while (pbs.hasNext()) {
+			Character c = decodeCharacter(pbs);
+			if (c != null) {
+				sb.append(c);
+			} else {
+				sb.append(pbs.next());
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Returns the decoded version of the next character from the input string and advances the
+	 * current character in the PushbackString.  If the current character is not encoded, this 
+	 * method MUST reset the PushbackString.
+	 * 
+	 * @param input	the Character to decode
+	 * 
+	 * @return the decoded Character
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		return input.next();
+	}
+
+	/**
+	 * Lookup the hex value of any character that is not alphanumeric.
+	 * @param c The character to lookup.
+	 * @return, return null if alphanumeric or the character code
+	 * 	in hex.
+	 */
+	public static String getHexForNonAlphanumeric(char c)
+	{
+		if(c<0xFF)
+			return hex[c];
+		return toHex(c);
+	}
+
+	public static String toOctal(char c)
+	{
+		return Integer.toOctalString(c);
+	}
+
+	public static String toHex(char c)
+	{
+		return Integer.toHexString(c);
+	}
+
+	/**
+	 * Utility to search a char[] for a specific char.
+	 * 
+	 * @param c
+	 * @param array
+	 * @return
+	 */
+	public static boolean containsCharacter( char c, char[] array ) {
+		for (char ch : array) {
+			if (c == ch) return true;
+		}
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java
new file mode 100644
index 0000000..a99818c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java
@@ -0,0 +1,68 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for DB2 strings. This function will only protect you from SQLi in limited situations.
+ * 
+ * @author Sivasankar Tanakala (stanakal at TRS.NYC.NY.US)
+ * @since October 26, 2010
+ * @see org.owasp.esapi.Encoder
+ */
+public class DB2Codec extends Codec {
+
+	public String encodeCharacter(char[] immune, Character c) {
+
+		if (c.charValue() == '\'')
+			return "\'\'";
+
+		if (c.charValue() == ';')
+			return ".";
+
+		return "" + c;
+	}
+
+	public Character decodeCharacter(PushbackString input) {
+
+		input.mark();
+		Character first = input.next();
+
+		if (first == null) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+
+		if (first.charValue() != '\'') {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+
+		if (second == null) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+		if (second.charValue() != '\'') {
+			input.reset();
+			return null;
+		}
+
+		return (Character.valueOf('\''));
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java
new file mode 100644
index 0000000..36d5876
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java
@@ -0,0 +1,550 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Implementation of the Codec interface for HTML entity encoding.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class HTMLEntityCodec extends Codec
+{
+	private static final char REPLACEMENT_CHAR = '\ufffd';
+	private static final String REPLACEMENT_HEX = "fffd";
+	private static final String REPLACEMENT_STR = "" + REPLACEMENT_CHAR;
+	private static final Map<Character,String> characterToEntityMap = mkCharacterToEntityMap();
+
+	private static final Trie<Character> entityToCharacterTrie = mkEntityToCharacterTrie();
+
+    /**
+     *
+     */
+    public HTMLEntityCodec() {
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+     * Encodes a Character for safe use in an HTML entity field.
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+
+		// check for immune characters
+		if ( containsCharacter(c, immune ) ) {
+			return ""+c;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric(c);
+		if ( hex == null ) {
+			return ""+c;
+		}
+		
+		// check for illegal characters
+		if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) )
+		{
+			hex = REPLACEMENT_HEX;	// Let's entity encode this instead of returning it
+			c = REPLACEMENT_CHAR;
+		}
+		
+		// check if there's a defined entity
+		String entityName = (String) characterToEntityMap.get(c);
+		if (entityName != null) {
+			return "&" + entityName + ";";
+		}
+		
+		// return the hex entity as suggested in the spec
+		return "&#x" + hex + ";";
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal both with and without semi-colon, upper/lower case:
+	 *   &#dddd;
+	 *   &#xhhhh;
+	 *   &name;
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if (first != '&' ) {
+			input.reset();
+			return null;
+		}
+		
+		// test for numeric encodings
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		if (second == '#' ) {
+			// handle numbers
+			Character c = getNumericEntity( input );
+			if ( c != null ) return c;
+		} else if ( Character.isLetter( second.charValue() ) ) {
+			// handle entities
+			input.pushback( second );
+			Character c = getNamedEntity( input );
+			if ( c != null ) return c;
+		}
+		input.reset();
+		return null;
+	}
+	
+	/**
+	 * getNumericEntry checks input to see if it is a numeric entity
+	 * 
+	 * @param input
+	 * 			The input to test for being a numeric entity
+	 *  
+	 * @return
+	 * 			null if input is null, the character of input after decoding
+	 */
+	private Character getNumericEntity( PushbackString input ) {
+		Character first = input.peek();
+		if ( first == null ) return null;
+
+		if (first == 'x' || first == 'X' ) {
+			input.next();
+			return parseHex( input );
+		}
+		return parseNumber( input );
+	}
+
+	/**
+	 * Parse a decimal number, such as those from JavaScript's String.fromCharCode(value)
+	 * 
+	 * @param input
+	 * 			decimal encoded string, such as 65
+	 * @return
+	 * 			character representation of this decimal value, e.g. A 
+	 * @throws NumberFormatException
+	 */
+	private Character parseNumber( PushbackString input ) {
+		StringBuilder sb = new StringBuilder();
+		while( input.hasNext() ) {
+			Character c = input.peek();
+			
+			// if character is a digit then add it on and keep going
+			if ( Character.isDigit( c.charValue() ) ) {
+				sb.append( c );
+				input.next();
+				
+			// if character is a semi-colon, eat it and quit
+			} else if (c == ';' ) {
+				input.next();
+				break;
+				
+			// otherwise just quit
+			} else {
+				break;
+			}
+		}
+		try {
+			int i = Integer.parseInt(sb.toString());
+            if (Character.isValidCodePoint(i)) {
+                return (char) i;
+            }
+		} catch( NumberFormatException e ) {
+			// throw an exception for malformed entity?
+		}
+			return null;
+		}
+	
+	/**
+	 * Parse a hex encoded entity
+	 * 
+	 * @param input
+	 * 			Hex encoded input (such as 437ae;)
+	 * @return
+	 * 			A single character from the string
+	 * @throws NumberFormatException
+	 */
+	private Character parseHex( PushbackString input ) {
+		StringBuilder sb = new StringBuilder();
+		while( input.hasNext() ) {
+			Character c = input.peek();
+			
+			// if character is a hex digit then add it on and keep going
+			if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) {
+				sb.append( c );
+				input.next();
+				
+			// if character is a semi-colon, eat it and quit
+			} else if (c == ';' ) {
+				input.next();
+				break;
+				
+			// otherwise just quit
+			} else {
+				break;
+			}
+		}
+		try {
+			int i = Integer.parseInt(sb.toString(), 16);
+            if (Character.isValidCodePoint(i)) {
+                return (char) i;
+            }
+		} catch( NumberFormatException e ) {
+			// throw an exception for malformed entity?
+		}
+			return null;
+		}
+	
+	/**
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal both with and without semi-colon, upper/lower case:
+	 *   &aa;
+	 *   &aaa;
+	 *   &aaaa;
+	 *   &aaaaa;
+	 *   &aaaaaa;
+	 *   &aaaaaaa;
+	 *
+	 * @param input
+	 * 		A string containing a named entity like "
+	 * @return
+	 * 		Returns the decoded version of the character starting at index, or null if no decoding is possible.
+	 */
+	private Character getNamedEntity( PushbackString input ) {
+		StringBuilder possible = new StringBuilder();
+		Map.Entry<CharSequence,Character> entry;
+		int len;
+		
+		// kludge around PushbackString....
+		len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength());
+		for(int i=0;i<len;i++)
+			possible.append(Character.toLowerCase(input.next()));
+
+		// look up the longest match
+		entry = entityToCharacterTrie.getLongestMatch(possible);
+		if(entry == null)
+			return null;	// no match, caller will reset input
+
+		// fixup input
+		input.reset();
+		input.next();	// read &
+		len = entry.getKey().length();	// what matched's length
+		for(int i=0;i<len;i++)
+			input.next();
+
+		// check for a trailing semicolen
+		if(input.peek(';'))
+			input.next();
+
+		return entry.getValue();
+	}
+
+	/**
+	 * Build a unmodifiable Map from entity Character to Name.
+	 * @return Unmodifiable map.
+	 */
+	private static synchronized Map<Character,String> mkCharacterToEntityMap()
+	{
+		Map<Character, String> map = new HashMap<Character,String>(252);
+
+		map.put((char)34,	"quot");	/* quotation mark */
+		map.put((char)38,	"amp");		/* ampersand */
+		map.put((char)60,	"lt");		/* less-than sign */
+		map.put((char)62,	"gt");		/* greater-than sign */
+		map.put((char)160,	"nbsp");	/* no-break space */
+		map.put((char)161,	"iexcl");	/* inverted exclamation mark */
+		map.put((char)162,	"cent");	/* cent sign */
+		map.put((char)163,	"pound");	/* pound sign */
+		map.put((char)164,	"curren");	/* currency sign */
+		map.put((char)165,	"yen");		/* yen sign */
+		map.put((char)166,	"brvbar");	/* broken bar */
+		map.put((char)167,	"sect");	/* section sign */
+		map.put((char)168,	"uml");		/* diaeresis */
+		map.put((char)169,	"copy");	/* copyright sign */
+		map.put((char)170,	"ordf");	/* feminine ordinal indicator */
+		map.put((char)171,	"laquo");	/* left-pointing double angle quotation mark */
+		map.put((char)172,	"not");		/* not sign */
+		map.put((char)173,	"shy");		/* soft hyphen */
+		map.put((char)174,	"reg");		/* registered sign */
+		map.put((char)175,	"macr");	/* macron */
+		map.put((char)176,	"deg");		/* degree sign */
+		map.put((char)177,	"plusmn");	/* plus-minus sign */
+		map.put((char)178,	"sup2");	/* superscript two */
+		map.put((char)179,	"sup3");	/* superscript three */
+		map.put((char)180,	"acute");	/* acute accent */
+		map.put((char)181,	"micro");	/* micro sign */
+		map.put((char)182,	"para");	/* pilcrow sign */
+		map.put((char)183,	"middot");	/* middle dot */
+		map.put((char)184,	"cedil");	/* cedilla */
+		map.put((char)185,	"sup1");	/* superscript one */
+		map.put((char)186,	"ordm");	/* masculine ordinal indicator */
+		map.put((char)187,	"raquo");	/* right-pointing double angle quotation mark */
+		map.put((char)188,	"frac14");	/* vulgar fraction one quarter */
+		map.put((char)189,	"frac12");	/* vulgar fraction one half */
+		map.put((char)190,	"frac34");	/* vulgar fraction three quarters */
+		map.put((char)191,	"iquest");	/* inverted question mark */
+		map.put((char)192,	"Agrave");	/* Latin capital letter a with grave */
+		map.put((char)193,	"Aacute");	/* Latin capital letter a with acute */
+		map.put((char)194,	"Acirc");	/* Latin capital letter a with circumflex */
+		map.put((char)195,	"Atilde");	/* Latin capital letter a with tilde */
+		map.put((char)196,	"Auml");	/* Latin capital letter a with diaeresis */
+		map.put((char)197,	"Aring");	/* Latin capital letter a with ring above */
+		map.put((char)198,	"AElig");	/* Latin capital letter ae */
+		map.put((char)199,	"Ccedil");	/* Latin capital letter c with cedilla */
+		map.put((char)200,	"Egrave");	/* Latin capital letter e with grave */
+		map.put((char)201,	"Eacute");	/* Latin capital letter e with acute */
+		map.put((char)202,	"Ecirc");	/* Latin capital letter e with circumflex */
+		map.put((char)203,	"Euml");	/* Latin capital letter e with diaeresis */
+		map.put((char)204,	"Igrave");	/* Latin capital letter i with grave */
+		map.put((char)205,	"Iacute");	/* Latin capital letter i with acute */
+		map.put((char)206,	"Icirc");	/* Latin capital letter i with circumflex */
+		map.put((char)207,	"Iuml");	/* Latin capital letter i with diaeresis */
+		map.put((char)208,	"ETH");		/* Latin capital letter eth */
+		map.put((char)209,	"Ntilde");	/* Latin capital letter n with tilde */
+		map.put((char)210,	"Ograve");	/* Latin capital letter o with grave */
+		map.put((char)211,	"Oacute");	/* Latin capital letter o with acute */
+		map.put((char)212,	"Ocirc");	/* Latin capital letter o with circumflex */
+		map.put((char)213,	"Otilde");	/* Latin capital letter o with tilde */
+		map.put((char)214,	"Ouml");	/* Latin capital letter o with diaeresis */
+		map.put((char)215,	"times");	/* multiplication sign */
+		map.put((char)216,	"Oslash");	/* Latin capital letter o with stroke */
+		map.put((char)217,	"Ugrave");	/* Latin capital letter u with grave */
+		map.put((char)218,	"Uacute");	/* Latin capital letter u with acute */
+		map.put((char)219,	"Ucirc");	/* Latin capital letter u with circumflex */
+		map.put((char)220,	"Uuml");	/* Latin capital letter u with diaeresis */
+		map.put((char)221,	"Yacute");	/* Latin capital letter y with acute */
+		map.put((char)222,	"THORN");	/* Latin capital letter thorn */
+		map.put((char)223,	"szlig");	/* Latin small letter sharp sXCOMMAX German Eszett */
+		map.put((char)224,	"agrave");	/* Latin small letter a with grave */
+		map.put((char)225,	"aacute");	/* Latin small letter a with acute */
+		map.put((char)226,	"acirc");	/* Latin small letter a with circumflex */
+		map.put((char)227,	"atilde");	/* Latin small letter a with tilde */
+		map.put((char)228,	"auml");	/* Latin small letter a with diaeresis */
+		map.put((char)229,	"aring");	/* Latin small letter a with ring above */
+		map.put((char)230,	"aelig");	/* Latin lowercase ligature ae */
+		map.put((char)231,	"ccedil");	/* Latin small letter c with cedilla */
+		map.put((char)232,	"egrave");	/* Latin small letter e with grave */
+		map.put((char)233,	"eacute");	/* Latin small letter e with acute */
+		map.put((char)234,	"ecirc");	/* Latin small letter e with circumflex */
+		map.put((char)235,	"euml");	/* Latin small letter e with diaeresis */
+		map.put((char)236,	"igrave");	/* Latin small letter i with grave */
+		map.put((char)237,	"iacute");	/* Latin small letter i with acute */
+		map.put((char)238,	"icirc");	/* Latin small letter i with circumflex */
+		map.put((char)239,	"iuml");	/* Latin small letter i with diaeresis */
+		map.put((char)240,	"eth");		/* Latin small letter eth */
+		map.put((char)241,	"ntilde");	/* Latin small letter n with tilde */
+		map.put((char)242,	"ograve");	/* Latin small letter o with grave */
+		map.put((char)243,	"oacute");	/* Latin small letter o with acute */
+		map.put((char)244,	"ocirc");	/* Latin small letter o with circumflex */
+		map.put((char)245,	"otilde");	/* Latin small letter o with tilde */
+		map.put((char)246,	"ouml");	/* Latin small letter o with diaeresis */
+		map.put((char)247,	"divide");	/* division sign */
+		map.put((char)248,	"oslash");	/* Latin small letter o with stroke */
+		map.put((char)249,	"ugrave");	/* Latin small letter u with grave */
+		map.put((char)250,	"uacute");	/* Latin small letter u with acute */
+		map.put((char)251,	"ucirc");	/* Latin small letter u with circumflex */
+		map.put((char)252,	"uuml");	/* Latin small letter u with diaeresis */
+		map.put((char)253,	"yacute");	/* Latin small letter y with acute */
+		map.put((char)254,	"thorn");	/* Latin small letter thorn */
+		map.put((char)255,	"yuml");	/* Latin small letter y with diaeresis */
+		map.put((char)338,	"OElig");	/* Latin capital ligature oe */
+		map.put((char)339,	"oelig");	/* Latin small ligature oe */
+		map.put((char)352,	"Scaron");	/* Latin capital letter s with caron */
+		map.put((char)353,	"scaron");	/* Latin small letter s with caron */
+		map.put((char)376,	"Yuml");	/* Latin capital letter y with diaeresis */
+		map.put((char)402,	"fnof");	/* Latin small letter f with hook */
+		map.put((char)710,	"circ");	/* modifier letter circumflex accent */
+		map.put((char)732,	"tilde");	/* small tilde */
+		map.put((char)913,	"Alpha");	/* Greek capital letter alpha */
+		map.put((char)914,	"Beta");	/* Greek capital letter beta */
+		map.put((char)915,	"Gamma");	/* Greek capital letter gamma */
+		map.put((char)916,	"Delta");	/* Greek capital letter delta */
+		map.put((char)917,	"Epsilon");	/* Greek capital letter epsilon */
+		map.put((char)918,	"Zeta");	/* Greek capital letter zeta */
+		map.put((char)919,	"Eta");		/* Greek capital letter eta */
+		map.put((char)920,	"Theta");	/* Greek capital letter theta */
+		map.put((char)921,	"Iota");	/* Greek capital letter iota */
+		map.put((char)922,	"Kappa");	/* Greek capital letter kappa */
+		map.put((char)923,	"Lambda");	/* Greek capital letter lambda */
+		map.put((char)924,	"Mu");		/* Greek capital letter mu */
+		map.put((char)925,	"Nu");		/* Greek capital letter nu */
+		map.put((char)926,	"Xi");		/* Greek capital letter xi */
+		map.put((char)927,	"Omicron");	/* Greek capital letter omicron */
+		map.put((char)928,	"Pi");		/* Greek capital letter pi */
+		map.put((char)929,	"Rho");		/* Greek capital letter rho */
+		map.put((char)931,	"Sigma");	/* Greek capital letter sigma */
+		map.put((char)932,	"Tau");		/* Greek capital letter tau */
+		map.put((char)933,	"Upsilon");	/* Greek capital letter upsilon */
+		map.put((char)934,	"Phi");		/* Greek capital letter phi */
+		map.put((char)935,	"Chi");		/* Greek capital letter chi */
+		map.put((char)936,	"Psi");		/* Greek capital letter psi */
+		map.put((char)937,	"Omega");	/* Greek capital letter omega */
+		map.put((char)945,	"alpha");	/* Greek small letter alpha */
+		map.put((char)946,	"beta");	/* Greek small letter beta */
+		map.put((char)947,	"gamma");	/* Greek small letter gamma */
+		map.put((char)948,	"delta");	/* Greek small letter delta */
+		map.put((char)949,	"epsilon");	/* Greek small letter epsilon */
+		map.put((char)950,	"zeta");	/* Greek small letter zeta */
+		map.put((char)951,	"eta");		/* Greek small letter eta */
+		map.put((char)952,	"theta");	/* Greek small letter theta */
+		map.put((char)953,	"iota");	/* Greek small letter iota */
+		map.put((char)954,	"kappa");	/* Greek small letter kappa */
+		map.put((char)955,	"lambda");	/* Greek small letter lambda */
+		map.put((char)956,	"mu");		/* Greek small letter mu */
+		map.put((char)957,	"nu");		/* Greek small letter nu */
+		map.put((char)958,	"xi");		/* Greek small letter xi */
+		map.put((char)959,	"omicron");	/* Greek small letter omicron */
+		map.put((char)960,	"pi");		/* Greek small letter pi */
+		map.put((char)961,	"rho");		/* Greek small letter rho */
+		map.put((char)962,	"sigmaf");	/* Greek small letter final sigma */
+		map.put((char)963,	"sigma");	/* Greek small letter sigma */
+		map.put((char)964,	"tau");		/* Greek small letter tau */
+		map.put((char)965,	"upsilon");	/* Greek small letter upsilon */
+		map.put((char)966,	"phi");		/* Greek small letter phi */
+		map.put((char)967,	"chi");		/* Greek small letter chi */
+		map.put((char)968,	"psi");		/* Greek small letter psi */
+		map.put((char)969,	"omega");	/* Greek small letter omega */
+		map.put((char)977,	"thetasym");	/* Greek theta symbol */
+		map.put((char)978,	"upsih");	/* Greek upsilon with hook symbol */
+		map.put((char)982,	"piv");		/* Greek pi symbol */
+		map.put((char)8194,	"ensp");	/* en space */
+		map.put((char)8195,	"emsp");	/* em space */
+		map.put((char)8201,	"thinsp");	/* thin space */
+		map.put((char)8204,	"zwnj");	/* zero width non-joiner */
+		map.put((char)8205,	"zwj");		/* zero width joiner */
+		map.put((char)8206,	"lrm");		/* left-to-right mark */
+		map.put((char)8207,	"rlm");		/* right-to-left mark */
+		map.put((char)8211,	"ndash");	/* en dash */
+		map.put((char)8212,	"mdash");	/* em dash */
+		map.put((char)8216,	"lsquo");	/* left single quotation mark */
+		map.put((char)8217,	"rsquo");	/* right single quotation mark */
+		map.put((char)8218,	"sbquo");	/* single low-9 quotation mark */
+		map.put((char)8220,	"ldquo");	/* left double quotation mark */
+		map.put((char)8221,	"rdquo");	/* right double quotation mark */
+		map.put((char)8222,	"bdquo");	/* double low-9 quotation mark */
+		map.put((char)8224,	"dagger");	/* dagger */
+		map.put((char)8225,	"Dagger");	/* double dagger */
+		map.put((char)8226,	"bull");	/* bullet */
+		map.put((char)8230,	"hellip");	/* horizontal ellipsis */
+		map.put((char)8240,	"permil");	/* per mille sign */
+		map.put((char)8242,	"prime");	/* prime */
+		map.put((char)8243,	"Prime");	/* double prime */
+		map.put((char)8249,	"lsaquo");	/* single left-pointing angle quotation mark */
+		map.put((char)8250,	"rsaquo");	/* single right-pointing angle quotation mark */
+		map.put((char)8254,	"oline");	/* overline */
+		map.put((char)8260,	"frasl");	/* fraction slash */
+		map.put((char)8364,	"euro");	/* euro sign */
+		map.put((char)8465,	"image");	/* black-letter capital i */
+		map.put((char)8472,	"weierp");	/* script capital pXCOMMAX Weierstrass p */
+		map.put((char)8476,	"real");	/* black-letter capital r */
+		map.put((char)8482,	"trade");	/* trademark sign */
+		map.put((char)8501,	"alefsym");	/* alef symbol */
+		map.put((char)8592,	"larr");	/* leftwards arrow */
+		map.put((char)8593,	"uarr");	/* upwards arrow */
+		map.put((char)8594,	"rarr");	/* rightwards arrow */
+		map.put((char)8595,	"darr");	/* downwards arrow */
+		map.put((char)8596,	"harr");	/* left right arrow */
+		map.put((char)8629,	"crarr");	/* downwards arrow with corner leftwards */
+		map.put((char)8656,	"lArr");	/* leftwards double arrow */
+		map.put((char)8657,	"uArr");	/* upwards double arrow */
+		map.put((char)8658,	"rArr");	/* rightwards double arrow */
+		map.put((char)8659,	"dArr");	/* downwards double arrow */
+		map.put((char)8660,	"hArr");	/* left right double arrow */
+		map.put((char)8704,	"forall");	/* for all */
+		map.put((char)8706,	"part");	/* partial differential */
+		map.put((char)8707,	"exist");	/* there exists */
+		map.put((char)8709,	"empty");	/* empty set */
+		map.put((char)8711,	"nabla");	/* nabla */
+		map.put((char)8712,	"isin");	/* element of */
+		map.put((char)8713,	"notin");	/* not an element of */
+		map.put((char)8715,	"ni");		/* contains as member */
+		map.put((char)8719,	"prod");	/* n-ary product */
+		map.put((char)8721,	"sum");		/* n-ary summation */
+		map.put((char)8722,	"minus");	/* minus sign */
+		map.put((char)8727,	"lowast");	/* asterisk operator */
+		map.put((char)8730,	"radic");	/* square root */
+		map.put((char)8733,	"prop");	/* proportional to */
+		map.put((char)8734,	"infin");	/* infinity */
+		map.put((char)8736,	"ang");		/* angle */
+		map.put((char)8743,	"and");		/* logical and */
+		map.put((char)8744,	"or");		/* logical or */
+		map.put((char)8745,	"cap");		/* intersection */
+		map.put((char)8746,	"cup");		/* union */
+		map.put((char)8747,	"int");		/* integral */
+		map.put((char)8756,	"there4");	/* therefore */
+		map.put((char)8764,	"sim");		/* tilde operator */
+		map.put((char)8773,	"cong");	/* congruent to */
+		map.put((char)8776,	"asymp");	/* almost equal to */
+		map.put((char)8800,	"ne");		/* not equal to */
+		map.put((char)8801,	"equiv");	/* identical toXCOMMAX equivalent to */
+		map.put((char)8804,	"le");		/* less-than or equal to */
+		map.put((char)8805,	"ge");		/* greater-than or equal to */
+		map.put((char)8834,	"sub");		/* subset of */
+		map.put((char)8835,	"sup");		/* superset of */
+		map.put((char)8836,	"nsub");	/* not a subset of */
+		map.put((char)8838,	"sube");	/* subset of or equal to */
+		map.put((char)8839,	"supe");	/* superset of or equal to */
+		map.put((char)8853,	"oplus");	/* circled plus */
+		map.put((char)8855,	"otimes");	/* circled times */
+		map.put((char)8869,	"perp");	/* up tack */
+		map.put((char)8901,	"sdot");	/* dot operator */
+		map.put((char)8968,	"lceil");	/* left ceiling */
+		map.put((char)8969,	"rceil");	/* right ceiling */
+		map.put((char)8970,	"lfloor");	/* left floor */
+		map.put((char)8971,	"rfloor");	/* right floor */
+		map.put((char)9001,	"lang");	/* left-pointing angle bracket */
+		map.put((char)9002,	"rang");	/* right-pointing angle bracket */
+		map.put((char)9674,	"loz");		/* lozenge */
+		map.put((char)9824,	"spades");	/* black spade suit */
+		map.put((char)9827,	"clubs");	/* black club suit */
+		map.put((char)9829,	"hearts");	/* black heart suit */
+		map.put((char)9830,	"diams");	/* black diamond suit */
+
+		return Collections.unmodifiableMap(map);
+	}
+
+	/**
+	 * Build a unmodifiable Trie from entitiy Name to Character
+	 * @return Unmodifiable trie.
+	 */
+	private static synchronized Trie<Character> mkEntityToCharacterTrie()
+	{
+		Trie<Character> trie = new HashTrie<Character>();
+
+		for(Map.Entry<Character,String> entry : characterToEntityMap.entrySet())
+			trie.put(entry.getValue(),entry.getKey());
+		return Trie.Util.unmodifiable(trie);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/HashTrie.java b/src/main/java/org/owasp/esapi/codecs/HashTrie.java
new file mode 100644
index 0000000..ba515d3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/HashTrie.java
@@ -0,0 +1,612 @@
+package org.owasp.esapi.codecs;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.owasp.esapi.util.NullSafe;
+
+/**
+ * Trie implementation for CharSequence keys. This uses HashMaps for each
+ * level instead of the traditional array. This is done as with unicode,
+ * each level's array would be 64k entries.
+ *
+ * <b>NOTE:</b><br>
+ * <ul>
+ *	<li>{@link Map.remove(Object)} is not supported.</li>
+ *	<li>
+ *		If deletion support is added the max key length will
+ *		need work or removal.
+ *	</li>
+ *	<li>Null values are not supported.</li>
+ * </ul>
+ *
+ * @author Ed Schaller
+ */
+public class HashTrie<T> implements Trie<T>
+{
+	private static class Entry<T> implements Map.Entry<CharSequence,T>
+	{
+		private CharSequence key;
+		private T value;
+
+		Entry(CharSequence key, T value)
+		{
+			this.key = key;
+			this.value = value;
+		}
+
+		/**
+		 * Convinence instantiator.
+		 * @param key The key for the new instance
+		 * @param keyLength The length of the key to use
+		 * @param value The value for the new instance
+		 * @return null if key or value is null
+		 *	new Entry(key,value) if {@link CharSequence#length()} == keyLength
+		 *	new Entry(key.subSequence(0,keyLength),value) otherwise
+		 */
+		static <T> Entry<T> newInstanceIfNeeded(CharSequence key, int keyLength, T value)
+		{
+			if(value == null || key == null)
+				return null;
+			if(key.length() > keyLength)
+				key = key.subSequence(0,keyLength);
+			return new Entry<T>(key,value);
+		}
+
+		/**
+		 * Convinence instantiator.
+		 * @param key The key for the new instance
+		 * @param value The value for the new instance
+		 * @return null if key or value is null
+		 *	new Entry(key,value) otherwise
+		 */
+		static <T> Entry<T> newInstanceIfNeeded(CharSequence key, T value)
+		{
+			if(value == null || key == null)
+				return null;
+			return new Entry<T>(key,value);
+		}
+
+                /*************/
+                /* Map.Entry */
+                /*************/
+
+		public CharSequence getKey()
+		{
+			return key;
+		}
+
+		public T getValue()
+		{
+			return value;
+		}
+
+		public T setValue(T value)
+		{
+			throw new UnsupportedOperationException();
+		}
+
+                /********************/
+                /* java.lang.Object */
+                /********************/
+
+		public boolean equals(Map.Entry other)
+		{
+			return (NullSafe.equals(key, other.getKey()) && NullSafe.equals(value, other.getValue()));
+		}
+
+		@Override
+		public boolean equals(Object o)
+		{
+			if(o instanceof Map.Entry)
+				return equals((Map.Entry)o);
+			return false;
+		}
+
+		@Override
+		public int hashCode()
+		{
+			return NullSafe.hashCode(key) ^ NullSafe.hashCode(value);
+		}
+
+		@Override
+		public String toString()
+		{
+			return NullSafe.toString(key) + " => " + NullSafe.toString(value);
+		}
+	}
+
+	/**
+	 * Node inside the trie.
+	 */
+	private static class Node<T>
+	{
+		private T value = null;
+		private Map<Character,Node<T>> nextMap;
+
+		/**
+		 * Create a new Map for a node level. This is here so
+		 * that if the underlying * Map implmentation needs to
+		 * be switched it is easily done.
+		 * @return A new Map for use.
+		 */
+		private static <T> Map<Character,Node<T>> newNodeMap()
+		{
+			return new HashMap<Character,Node<T>>();
+		}
+
+		/**
+		 * Create a new Map for a node level. This is here so
+		 * that if the underlying * Map implmentation needs to
+		 * be switched it is easily done.
+		 * @param prev Pervious map to use to populate the
+		 * new map.
+		 * @return A new Map for use.
+		 */
+		private static <T> Map<Character,Node<T>> newNodeMap(Map<Character,Node<T>> prev)
+		{
+			return new HashMap<Character,Node<T>>(prev);
+		}
+
+		/** 
+		 * Set the value for the key terminated at this node.
+		 * @param value The value for this key.
+		 */
+		void setValue(T value)
+		{
+			this.value = value;
+		}
+
+		/**
+		 * Get the node for the specified character.
+		 * @param ch The next character to look for.
+		 * @return The node requested or null if it is not
+		 *	present.
+		 */
+		Node<T> getNextNode(Character ch)
+		{
+			if(nextMap == null)
+				return null;
+			return nextMap.get(ch);
+		}
+
+		/**
+		 * Recursively add a key.
+		 * @param key The key being added.
+		 * @param pos The position in key that is being handled
+		 *	at this level.
+		 */
+		T put(CharSequence key, int pos, T addValue)
+		{
+			Node<T> nextNode;
+			Character ch;
+			T old;
+
+			if(key.length() == pos)
+			{	// at terminating node
+				old = value;
+				setValue(addValue);
+				return old;
+			}
+			ch = key.charAt(pos);
+			if(nextMap == null)
+			{
+				nextMap = newNodeMap();
+				nextNode = new Node();
+				nextMap.put(ch, nextNode);
+			}
+			else if((nextNode = nextMap.get(ch))==null)
+			{
+				nextNode = new Node();
+				nextMap.put(ch,nextNode);
+			}
+			return nextNode.put(key,pos+1,addValue);
+		}
+
+		/**
+		 * Recursively lookup a key's value.
+		 * @param key The key being looked up.
+		 * @param pos The position in the key that is being
+		 *	looked up at this level.
+		 * @return The value assocatied with the key or null if
+		 *	none exists.
+		 */
+		T get(CharSequence key, int pos)
+		{
+			Node<T> nextNode;
+
+			if(key.length() <= pos)	// <= instead of == just in case
+				return value;	// no value is null which is also not found
+			if((nextNode = getNextNode(key.charAt(pos)))==null)
+				return null;
+			return nextNode.get(key,pos+1);
+		}
+			
+		/**
+		 * Recursively lookup the longest key match.
+		 * @param key The key being looked up.
+		 * @param pos The position in the key that is being
+		 *	looked up at this level.
+		 * @return The Entry assocatied with the longest key
+		 *	match or null if none exists.
+		 */
+		Entry<T> getLongestMatch(CharSequence key, int pos)
+		{
+			Node<T> nextNode;
+			Entry<T> ret;
+
+			if(key.length() <= pos)	// <= instead of == just in case
+				return Entry.newInstanceIfNeeded(key,value);
+			if((nextNode = getNextNode(key.charAt(pos)))==null)
+			{	// last in trie... return ourselves
+				return Entry.newInstanceIfNeeded(key,pos,value);
+			}
+			if((ret = nextNode.getLongestMatch(key, pos+1))!=null)
+				return ret;
+			return Entry.newInstanceIfNeeded(key,pos,value);
+		}
+
+		/**
+		 * Recursively lookup the longest key match.
+		 * @param keyIn Where to read the key from
+		 * @param pos The position in the key that is being
+		 *	looked up at this level.
+		 * @return The Entry assocatied with the longest key
+		 *	match or null if none exists.
+		 */
+		Entry<T> getLongestMatch(PushbackReader keyIn, StringBuilder key) throws IOException
+		{
+			Node<T> nextNode;
+			Entry<T> ret;
+			int c;
+			char ch;
+			int prevLen;
+
+			// read next key char and append to key...
+			if((c = keyIn.read())<0)
+				// end of input, return what we have currently
+				return Entry.newInstanceIfNeeded(key,value);
+			ch = (char)c;
+			prevLen = key.length();
+			key.append(ch);
+
+			if((nextNode = getNextNode(ch))==null)
+			{	// last in trie... return ourselves
+				return Entry.newInstanceIfNeeded(key,value);
+			}
+			if((ret = nextNode.getLongestMatch(keyIn, key))!=null)
+				return ret;
+
+			// undo reading of key char and appending to key...
+			key.setLength(prevLen);
+			keyIn.unread(c);
+
+			return Entry.newInstanceIfNeeded(key,value);
+		}
+
+		/**
+		 * Recursively rebuild the internal maps.
+		 */
+		void remap()
+		{
+			if(nextMap == null)
+				return;
+			nextMap = newNodeMap(nextMap);
+			for(Node<T> node : nextMap.values())
+				node.remap();
+		}
+
+		/**
+		 * Recursively search for a value.
+		 * @param toFind The value to search for
+		 * @return true if the value was found
+		 *	false otherwise
+		 */
+		boolean containsValue(Object toFind)
+		{
+			if(value != null && toFind.equals(value))
+				return true;
+			if(nextMap == null)
+				return false;
+			for(Node<T> node : nextMap.values())
+				if(node.containsValue(toFind))
+					return true;
+			return false;
+		}
+
+		/**
+		 * Recursively build values.
+		 * @param values List being built.
+		 * @return true if the value was found
+		 *	false otherwise
+		 */
+		Collection<T> values(Collection<T> values)
+		{
+			if(value != null)
+				values.add(value);
+			if(nextMap == null)
+				return values;
+			for(Node<T> node : nextMap.values())
+				node.values(values);
+			return values;
+		}
+
+		/**
+		 * Recursively build a key set.
+		 * @param key StringBuilder with our key.
+		 * @param keys Set to add to
+		 * @return keys with additions
+		 */
+		Set<CharSequence> keySet(StringBuilder key, Set<CharSequence> keys)
+		{
+			int len = key.length();
+
+			if(value != null)
+				// MUST toString here
+				keys.add(key.toString());
+			if(nextMap != null && nextMap.size() > 0)
+			{
+				key.append('X');
+				for(Map.Entry<Character,Node<T>> entry : nextMap.entrySet())
+				{
+					key.setCharAt(len,entry.getKey());
+					entry.getValue().keySet(key,keys);
+				}
+				key.setLength(len);
+			}
+			return keys;
+		}
+
+		/**
+		 * Recursively build a entry set.
+		 * @param key StringBuilder with our key.
+		 * @param entries Set to add to
+		 * @return entries with additions
+		 */
+		Set<Map.Entry<CharSequence,T>> entrySet(StringBuilder key, Set<Map.Entry<CharSequence,T>> entries)
+		{
+			int len = key.length();
+
+			if(value != null)
+				// MUST toString here
+				entries.add(new Entry(key.toString(),value));
+			if(nextMap != null && nextMap.size() > 0)
+			{
+				key.append('X');
+				for(Map.Entry<Character,Node<T>> entry : nextMap.entrySet())
+				{
+					key.setCharAt(len,entry.getKey());
+					entry.getValue().entrySet(key,entries);
+				}
+				key.setLength(len);
+			}
+			return entries;
+		}
+	}
+
+	private Node<T> root;
+	private int maxKeyLen;
+	private int size;
+
+	public HashTrie()
+	{
+		clear();
+	}
+
+	/**
+	 * Get the key value entry who's key is the longest prefix match.
+	 * @param key The key to lookup
+	 * @return Entry with the longest matching key.
+	 */
+	public Map.Entry<CharSequence,T> getLongestMatch(CharSequence key)
+	{
+		if(root == null || key == null)
+			return null;
+		return root.getLongestMatch(key, 0);
+	}
+
+	/**
+	 * Get the key value entry who's key is the longest prefix match.
+	 * @param keyIn Pushback reader to read the key from. This should
+	 * have a buffer at least as large as {@link #getMaxKeyLength()}
+	 * or an IOException may be thrown backing up.
+	 * @return Entry with the longest matching key.
+	 * @throws IOException if keyIn.read() or keyIn.unread() does.
+	 */
+	public Map.Entry<CharSequence,T> getLongestMatch(PushbackReader keyIn) throws IOException
+	{
+		if(root == null || keyIn == null)
+			return null;
+		return root.getLongestMatch(keyIn, new StringBuilder());
+	}
+
+	/**
+	 * Get the maximum key length.
+	 * @return max key length.
+	 */
+	public int getMaxKeyLength()
+	{
+		return maxKeyLen;
+	}
+
+        /*****************/
+        /* java.util.Map */
+        /*****************/
+
+	/**
+	 * Clear all entries.
+	 */
+	public void clear()
+	{
+		root = null;
+		maxKeyLen = -1;
+		size = 0;
+	}
+
+	/** {@inheritDoc} */
+	public boolean containsKey(Object key)
+	{
+		return (get(key) != null);
+	}
+
+	/** {@inheritDoc} */
+	public boolean containsValue(Object value)
+	{
+		if(root == null)
+			return false;
+		return root.containsValue(value);
+	}
+
+	/**
+	 * Add mapping.
+	 * @param key The mapping's key.
+	 * @value value The mapping's value
+	 * @throws NullPointerException if key or value is null.
+	 */
+	public T put(CharSequence key, T value) throws NullPointerException
+	{
+		int len;
+		T old;
+
+		if(key == null)
+			throw new NullPointerException("Null keys are not handled");
+		if(value == null)
+			throw new NullPointerException("Null values are not handled");
+		if(root == null)
+			root = new Node<T>();
+		if((old = root.put(key,0,value))!=null)
+			return old;
+
+		// after in case of replacement
+		if((len = key.length()) > maxKeyLen)
+			maxKeyLen = len;
+		size++;
+		return null;
+	}
+
+	/**
+	 * Remove a entry.
+	 * @return previous value
+	 * @throws UnsupportedOperationException always.
+	 */
+	public T remove(Object key) throws UnsupportedOperationException
+	{
+		throw new UnsupportedOperationException();
+	}
+
+	/** {@inheritDoc} */
+	public void putAll(Map<? extends CharSequence, ? extends T> map)
+	{
+		for(Map.Entry<? extends CharSequence, ? extends T> entry : map.entrySet())
+			put(entry.getKey(),entry.getValue());
+	}
+
+	/** {@inheritDoc} */
+	public Set<CharSequence> keySet()
+	{
+		Set<CharSequence> keys = new HashSet<CharSequence>(size);
+		
+		if(root == null)
+			return keys;
+		return root.keySet(new StringBuilder(), keys);
+	}
+
+	/** {@inheritDoc} */
+	public Collection<T> values()
+	{
+		ArrayList<T> values = new ArrayList<T>(size());
+
+		if(root == null)
+			return values;
+		return root.values(values);
+	}
+
+	/** {@inheritDoc} */
+	public Set<Map.Entry<CharSequence,T>> entrySet()
+	{
+		Set<Map.Entry<CharSequence,T>> entries = new HashSet<Map.Entry<CharSequence,T>>(size());
+
+		if(root == null)
+			return entries;
+		return root.entrySet(new StringBuilder(), entries);
+	}
+
+	/**
+	 * Get the value for a key.
+	 * @param key The key to look up.
+	 * @return The value for key or null if the key is not found.
+	 */
+	public T get(Object key)
+	{
+		if(root == null || key == null)
+			return null;
+		if(!(key instanceof CharSequence))
+			return null;
+		return root.get((CharSequence)key,0);
+	}
+
+	/**
+	 * Get the number of entries.
+	 * @return the number or entries.
+	 */
+	public int size()
+	{
+		return size;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean equals(Object other)
+	{
+		if(other == null)
+			return false;
+		if(!(other instanceof Map))
+			return false;
+		// per spec
+		return entrySet().equals(((Map)other).entrySet());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public int hashCode()
+	{
+		// per spec
+		return entrySet().hashCode();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public String toString()
+	{
+		StringBuilder sb;
+		boolean first;
+
+		if(isEmpty())
+			return "{}";
+		sb = new StringBuilder();
+		first = true;
+		sb.append("{ ");
+		for(Map.Entry<CharSequence,T> entry : entrySet())
+		{
+			if(first)
+				first = false;
+			else
+				sb.append(", ");
+			sb.append(entry.toString());
+		}
+		sb.append(" }");
+		return sb.toString();
+	}
+
+	/** {@inheritDoc} */
+	public boolean isEmpty()
+	{
+		return(size() == 0);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/Hex.java b/src/main/java/org/owasp/esapi/codecs/Hex.java
new file mode 100644
index 0000000..17ab4bf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/Hex.java
@@ -0,0 +1,84 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.codecs;
+
+/** Encode and decode to/from hexadecimal strings to byte arrays. */
+public class Hex {
+
+    /** Output byte representation as hexadecimal representation.
+     * 
+     * @param b				Bytes to encode to hexadecimal representation.
+     * @param leading0x		If true, return with leading "0x".
+     * @return				Hexadecimal representation of specified bytes.
+     */
+    public static String toHex(byte[] b, boolean leading0x) {
+        StringBuffer hexString = new StringBuffer();
+        if ( leading0x ) {
+            hexString.append("0x");
+        }
+        for ( int i = 0; i < b.length; i++ ) {
+            int j = b[i] & 0xff;        // Convert byte to int.
+            String hex = Integer.toHexString(j);
+            if (hex.length() == 1) {
+                hexString.append('0');  // Add leading zero.
+            }
+            hexString.append(hex);      // Hex digit(s).
+        }
+        return hexString.toString();
+    }
+
+    /**
+     * Output byte representation as hexadecimal representation.
+     * Alias for <code>toHex()</code> method.
+     * 
+     * @param b				Bytes to encode to hexadecimal representation.
+     * @param leading0x		If true, return with leading "0x".
+     * @return				Hexadecimal representation of specified bytes.
+     */
+    public static String encode(byte[] b, boolean leading0x) {
+        return toHex(b, leading0x);
+    }
+
+    /**
+     * Decode hexadecimal-encoded string and return raw byte array.
+     * Important note: This method preserves leading 0 filled bytes on the
+     * conversion process, which is important for cryptographic operations
+     * in dealing with things like keys, initialization vectors, etc. For
+     * example, the string "0x0000face" is going to return a byte array
+     * whose length is 4, not 2.
+     * 
+     * @param hexStr	Hexadecimal-encoded string, with or without leading "0x".
+     * @return			The equivalent byte array.
+     */
+    public static byte[] fromHex(String hexStr) {
+        String hexRep = hexStr;
+        if ( hexStr.startsWith("0x") ) {
+            // Then skip over the leading 0x.
+            hexRep = hexStr.substring(2);
+        }
+        int len = hexRep.length() / 2;
+        byte[] rawBytes = new byte[len];
+        for (int i = 0; i < len; i++) {
+            String substr = hexRep.substring(i * 2, (i * 2) + 2);
+            rawBytes[i] = (byte)(Integer.parseInt(substr, 16));
+        }
+        return rawBytes;
+    }
+
+    /** Decode hexadecimal-encoded string and return raw byte array.
+     * Alias for <code>fromHex()</code> method.
+     * 
+     * @param hexStr	Hexadecimal-encoded string, with or without leading "0x".
+     * @return			The equivalent byte array. 
+     */
+    public static byte[] decode(String hexStr) {
+        return fromHex(hexStr);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java
new file mode 100644
index 0000000..6d128e4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java
@@ -0,0 +1,217 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for backslash encoding in JavaScript.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class JavaScriptCodec extends Codec {
+
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns backslash encoded numeric format. Does not use backslash character escapes
+	 * such as, \" or \' as these may cause parsing problems. For example, if a javascript
+	 * attribute, such as onmouseover, contains a \" that will close the entire attribute and
+	 * allow an attacker to inject another script attribute.
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+
+		// check for immune characters
+		if ( containsCharacter(c, immune ) ) {
+			return ""+c;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric(c);
+		if ( hex == null ) {
+			return ""+c;
+		}
+				
+		// Do not use these shortcuts as they can be used to break out of a context
+		// if ( ch == 0x00 ) return "\\0";
+		// if ( ch == 0x08 ) return "\\b";
+		// if ( ch == 0x09 ) return "\\t";
+		// if ( ch == 0x0a ) return "\\n";
+		// if ( ch == 0x0b ) return "\\v";
+		// if ( ch == 0x0c ) return "\\f";
+		// if ( ch == 0x0d ) return "\\r";
+		// if ( ch == 0x22 ) return "\\\"";
+		// if ( ch == 0x27 ) return "\\'";
+		// if ( ch == 0x5c ) return "\\\\";
+
+		// encode up to 256 with \\xHH
+        String temp = Integer.toHexString(c);
+		if ( c < 256 ) {
+	        String pad = "00".substring(temp.length() );
+	        return "\\x" + pad + temp.toUpperCase();
+		}
+
+		// otherwise encode with \\uHHHH
+        String pad = "0000".substring(temp.length() );
+        return "\\u" + pad + temp.toUpperCase();
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * See http://www.planetpdf.com/codecuts/pdfs/tutorial/jsspec.pdf 
+	 * Formats all are legal both upper/lower case:
+	 *   \\a - special characters
+	 *   \\xHH
+	 *   \\uHHHH
+	 *   \\OOO (1, 2, or 3 digits)
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if (first != '\\' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// \0 collides with the octal decoder and is non-standard
+		// if ( second.charValue() == '0' ) {
+		//	return Character.valueOf( (char)0x00 );
+		if (second == 'b' ) {
+			return 0x08;
+		} else if (second == 't' ) {
+			return 0x09;
+		} else if (second == 'n' ) {
+			return 0x0a;
+		} else if (second == 'v' ) {
+			return 0x0b;
+		} else if (second == 'f' ) {
+			return 0x0c;
+		} else if (second == 'r' ) {
+			return 0x0d;
+		} else if (second == '\"' ) {
+			return 0x22;
+		} else if (second == '\'' ) {
+			return 0x27;
+		} else if (second == '\\' ) {
+			return 0x5c;
+			
+		// look for \\xXX format
+		} else if ( Character.toLowerCase( second.charValue() ) == 'x' ) {
+			// Search for exactly 2 hex digits following
+			StringBuilder sb = new StringBuilder();
+			for ( int i=0; i<2; i++ ) {
+				Character c = input.nextHex();
+				if ( c != null ) sb.append( c );
+				else {
+					input.reset();
+					return null;
+				}
+			}
+			try {
+				// parse the hex digit and create a character
+				int i = Integer.parseInt(sb.toString(), 16);
+                if (Character.isValidCodePoint(i)) {
+                    return (char) i;
+                }
+			} catch( NumberFormatException e ) {
+				// throw an exception for malformed entity?
+				input.reset();
+				return null;
+			}
+			
+		// look for \\uXXXX format
+		} else if ( Character.toLowerCase( second.charValue() ) == 'u') {
+			// Search for exactly 4 hex digits following
+			StringBuilder sb = new StringBuilder();
+			for ( int i=0; i<4; i++ ) {
+				Character c = input.nextHex();
+				if ( c != null ) sb.append( c );
+				else {
+					input.reset();
+					return null;
+				}
+			}
+			try {
+				// parse the hex string and create a character
+				int i = Integer.parseInt(sb.toString(), 16);
+                if (Character.isValidCodePoint(i)) {
+                    return (char) i;
+                }
+			} catch( NumberFormatException e ) {
+				// throw an exception for malformed entity?
+				input.reset();
+				return null;
+			}
+			
+		// look for one, two, or three octal digits
+		} else if ( PushbackString.isOctalDigit(second) ) {
+			StringBuilder sb = new StringBuilder();
+            // get digit 1
+            sb.append(second);
+            
+            // get digit 2 if present
+            Character c2 = input.next();
+            if ( !PushbackString.isOctalDigit(c2) ) {
+            	input.pushback( c2 );
+            } else {
+            	sb.append( c2 );
+	            // get digit 3 if present
+	            Character c3 = input.next();
+	            if ( !PushbackString.isOctalDigit(c3) ) {
+	            	input.pushback( c3 );
+	            } else {
+	            	sb.append( c3 );
+	            }
+            }
+			try {
+				// parse the octal string and create a character
+				int i = Integer.parseInt(sb.toString(), 8);
+                if (Character.isValidCodePoint(i)) {
+                    return (char) i;
+                }
+			} catch( NumberFormatException e ) {
+				// throw an exception for malformed entity?
+				input.reset();
+				return null;
+			}
+		}
+		
+		// ignore the backslash and return the character
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java
new file mode 100644
index 0000000..01db4fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java
@@ -0,0 +1,264 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+
+/**
+ * Implementation of the Codec interface for MySQL strings. See http://mirror.yandex.ru/mirrors/ftp.mysql.com/doc/refman/5.0/en/string-syntax.html
+ * for more information.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class MySQLCodec extends Codec {
+    /**
+     * Specifies the SQL Mode the target MySQL Server is running with. For details about MySQL Server Modes
+     * please see the Manual at {@link http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_ansi}
+     *
+     * Currently the only supported modes are:
+     * ANSI
+     * STANDARD
+     */
+    public static enum Mode {
+        ANSI(1),STANDARD(0);
+
+        private int key;
+        private Mode(int key) { this.key = key; }
+
+        static Mode findByKey(int key) {
+            for ( Mode m : values() ) {
+                if ( m.key == key )
+                    return m;
+            }
+            return null;
+        }
+    }
+
+    /** Target MySQL Server is running in Standard MySQL (Default) mode. */
+    public static final int MYSQL_MODE = 0;
+    /** Target MySQL Server is running in {@link "http://dev.mysql.com/doc/refman/5.0/en/ansi-mode.html"} ANSI Mode */
+    public static final int ANSI_MODE = 1;
+	
+	//private int mode = 0;
+    private Mode mode;
+	
+	/**
+	 * Instantiate the MySQL codec
+	 * 
+	 * @param mode
+	 * 			Mode has to be one of {MYSQL_MODE|ANSI_MODE} to allow correct encoding
+     * @deprecated
+     * @see #MySQLCodec(org.owasp.esapi.codecs.MySQLCodec.Mode)
+	 */
+	public MySQLCodec( int mode ) {
+		this.mode = Mode.findByKey(mode);
+	}
+
+    /**
+     * Instantiate the MySQL Codec with the given SQL {@link Mode}.
+     * @param mode The mode the target server is running in
+     */
+    public MySQLCodec( Mode mode ) {
+        this.mode = mode;
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns quote-encoded character
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+		switch( mode ) {
+			case ANSI: return encodeCharacterANSI( c );
+			case STANDARD: return encodeCharacterMySQL( c );
+		}
+		return null;
+	}
+	
+	/**
+	 * encodeCharacterANSI encodes for ANSI SQL. 
+	 * 
+	 * Apostrophe is encoded
+     *
+     * Bug ###: In ANSI Mode Strings can also be passed in using the quotation. In ANSI_QUOTES mode a quotation
+     * is considered to be an identifier, thus cannot be used at all in a value and will be dropped completely.
+	 * 
+	 * @param c 
+	 * 			character to encode
+	 * @return
+	 * 			String encoded to standards of MySQL running in ANSI mode
+	 */
+	private String encodeCharacterANSI( Character c ) {
+		if ( c == '\'' )
+        	return "\'\'";
+        if ( c == '\"' )
+            return "";
+        return ""+c;
+	}
+
+	/**
+	 * Encode a character suitable for MySQL
+	 * 
+	 * @param c
+	 * 			Character to encode
+	 * @return
+	 * 			Encoded Character
+	 */
+	private String encodeCharacterMySQL( Character c ) {
+		char ch = c.charValue();
+		if ( ch == 0x00 ) return "\\0";
+		if ( ch == 0x08 ) return "\\b";
+		if ( ch == 0x09 ) return "\\t";
+		if ( ch == 0x0a ) return "\\n";
+		if ( ch == 0x0d ) return "\\r";
+		if ( ch == 0x1a ) return "\\Z";
+		if ( ch == 0x22 ) return "\\\"";
+		if ( ch == 0x25 ) return "\\%";
+		if ( ch == 0x27 ) return "\\'";
+		if ( ch == 0x5c ) return "\\\\";
+		if ( ch == 0x5f ) return "\\_";
+	    return "\\" + c;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal (case sensitive)
+	 *   In ANSI_MODE '' decodes to '
+	 *   In MYSQL_MODE \x decodes to x (or a small list of specials)
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		switch( mode ) {
+			case ANSI: return decodeCharacterANSI( input );
+			case STANDARD: return decodeCharacterMySQL( input );
+		}
+		return null;
+	}
+
+	/**
+	 * decodeCharacterANSI decodes the next character from ANSI SQL escaping
+	 *  
+	 * @param input
+	 * 			A PushBackString containing characters you'd like decoded
+	 * @return
+	 * 			A single character, decoded
+	 */
+	private Character decodeCharacterANSI( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( second.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+		return( Character.valueOf( '\'' ) );
+	}
+
+	/**
+	 * decodeCharacterMySQL decodes all the potential escaped characters that MySQL is prepared to escape
+	 * 
+	 * @param input
+	 * 			A string you'd like to be decoded
+	 * @return
+	 * 			A single character from that string, decoded.
+	 */
+	private Character decodeCharacterMySQL( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\\' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		if ( second.charValue() == '0' ) {
+			return Character.valueOf( (char)0x00 );
+		} else if ( second.charValue() == 'b' ) {
+			return Character.valueOf( (char)0x08 );
+		} else if ( second.charValue() == 't' ) {
+			return Character.valueOf( (char)0x09 );
+		} else if ( second.charValue() == 'n' ) {
+			return Character.valueOf( (char)0x0a );
+		} else if ( second.charValue() == 'r' ) {
+			return Character.valueOf( (char)0x0d );
+		} else if ( second.charValue() == 'z' ) {
+			return Character.valueOf( (char)0x1a );
+		} else if ( second.charValue() == '\"' ) {
+			return Character.valueOf( (char)0x22 );
+		} else if ( second.charValue() == '%' ) {
+			return Character.valueOf( (char)0x25 );
+		} else if ( second.charValue() == '\'' ) {
+			return Character.valueOf( (char)0x27 );
+		} else if ( second.charValue() == '\\' ) {
+			return Character.valueOf( (char)0x5c );
+		} else if ( second.charValue() == '_' ) {
+			return Character.valueOf( (char)0x5f );
+		} else {
+			return second;
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java
new file mode 100644
index 0000000..953fece
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java
@@ -0,0 +1,90 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+
+/**
+ * Implementation of the Codec interface for Oracle strings. This function will only protect you from SQLi in the case of user data
+ * bring placed within an Oracle quoted string such as:
+ * 
+ * select * from table where user_name='  USERDATA    ';
+ * 
+ * @see <a href="http://oraqa.com/2006/03/20/how-to-escape-single-quotes-in-strings/">how-to-escape-single-quotes-in-strings</a>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class OracleCodec extends Codec {
+
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Encodes ' to ''
+     *
+	 * Encodes ' to ''
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		if ( c.charValue() == '\'' )
+        	return "\'\'";
+        return ""+c;
+	}
+	
+
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 *
+	 * Formats all are legal
+	 *   '' decodes to '
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		if ( second == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( second.charValue() != '\'' ) {
+			input.reset();
+			return null;
+		}
+		return( Character.valueOf( '\'' ) );
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java
new file mode 100644
index 0000000..307da47
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java
@@ -0,0 +1,154 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * Implementation of the Codec interface for percent encoding (aka URL encoding).
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class PercentCodec extends Codec
+{
+	private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+	@SuppressWarnings("unused")
+	private static final String RFC3986_RESERVED_STR = ":/?#[]@!$&'()*+,;=";
+	private static final String RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR = "-._~";
+		// rfc3986 2.3: For consistency, percent-encoded octets
+		// in the ranges of ALPHA (%41-%5A and %61-%7A), DIGIT
+		// (%30-%39), hyphen (%2D), period (%2E), underscore
+		// (%5F), or tilde (%7E) should not be created by URI
+		// producers
+	private static final boolean ENCODED_NON_ALPHA_NUMERIC_UNRESERVED = true;
+	private static final String UNENCODED_STR = ALPHA_NUMERIC_STR +
+		(ENCODED_NON_ALPHA_NUMERIC_UNRESERVED ? "" : RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR);
+	private static final Set<Character> UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR);
+
+	/**
+	 * Convinence method to encode a string into UTF-8. This
+	 * wraps the {@link UnsupportedEncodingException} that
+	 * {@link String#getBytes(String)} throws in a
+	 * {@link IllegalStateException} as UTF-8 support is required
+	 * by the Java spec and should never throw this exception.
+	 * @param str the string to encode
+	 * @return str encoded in UTF-8 as bytes.
+	 * @throws IllegalStateException wrapped {@link
+	 *	UnsupportedEncodingException} if
+	 *	{@link String.getBytes(String)} throws it.
+	 */
+	private static byte[] toUtf8Bytes(String str)
+	{
+		try
+		{
+			return str.getBytes("UTF-8");
+		}
+		catch(UnsupportedEncodingException e)
+		{
+			throw new IllegalStateException("The Java spec requires UTF-8 support.", e);
+		}
+	}
+
+	/**
+	 * Append the two upper case hex characters for a byte.
+	 * @param sb The string buffer to append to.
+	 * @param b The byte to hexify
+	 * @return sb with the hex characters appended.
+	 */
+	// rfc3986 2.1: For consistency, URI producers 
+	// should use uppercase hexadecimal digits for all percent-
+	// encodings.
+	private static StringBuilder appendTwoUpperHex(StringBuilder sb, int b)
+	{
+		if(b < Byte.MIN_VALUE || b > Byte.MAX_VALUE)
+			throw new IllegalArgumentException("b is not a byte (was " + b + ')');
+		b &= 0xFF;
+		if(b<0x10)
+			sb.append('0');
+		return sb.append(Integer.toHexString(b).toUpperCase());
+	}
+
+	/**
+	 * Encode a character for URLs
+	 * @param immune characters not to encode
+	 * @param c character to encode
+	 * @return the encoded string representing c
+	 */
+	public String encodeCharacter( char[] immune, Character c )
+	{
+		String cStr = String.valueOf(c.charValue());
+		byte[] bytes;
+		StringBuilder sb;
+
+		if(UNENCODED_SET.contains(c))
+			return cStr;
+
+		bytes = toUtf8Bytes(cStr);
+		sb = new StringBuilder(bytes.length * 3);
+		for(byte b : bytes)
+			appendTwoUpperHex(sb.append('%'), b);
+		return sb.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Formats all are legal both upper/lower case:
+	 *   %hh;
+	 *   
+	 * @param input
+	 * 			encoded character using percent characters (such as URL encoding)
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+
+		// if this is not an encoded character, return null
+		if (first != '%' ) {
+			input.reset();
+			return null;
+		}
+
+		// Search for exactly 2 hex digits following
+		StringBuilder sb = new StringBuilder();
+		for ( int i=0; i<2; i++ ) {
+			Character c = input.nextHex();
+			if ( c != null ) sb.append( c );
+		}
+		if ( sb.length() == 2 ) {
+			try {
+				// parse the hex digit and create a character
+				int i = Integer.parseInt(sb.toString(), 16);
+				if (Character.isValidCodePoint(i)) {
+					return (char) i;
+				}
+			} catch( NumberFormatException ignored ) { }
+		}
+		input.reset();
+		return null;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackString.java b/src/main/java/org/owasp/esapi/codecs/PushbackString.java
new file mode 100644
index 0000000..d218eb9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/PushbackString.java
@@ -0,0 +1,185 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * The pushback string is used by Codecs to allow them to push decoded characters back onto a string
+ * for further decoding. This is necessary to detect double-encoding.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class PushbackString {
+
+	private String input;
+	private Character pushback;
+	private Character temp;
+	private int index = 0;
+	private int mark = 0;
+	
+    /**
+     *
+     * @param input
+     */
+    public PushbackString( String input ) {
+		this.input = input;
+	}
+
+    /**
+     *
+     * @param c
+     */
+    public void pushback( Character c ) {
+		pushback = c;
+	}
+	
+
+    /**
+     * Get the current index of the PushbackString. Typically used in error messages.
+     * @return The current index of the PushbackString.
+     */
+    public int index() {
+		return index;
+	}
+	
+    /**
+     *
+     * @return
+     */
+    public boolean hasNext() {
+		if ( pushback != null ) return true;
+		if ( input == null ) return false;
+		if ( input.length() == 0 ) return false;
+		if ( index >= input.length() ) return false;
+		return true;		
+	}
+	
+    /**
+     *
+     * @return
+     */
+    public Character next() {
+		if ( pushback != null ) {
+			Character save = pushback;
+			pushback = null;
+			return save;
+		}
+		if ( input == null ) return null;
+		if ( input.length() == 0 ) return null;
+		if ( index >= input.length() ) return null;		
+		return Character.valueOf( input.charAt(index++) );
+	}
+	
+    /**
+    *
+    * @return
+    */
+   public Character nextHex() {
+		Character c = next();
+		if ( c == null ) return null;
+		if ( isHexDigit( c ) ) return c;
+		return null;
+	}
+
+   /**
+   *
+   * @return
+   */
+  public Character nextOctal() {
+		Character c = next();
+		if ( c == null ) return null;
+		if ( isOctalDigit( c ) ) return c;
+		return null;
+	}
+
+  /**
+ * Returns true if the parameter character is a hexidecimal digit 0 through 9, a through f, or A through F.
+  * @param c
+  * @return
+  */
+ public static boolean isHexDigit( Character c ) {
+		if ( c == null ) return false;
+		char ch = c.charValue();
+		return (ch >= '0' && ch <= '9' ) || (ch >= 'a' && ch <= 'f' ) || (ch >= 'A' && ch <= 'F' );
+	}
+
+ /**
+ * Returns true if the parameter character is an octal digit 0 through 7.
+ * @param c
+ * @return
+ */
+public static boolean isOctalDigit( Character c ) {
+	if ( c == null ) return false;
+	char ch = c.charValue();
+	return ch >= '0' && ch <= '7';
+}
+
+    /**
+     * Return the next character without affecting the current index.
+     * @return
+     */
+    public Character peek() {
+		if ( pushback != null ) return pushback;
+		if ( input == null ) return null;
+		if ( input.length() == 0 ) return null;
+		if ( index >= input.length() ) return null;		
+		return Character.valueOf( input.charAt(index) );
+	}
+	
+    /**
+     * Test to see if the next character is a particular value without affecting the current index.
+     * @param c
+     * @return
+     */
+    public boolean peek( char c ) {
+		if ( pushback != null && pushback.charValue() == c ) return true;
+		if ( input == null ) return false;
+		if ( input.length() == 0 ) return false;
+		if ( index >= input.length() ) return false;		
+		return input.charAt(index) == c;
+	}	
+	
+    /**
+     *
+     */
+    public void mark() {
+		temp = pushback;
+		mark = index;
+	}
+
+    /**
+     *
+     */
+    public void reset() {
+		pushback = temp;
+		index = mark;
+	}
+	
+    /**
+     *
+     * @return
+     */
+    protected String remainder() {
+		String output = input.substring( index );
+		if ( pushback != null ) {
+			output = pushback + output;
+		}
+		return output;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/Trie.java b/src/main/java/org/owasp/esapi/codecs/Trie.java
new file mode 100644
index 0000000..da11268
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/Trie.java
@@ -0,0 +1,172 @@
+package org.owasp.esapi.codecs;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public interface Trie<T> extends Map<CharSequence,T>
+{
+	public Map.Entry<CharSequence,T> getLongestMatch(CharSequence key);
+	public Map.Entry<CharSequence,T> getLongestMatch(PushbackReader keyIn) throws IOException;
+	public int getMaxKeyLength();
+
+	static class TrieProxy<T> implements Trie<T>
+	{
+		private Trie<T> wrapped;
+
+		TrieProxy(Trie<T> toWrap)
+		{
+			wrapped = toWrap;
+		}
+
+		protected Trie<T> getWrapped()
+		{
+			return wrapped;
+		}
+
+		public Map.Entry<CharSequence,T> getLongestMatch(CharSequence key)
+		{
+			return wrapped.getLongestMatch(key);
+		}
+
+		public Map.Entry<CharSequence,T> getLongestMatch(PushbackReader keyIn) throws IOException
+		{
+			return wrapped.getLongestMatch(keyIn);
+		}
+
+		public int getMaxKeyLength()
+		{
+			return wrapped.getMaxKeyLength();
+		}
+
+		/* java.util.Map: */
+
+    		public int size()
+		{
+			return wrapped.size();
+		}
+
+    		public boolean isEmpty()
+		{
+			return wrapped.isEmpty();
+		}
+
+    		public boolean containsKey(Object key)
+		{
+			return wrapped.containsKey(key);
+		}
+
+    		public boolean containsValue(Object val)
+		{
+			return wrapped.containsValue(val);
+		}
+
+    		public T get(Object key)
+		{
+			return wrapped.get(key);
+		}
+
+    		public T put(CharSequence key, T value)
+		{
+			return wrapped.put(key, value);
+		}
+
+    		public T remove(Object key)
+		{
+			return wrapped.remove(key);
+		}
+
+    		public void putAll(Map<? extends CharSequence,? extends T> t)
+		{
+			wrapped.putAll(t);
+		}
+
+    		public void clear()
+		{
+			wrapped.clear();
+		}
+
+    		public Set<CharSequence> keySet()
+		{
+			return wrapped.keySet();
+		}
+
+    		public Collection<T> values()
+		{
+			return wrapped.values();
+		}
+
+    		public Set<Map.Entry<CharSequence,T>> entrySet()
+		{
+			return wrapped.entrySet();
+		}
+
+    		public boolean equals(Object other)
+		{
+			return wrapped.equals(other);
+		}
+
+    		public int hashCode()
+		{
+			return wrapped.hashCode();
+		}
+	}
+
+	static class Unmodifiable<T> extends TrieProxy<T>
+	{
+		Unmodifiable(Trie<T> toWrap)
+		{
+			super(toWrap);
+		}
+
+    		public T put(CharSequence key, T value)
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public T remove(CharSequence key)
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public void putAll(Map<? extends CharSequence,? extends T> t)
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public void clear()
+		{
+			throw new UnsupportedOperationException("Unmodifiable Trie");
+		}
+
+    		public Set<CharSequence> keySet()
+		{
+			return Collections.unmodifiableSet(super.keySet());
+		}
+
+    		public Collection<T> values()
+		{
+			return Collections.unmodifiableCollection(super.values());
+		}
+
+    		public Set<Map.Entry<CharSequence,T>> entrySet()
+		{
+			return Collections.unmodifiableSet(super.entrySet());
+		}
+	}
+
+	public static class Util
+	{
+		private Util()
+		{
+		}
+
+		static <T> Trie<T> unmodifiable(Trie<T> toWrap)
+		{
+			return new Unmodifiable<T>(toWrap);
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java
new file mode 100644
index 0000000..19ecc80
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java
@@ -0,0 +1,82 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for '\' encoding from Unix command shell.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class UnixCodec extends Codec {
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns backslash-encoded character
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+        return "\\" + c;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * <p>
+	 * Formats all are legal both upper/lower case:
+	 *   \x - all special characters
+	 *   
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\\' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java
new file mode 100644
index 0000000..2dccbbd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java
@@ -0,0 +1,117 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import org.owasp.esapi.EncoderConstants;
+
+
+/**
+ * Implementation of the Codec interface for 'quote' encoding from VBScript.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class VBScriptCodec extends Codec {
+
+	/**
+	 * Encode a String so that it can be safely used in a specific context.
+	 * 
+     * @param immune
+     * @param input
+	 * 		the String to encode
+	 * @return the encoded String
+	 */
+    public String encode(char[] immune, String input) {
+    	StringBuilder sb = new StringBuilder();
+		boolean encoding = false;
+		boolean inquotes = false;
+		for ( int i=0; i<input.length(); i++ ) {
+			char c = input.charAt(i);
+			
+			// handle normal characters and surround them with quotes
+			if (containsCharacter(c, EncoderConstants.CHAR_ALPHANUMERICS) || containsCharacter(c, immune)) {
+				if ( encoding && i > 0 ) sb.append( "&" );
+				if ( !inquotes && i > 0 ) sb.append( "\"" );
+				sb.append( c );
+				inquotes = true;
+				encoding = false;
+				
+			// handle characters that need encoding
+			} else {
+				if ( inquotes && i < input.length() ) sb.append( "\"" );
+				if ( i > 0 ) sb.append( "&" );
+				sb.append( encodeCharacter( immune, Character.valueOf( c ) ) );
+				inquotes = false;
+				encoding = true;
+			}
+		}
+		return sb.toString();
+    }
+
+
+	/**
+	 * Returns quote-encoded character
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+        return "chrw(" + (int)c.charValue() + ")";
+	}
+	
+	
+	
+	/**
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Formats all are legal both upper/lower case:
+	 *   "x - all special characters
+	 *   " + chr(x) + "  - not supported yet
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '\"' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java
new file mode 100644
index 0000000..719f009
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java
@@ -0,0 +1,82 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+
+/**
+ * Implementation of the Codec interface for '^' encoding from Windows command shell.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class WindowsCodec extends Codec {
+
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns Windows shell encoded character (which is ^)
+     *
+     * @param immune
+     */
+	public String encodeCharacter( char[] immune, Character c ) {
+		char ch = c.charValue();
+		
+		// check for immune characters
+		if ( containsCharacter( ch, immune ) ) {
+			return ""+ch;
+		}
+		
+		// check for alphanumeric characters
+		String hex = Codec.getHexForNonAlphanumeric( ch );
+		if ( hex == null ) {
+			return ""+ch;
+		}
+		
+        return "^" + c;
+	}
+	
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * <p>
+	 * Formats all are legal both upper/lower case:
+	 *   ^x - all special characters
+	 */
+	public Character decodeCharacter( PushbackString input ) {
+		input.mark();
+		Character first = input.next();
+		if ( first == null ) {
+			input.reset();
+			return null;
+		}
+		
+		// if this is not an encoded character, return null
+		if ( first.charValue() != '^' ) {
+			input.reset();
+			return null;
+		}
+
+		Character second = input.next();
+		return second;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java
new file mode 100644
index 0000000..e248392
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java
@@ -0,0 +1,298 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ */
+package org.owasp.esapi.codecs;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * Implementation of the Codec interface for XML entity encoding.
+ * This differes from HTML entity encoding in that only the following
+ * named entities are predefined:
+ * <ul>
+ * 	<li>lt</li>
+ * 	<li>gt</li>
+ * 	<li>amp</li>
+ * 	<li>apos</li>
+ * 	<li>quot</li>
+ * </ul>
+ * However, the XML Specification 1.0 states in section 4.6 "Predefined
+ * Entities" that these should still be declared for interoperability
+ * purposes. As such, encoding in this class will not use them.
+ *
+ * It's also worth noting that unlike the HTMLEntityCodec, a trailing
+ * semicolon is required and all valid codepoints are accepted.
+ *
+ * Note that it is a REALLY bad idea to use this for decoding as an XML
+ * document can declare arbitrary entities that this Codec has no way
+ * of knowing about. Decoding is included for completeness but it's use
+ * is not recommended. Use a XML parser instead!
+ */
+public class XMLEntityCodec extends Codec
+{
+	private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+	private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t";
+	private static final Set<Character> UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR);
+	private static final HashTrie<Character> entityToCharacterMap;
+
+	static
+	{	// populate entitites
+		entityToCharacterMap = new HashTrie<Character>();
+		entityToCharacterMap.put("lt", '<');
+		entityToCharacterMap.put("gt", '>');
+		entityToCharacterMap.put("amp", '&');
+		entityToCharacterMap.put("apos", '\'');
+		entityToCharacterMap.put("quot", '"');
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Encodes a Character using XML entities as necessary.
+	 *
+	 * @param immune characters that should not be encoded as entities
+	 */
+	public String encodeCharacter(char[] immune, Character c)
+	{
+		// check for immune characters
+		if(containsCharacter(c, immune))
+			return c.toString();
+
+		// check for unencoded characters
+		if(UNENCODED_SET.contains(c))
+			return c.toString();
+
+		return "&#x" + Integer.toHexString(c.charValue()) + ";";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * Returns the decoded version of the character starting at index, or
+	 * null if no decoding is possible.
+	 * 
+	 * Legal formats:
+	 * <ul>
+	 * 	<li>&#dddd;</li>
+	 * 	<li>&#xhhhh;</li>
+	 * 	<li>&name;</li>
+	 * </ul>
+	 */
+	public Character decodeCharacter(PushbackString input)
+	{
+		Character ret = null;
+		Character first;
+		Character second;
+
+		input.mark();
+		try
+		{
+			first = input.next();
+			if(first == null)
+				return null;
+
+			// if this is not an encoded character, return null
+			if(first != '&')
+				return null;
+
+			// test for numeric encodings
+			second = input.next();
+			if(second==null)
+				return null;
+
+			if(second=='#')
+			{	// handle numbers
+				ret = getNumericEntity(input);
+			}
+			else if(Character.isLetter(second.charValue()))
+			{	// handle entities
+				input.pushback(second);
+				ret = getNamedEntity(input);
+			}
+		}
+		finally
+		{
+			if(ret == null)
+				input.reset();
+		}
+		return ret;
+	}
+
+	/**
+	 * Converts the rest of a numeric entity to a character.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &#
+	 * @return The character decoded or null on failure.
+	 */
+	private static Character getNumericEntity(PushbackString input)
+	{
+		Character first = input.peek();
+
+		if(first == null)
+			return null;
+
+		if(first=='x'||first=='X')
+		{
+			input.next();	// nuke X
+			return parseHex(input);
+		}
+		return parseNumber(input);
+	}
+
+	/**
+	 * Convert a integer code point to a Character.
+	 * @param i the integer
+	 * @return i as a Character or null if i is a invalid code point
+	 * 	or outside of the Java char range.
+	 */
+	private static Character int2char(int i)
+	{
+		if(!Character.isValidCodePoint(i))
+			return null;
+		if(!(Character.MIN_VALUE <= i && i <= Character.MAX_VALUE))
+			return null;	// we can't 0x010000-0x100000 currently
+		return (char)i;
+	}
+
+	/**
+	 * Converts the rest of a decimal numeric entity to a character.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &# and that
+	 *	the next char is not a 'x' or 'X'.
+	 * @return The character decoded or null on failutre.
+	 */
+	private static Character parseNumber(PushbackString input)
+	{
+		StringBuilder sb = new StringBuilder();
+		Character c;
+		while((c=input.next())!=null)
+		{
+			// end of entity?
+			if(c==';')
+				break;
+
+			// check for digit
+			if(!Character.isDigit(c.charValue()))
+				return null;
+			sb.append(c);
+		}
+		if(c==null)
+			return null;	// not ';' termintated
+		if(sb.length()<=0)	// no digits
+			return null;
+		try
+		{
+			return int2char(Integer.parseInt(sb.toString()));
+		}
+		catch(NumberFormatException e)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * Converts the rest of a hexidecimal numeric entity to a character.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &#[xX]
+	 * @return The character decoded or null on failutre.
+	 */
+	private static Character parseHex(PushbackString input)
+	{
+		Character c;
+		StringBuilder sb = new StringBuilder();
+		input_loop: while((c=input.next())!=null)
+		{
+			switch(c.charValue())
+			{
+				case 'a':
+				case 'b':
+				case 'c':
+				case 'd':
+				case 'e':
+				case 'f':
+				case 'A':
+				case 'B':
+				case 'C':
+				case 'D':
+				case 'E':
+				case '0':
+				case '1':
+				case '2':
+				case '3':
+				case '4':
+				case '5':
+				case '6':
+				case '7':
+				case '8':
+				case '9':
+					sb.append(c);
+					break;
+				case ';':
+					break input_loop;
+				default:
+					return null;
+			}
+		}
+		if(c==null)
+			return null;	// not ';' termintated
+		if(sb.length()<=0)	// no digits
+			return null;
+		try
+		{
+			return int2char(Integer.parseInt(sb.toString(),16));
+		}
+		catch(NumberFormatException e)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * 
+	 * Converts the rest of a named entity to a character.
+	 * null if no decoding is possible.
+	 * @param input The input to read from. It is assumed that input
+	 * 	is positioned at the character after the &.
+	 * @return The character decoded or null on failutre.
+	 */
+	private Character getNamedEntity(PushbackString input)
+	{
+		StringBuilder possible = new StringBuilder();
+		Map.Entry<CharSequence,Character> entry;
+		int len;
+
+		// kludge around PushbackString....
+		len = Math.min(input.remainder().length(), entityToCharacterMap.getMaxKeyLength()+1);
+		for(int i=0;i<len;i++)
+			possible.append(Character.toLowerCase(input.next()));
+
+		// look up the longest match
+		entry = entityToCharacterMap.getLongestMatch(possible);
+		if(entry == null)
+			return null;	// no match, caller will reset input
+		len = entry.getKey().length();	// what matched's length
+		if(possible.length() <= len || possible.charAt(len)!=';')
+			return null;	// not semicolon
+
+		// fixup input
+		input.reset();
+		input.next();	// read &
+		for(int i=0;i<len;i++)
+			input.next();
+		input.next();	// read semicolen
+		return entry.getValue();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/codecs/package.html b/src/main/java/org/owasp/esapi/codecs/package.html
new file mode 100644
index 0000000..0951a73
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/codecs/package.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains codecs for application layer encoding/escaping schemes that can be used for
+both canonicalization and output encoding. By using the codecs to decode (canonicalize) input
+before validation, many attacks can be detected and handled.  By using the codecs to encode
+untrusted data before sending it to an interpreter, a wide variety of 'injection' attacks can
+be stopped. However,
+this package does not currently address issues related to converting between byte-streams and 
+internal character representations, such as overlong UTF-8 issues. Those are left to the platform.
+The codecs cover protocol encodings such as HTML entity encoding and percent encoding, but also
+common product escaping schemes, such as Unix, Windows, MySQL, and Oracle.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/all-wcprops b/src/main/java/org/owasp/esapi/crypto/.svn/all-wcprops
new file mode 100644
index 0000000..3695fe3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/all-wcprops
@@ -0,0 +1,65 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto
+END
+CipherTextSerializer.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java
+END
+SecurityProviderLoader.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/package.html
+END
+CipherText.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/CipherText.java
+END
+CryptoHelper.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java
+END
+CryptoToken.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/CryptoToken.java
+END
+KeyDerivationFunction.java
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java
+END
+CipherSpec.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/CipherSpec.java
+END
+CryptoDiscoverer.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java
+END
+PlainText.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto/PlainText.java
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/entries b/src/main/java/org/owasp/esapi/crypto/.svn/entries
new file mode 100644
index 0000000..36e48a2
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/entries
@@ -0,0 +1,368 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/crypto
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+419a81b203a6a2e082efa944ebf2b83a
+2010-02-04T03:26:12.593455Z
+1078
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+311
+

+CipherText.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+3a5ac1979edd4e4d82c23e3b70b1fe82
+2013-08-31T22:41:13.661788Z
+1892
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+39064
+

+CryptoHelper.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+cfb05640f66b6513de26ed481121880c
+2013-08-31T22:41:13.661788Z
+1892
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+18077
+

+CryptoToken.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+890bc1277083e9a70914aed0ffe7cead
+2013-08-31T22:41:13.661788Z
+1892
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+34565
+

+KeyDerivationFunction.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+6dc2b7e88f49d986879cd074026d3333
+2013-08-31T22:41:13.661788Z
+1892
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+21270
+

+CipherSpec.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+0e54b4aae60556e41e11fde6c77abb3c
+2013-08-31T22:41:13.661788Z
+1892
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13120
+

+CryptoDiscoverer.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+d1372b576a290b267cc3ece43a918eb2
+2011-01-09T05:36:40.145164Z
+1672
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2767
+

+PlainText.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+eb905f776e6cdc2da18cb04ef8b7630e
+2013-08-31T22:43:12.271347Z
+1893
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5513
+

+CipherTextSerializer.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+44c705d348d5d9d892cc31ce01e1cca6
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+20820
+

+SecurityProviderLoader.java
+file
+
+
+
+
+2014-02-18T16:19:52.737962Z
+ea8f1ee04dc6d2b89ef53d15e17cbedc
+2013-08-31T22:43:12.271347Z
+1893
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15295
+

diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CipherSpec.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CipherSpec.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CipherSpec.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CipherTextSerializer.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CipherTextSerializer.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CipherTextSerializer.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoHelper.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoHelper.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoHelper.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoToken.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoToken.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoToken.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/KeyDerivationFunction.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/KeyDerivationFunction.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/KeyDerivationFunction.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/PlainText.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/PlainText.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/PlainText.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/SecurityProviderLoader.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/SecurityProviderLoader.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/SecurityProviderLoader.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/package.html.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/package.html.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/prop-base/package.html.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherSpec.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherSpec.java.svn-base
new file mode 100644
index 0000000..556ef8e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherSpec.java.svn-base
@@ -0,0 +1,388 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+// If we had PRIVATE packages, e.g., org.owasp.esapi.util.pvt, this would belong
+// there. CipherText uses this, but doesn't expose it directly.
+package org.owasp.esapi.crypto;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+
+import javax.crypto.Cipher;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.util.NullSafe;
+
+
+/**
+ * Specifies all the relevant configuration data needed in constructing and
+ * using a {@link javax.crypto.Cipher} except for the encryption key.
+ * </p><p>
+ * The "setters" all return a reference to {@code this} so that they can be
+ * strung together.
+ * </p><p>
+ * Note: While this is a useful class in it's own right, it should primarily be
+ * regarded as an implementation class to use with ESAPI encryption, especially
+ * the reference implementation. It is <i>not</i> intended to be used directly
+ * by application developers, but rather only by those either extending ESAPI
+ * or in the ESAPI reference implementation. Use <i>directly</i> by application
+ * code is not recommended or supported.
+ * 
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public final class CipherSpec implements Serializable {
+
+	private static final long serialVersionUID = 20090822;	// version, in YYYYMMDD format
+	
+	private String  cipher_xform_   = ESAPI.securityConfiguration().getCipherTransformation();
+	private int     keySize_        = ESAPI.securityConfiguration().getEncryptionKeyLength(); // In bits
+	private int     blockSize_      = 16;   // In bytes! I.e., 128 bits!!!
+	private byte[]  iv_             = null;
+	
+	// Cipher transformation component. Format is ALG/MODE/PADDING
+    private enum CipherTransformationComponent { ALG, MODE, PADDING }
+
+	/**
+	 * CTOR that explicitly sets everything.
+	 * @param cipherXform	The cipher transformation
+	 * @param keySize		The key size (in bits).
+	 * @param blockSize		The block size (in bytes).
+	 * @param iv			The initialization vector. Null if not applicable.
+	 */
+	public CipherSpec(String cipherXform, int keySize, int blockSize, final byte[] iv) {
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+		setBlockSize(blockSize);
+		setIV(iv);
+	}
+	
+	/**
+	 * CTOR that sets everything but IV.
+	 * @param cipherXform	The cipher transformation
+	 * @param keySize		The key size (in bits).
+	 * @param blockSize		The block size (in bytes).
+	 */
+	public CipherSpec(String cipherXform, int keySize, int blockSize) {
+		// Note: Do NOT use
+		//			this(cipherXform, keySize, blockSize, null);
+		// because of assertion in setIV().
+		//
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+		setBlockSize(blockSize);
+	}
+	
+	/** CTOR that sets everything but block size and IV. */
+	public CipherSpec(String cipherXform, int keySize) {
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+	}
+	
+	/** CTOR that sets everything except block size. */
+	public CipherSpec(String cipherXform, int keySize, final byte[] iv) {
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+		setIV(iv);
+	}
+
+	/** CTOR that sets everything except for the cipher key size and possibly
+	 *  the IV. (IV may not be applicable--e.g., with ECB--or may not have
+	 *  been specified yet.
+	 */
+	public CipherSpec(final Cipher cipher) {
+		setCipherTransformation(cipher.getAlgorithm(), true);
+		setBlockSize(cipher.getBlockSize());
+		if ( cipher.getIV() != null ) {
+			setIV(cipher.getIV());
+		}
+	}
+	
+	/** CTOR that sets everything. */
+	public CipherSpec(final Cipher cipher, int keySize) {
+		this(cipher);
+		setKeySize(keySize);
+	}
+	
+	/* CTOR that sets only the IV and uses defaults for everything else. */
+	public CipherSpec(final byte[] iv) {
+		setIV(iv);
+	}
+	
+	/**
+	 * Default CTOR. Creates a cipher specification for 128-bit cipher
+	 * transformation of "AES/CBC/PKCS5Padding" and a {@code null} IV.
+	 */
+	public CipherSpec() {
+		// All defaults
+	}
+
+	/**
+	 * Set the cipher transformation for this {@code CipherSpec}.
+	 * @param cipherXform	The cipher transformation string; e.g., "DESede/CBC/PKCS5Padding".
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setCipherTransformation(String cipherXform) {
+		setCipherTransformation(cipherXform, false);
+		return this;
+	}
+
+	/**
+	 * Set the cipher transformation for this {@code CipherSpec}. This is only
+	 * used by the CTOR {@code CipherSpec(Cipher)} and {@code CipherSpec(Cipher, int)}.
+	 * @param cipherXform	The cipher transformation string; e.g.,
+	 * 						"DESede/CBC/PKCS5Padding". May not be null or empty.
+	 * @param fromCipher If true, the cipher transformation was set via
+	 * 					 {@code Cipher.getAlgorithm()} which may only return the
+	 * 					 actual algorithm. In that case we check and if all 3 parts
+	 * 					 were not specified, then we specify the parts that were
+	 * 					 based on "ECB" as the default cipher mode and "NoPadding"
+	 * 					 as the default padding scheme.
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	private CipherSpec setCipherTransformation(String cipherXform, boolean fromCipher) {
+		if ( ! StringUtilities.notNullOrEmpty(cipherXform, true) ) {	// Yes, really want '!' here.
+			throw new IllegalArgumentException("Cipher transformation may not be null or empty string (after trimming whitespace).");
+		}
+		int parts = cipherXform.split("/").length;
+		assert ( !fromCipher ? (parts == 3) : true ) :
+			"Malformed cipherXform (" + cipherXform + "); must have form: \"alg/mode/paddingscheme\"";
+		if ( fromCipher && (parts != 3)  ) {
+				// Indicates cipherXform was set based on Cipher.getAlgorithm()
+				// and thus may not be a *complete* cipher transformation.
+			if ( parts == 1 ) {
+				// Only algorithm was given.
+				cipherXform += "/ECB/NoPadding";
+			} else if ( parts == 2 ) {
+				// Only algorithm and mode was given.
+				cipherXform += "/NoPadding";
+			} else if ( parts == 3 ) {
+				// All three parts provided. Do nothing. Could happen if not compiled with
+				// assertions enabled.
+				;	// Do nothing - shown only for completeness.
+			} else {
+				// Should never happen unless Cipher implementation is totally screwed up.
+				throw new IllegalArgumentException("Cipher transformation '" +
+								cipherXform + "' must have form \"alg/mode/paddingscheme\"");
+			}
+		} else if ( !fromCipher && parts != 3 ) {
+			throw new IllegalArgumentException("Malformed cipherXform (" + cipherXform +
+											   "); must have form: \"alg/mode/paddingscheme\"");
+		}
+		assert cipherXform.split("/").length == 3 : "Implementation error setCipherTransformation()";
+		this.cipher_xform_ = cipherXform;
+		return this;
+	}
+	
+	/**
+	 * Get the cipher transformation.
+	 * @return	The cipher transformation {@code String}.
+	 */
+	public String getCipherTransformation() {
+		return cipher_xform_;
+	}
+
+	/**
+	 * Set the key size for this {@code CipherSpec}.
+	 * @param keySize	The key size, in bits. Must be positive integer.
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setKeySize(int keySize) {
+		assert keySize > 0 : "keySize must be > 0; keySize=" + keySize;
+		this.keySize_ = keySize;
+		return this;
+	}
+
+	/**
+	 * Retrieve the key size, in bits.
+	 * @return	The key size, in bits, is returned.
+	 */
+	public int getKeySize() {
+		return keySize_;
+	}
+
+	/**
+	 * Set the block size for this {@code CipherSpec}.
+	 * @param blockSize	The block size, in bytes. Must be positive integer.
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setBlockSize(int blockSize) {
+		assert blockSize > 0 : "blockSize must be > 0; blockSize=" + blockSize;
+		this.blockSize_ = blockSize;
+		return this;
+	}
+
+	/**
+	 * Retrieve the block size, in bytes.
+	 * @return	The block size, in bytes, is returned.
+	 */
+	public int getBlockSize() {
+		return blockSize_;
+	}
+
+	/**
+	 * Retrieve the cipher algorithm.
+	 * @return	The cipher algorithm.
+	 */
+	public String getCipherAlgorithm() {
+		return getFromCipherXform(CipherTransformationComponent.ALG);
+	}
+	
+	/**
+	 * Retrieve the cipher mode.
+	 * @return	The cipher mode.
+	 */
+	public String getCipherMode() {
+		return getFromCipherXform(CipherTransformationComponent.MODE);
+	}
+	
+	/**
+	 * Retrieve the cipher padding scheme.
+	 * @return	The padding scheme is returned.
+	 */
+	public String getPaddingScheme() {
+		return getFromCipherXform(CipherTransformationComponent.PADDING);
+	}
+	
+	/**
+	 * Retrieve the initialization vector (IV).
+	 * @return	The IV as a byte array.
+	 */
+	public byte[] getIV() {
+		return iv_;
+	}
+	
+	/**
+	 * Set the initialization vector (IV).
+	 * @param iv	The byte array to set as the IV. A copy of the IV is saved.
+	 * 				This parameter is ignored if the cipher mode does not
+	 * 				require an IV.
+	 * @return		This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setIV(final byte[] iv) {
+		assert requiresIV() && (iv != null && iv.length != 0) : "Required IV cannot be null or 0 length";
+		// Don't store a reference, but make a copy!
+		if ( iv != null ) {	// Allow null IV for ECB mode.
+			iv_ = new byte[ iv.length ];
+			CryptoHelper.copyByteArray(iv, iv_);
+		}
+		return this;
+	}
+
+	/**
+	 * Return true if the cipher mode requires an IV.
+	 * @return True if the cipher mode requires an IV, otherwise false.
+	 * */
+	public boolean requiresIV() {
+		
+		String cm = getCipherMode();
+		
+		// Add any other cipher modes supported by JCE but not requiring IV.
+		// ECB is the only one I'm aware of that doesn't. Mode is not case
+		// sensitive.
+		if ( "ECB".equalsIgnoreCase(cm) ) {
+			return false;
+		}
+		return true;
+	}
+	
+	/**
+	 * Override {@code Object.toString()} to provide something more useful.
+	 * @return A meaningful string describing this object.
+	 */
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder("CipherSpec: ");
+		sb.append( getCipherTransformation() ).append("; keysize= ").append( getKeySize() );
+		sb.append(" bits; blocksize= ").append( getBlockSize() ).append(" bytes");
+		byte[] iv = getIV();
+		String ivLen = null;
+		if ( iv != null ) {
+			ivLen = "" + iv.length;	// Convert length to a string
+		} else {
+			ivLen = "[No IV present (not set or not required)]";
+		}
+		sb.append("; IV length = ").append( ivLen ).append(" bytes.");
+		return sb.toString();
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object other) {
+        boolean result = false;
+        if ( this == other )
+            return true;
+        if ( other == null )
+            return false;
+        if ( other instanceof CipherSpec) {
+            CipherSpec that = (CipherSpec)other;
+            result = (that.canEqual(this) &&
+                      NullSafe.equals(this.cipher_xform_, that.cipher_xform_) &&
+                      this.keySize_ == that.keySize_ &&
+                      this.blockSize_ == that.blockSize_ &&
+                        // Comparison safe from timing attacks.
+                      CryptoHelper.arrayCompare(this.iv_, that.iv_) );
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        StringBuilder sb = new StringBuilder();
+        sb.append( getCipherTransformation() );
+        sb.append( "" + getKeySize() );
+        sb.append( "" + getBlockSize() );
+        byte[] iv = getIV();
+        if ( iv != null && iv.length > 0 ) {
+            String ivStr = null;
+            try {
+                ivStr = new String(iv, "UTF-8");
+            }
+            catch(UnsupportedEncodingException ex) {
+                // Should never happen as UTF-8 encode supported by rt.jar,
+                // but it it does, just use default encoding.
+                ivStr = new String(iv);
+            }
+            sb.append( ivStr );
+        }
+        return sb.toString().hashCode();
+    }
+
+    /**
+     * Needed for correct definition of equals for general classes.
+     * (Technically not needed for 'final' classes like this class though; this
+     * will just allow it to work in the future should we decide to allow
+     * sub-classing of this class.)
+     * </p><p>
+     * See <a href="http://www.artima.com/lejava/articles/equality.html">
+     * How to write an Equality Method in Java</a>
+     * for full explanation.
+     * </p>
+     */
+    protected boolean canEqual(Object other) {
+        return (other instanceof CipherSpec);
+    }	
+	
+	/**
+	 * Split the current cipher transformation and return the requested part. 
+	 * @param component The component of the cipher transformation to return.
+	 * @return The cipher algorithm, cipher mode, or padding, as requested.
+	 */
+	private String getFromCipherXform(CipherTransformationComponent component) {
+        int part = component.ordinal();
+		String[] parts = getCipherTransformation().split("/");
+		assert parts.length == 3 : "Invalid cipher transformation: " + getCipherTransformation();	
+		return parts[part];
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherText.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherText.java.svn-base
new file mode 100644
index 0000000..47e6fc7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherText.java.svn-base
@@ -0,0 +1,873 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.crypto;
+
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.Iterator;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.EncryptionException;
+
+// CHECKME: Some of these assertions probably should be actual runtime checks
+//          with suitable exceptions to account for cases where programmers
+//          accidentally pass in byte arrays that are not really serialized
+//          CipherText objects (note: as per asPortableSerializedByteArra()).
+//          However, not sure what exception time is really suitable here.
+//          It probably should be a sub-class of RuntimeException, but
+//          IllegalArguementException doesn't really make sense here. Suggestions?
+
+/**
+ * A {@code Serializable} interface representing the result of encrypting
+ * plaintext and some additional information about the encryption algorithm,
+ * the IV (if pertinent), and an optional Message Authentication Code (MAC).
+ * </p><p>
+ * Note that while this class is {@code Serializable} in the usual Java sense,
+ * ESAPI uses {@link #asPortableSerializedByteArray()} for serialization. Not
+ * only is this serialization somewhat more compact, it is also portable
+ * across other ESAPI programming language implementations. However, Java
+ * serialization is supported in the event that one wishes to store
+ * {@code CipherText} in an {@code HttpSession} object.
+ * </p><p>
+ * Copyright © 2009 - The OWASP Foundation
+ * </p>
+ * @author kevin.w.wall at gmail.com
+ * @see PlainText
+ * @see org.owasp.esapi.Encryptor
+ * @since 2.0
+ */
+public final class CipherText implements Serializable {	
+    // NOTE: Do NOT change this in future versions, unless you are knowingly
+    //       making changes to the class that will render this class incompatible
+    //       with previously serialized objects from older versions of this class.
+	//		 If this is done, that you must provide for supporting earlier ESAPI versions.
+    //       Be wary making incompatible changes as discussed at:
+    //          http://java.sun.com/javase/6/docs/platform/serialization/spec/version.html#6678
+    //       Any incompatible change in the serialization of CipherText *must* be
+    //       reflected in the class CipherTextSerializer.
+    // This should be *same* version as in CipherTextSerializer and KeyDerivationFunction.
+	// If one changes, the other should as well to accommodate any differences.
+	//		Previous versions:	20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
+	//						    20130830 - Fix to issue #306 (release 2.1.0)
+	public  static final int cipherTextVersion = 20130830; // Format: YYYYMMDD, max is 99991231.
+		// Required by Serializable classes.
+	private static final long serialVersionUID = cipherTextVersion; // Format: YYYYMMDD
+	
+	private static final Logger logger = ESAPI.getLogger("CipherText");
+    
+    private CipherSpec cipherSpec_           = null;
+    private byte[]     raw_ciphertext_       = null;
+    private byte[]     separate_mac_         = null;
+    private long       encryption_timestamp_ = 0;
+    private int		   kdfVersion_           = KeyDerivationFunction.kdfVersion;
+    private int		   kdfPrfSelection_      = KeyDerivationFunction.getDefaultPRFSelection();
+
+    // All the various pieces that can be set, either directly or indirectly
+    // via CipherSpec.
+    private enum CipherTextFlags {
+        ALGNAME, CIPHERMODE, PADDING, KEYSIZE, BLOCKSIZE, CIPHERTEXT, INITVECTOR
+    }
+
+    // If we have everything set, we compare it to this using '==' which javac
+    // specially overloads for this.
+    private final EnumSet<CipherTextFlags> allCtFlags =
+        EnumSet.of(CipherTextFlags.ALGNAME,    CipherTextFlags.CIPHERMODE,
+                   CipherTextFlags.PADDING,    CipherTextFlags.KEYSIZE,
+                   CipherTextFlags.BLOCKSIZE,  CipherTextFlags.CIPHERTEXT,
+                   CipherTextFlags.INITVECTOR);
+    
+    // These are all the pieces we collect when passed a CipherSpec object.
+    private final EnumSet<CipherTextFlags> fromCipherSpec =
+        EnumSet.of(CipherTextFlags.ALGNAME,    CipherTextFlags.CIPHERMODE,
+                   CipherTextFlags.PADDING,    CipherTextFlags.KEYSIZE,
+                   CipherTextFlags.BLOCKSIZE);
+
+    // How much we've collected so far. We start out with having collected nothing.
+    private EnumSet<CipherTextFlags> progress = EnumSet.noneOf(CipherTextFlags.class);
+    
+    // Check if versions of KeyDerivationFunction, CipherText, and
+    // CipherTextSerializer are all the same.
+    {
+    	// Ignore error about comparing identical versions and dead code.
+    	// We expect them to be, but the point is to catch us if they aren't.
+    	assert CipherTextSerializer.cipherTextSerializerVersion == CipherText.cipherTextVersion :
+            "Versions of CipherTextSerializer and CipherText are not compatible.";
+    	assert CipherTextSerializer.cipherTextSerializerVersion == KeyDerivationFunction.kdfVersion :
+    		"Versions of CipherTextSerializer and KeyDerivationFunction are not compatible.";
+    }
+
+    ///////////////////////////  C O N S T R U C T O R S  /////////////////////////
+    
+    /**
+     * Default CTOR. Takes all the defaults from the ESAPI.properties, or
+     * default values from initial values from this class (when appropriate)
+     * when they are not set in ESAPI.properties.
+     */
+    public CipherText() {
+        cipherSpec_ = new CipherSpec(); // Uses default for everything but IV.
+        received(fromCipherSpec);
+    }
+    
+    /**
+     * Construct from a {@code CipherSpec} object. Still needs to have
+     * {@link #setCiphertext(byte[])} or {@link #setIVandCiphertext(byte[], byte[])}
+     * called to be usable.
+     * 
+     * @param cipherSpec The cipher specification to use.
+     */
+    public CipherText(final CipherSpec cipherSpec) {
+        cipherSpec_  = cipherSpec;
+        received(fromCipherSpec);
+        if ( cipherSpec.getIV() != null ) {
+            received(CipherTextFlags.INITVECTOR);
+        }
+    }
+    
+    /**
+     * Construct from a {@code CipherSpec} object and the raw ciphertext.
+     * 
+     * @param cipherSpec The cipher specification to use.
+     * @param cipherText The raw ciphertext bytes to use.
+     * @throws EncryptionException  Thrown if {@code cipherText} is null or
+     *                   empty array.
+     */
+    public CipherText(final CipherSpec cipherSpec, byte[] cipherText)
+        throws EncryptionException
+    {
+        cipherSpec_ = cipherSpec;
+        setCiphertext(cipherText);
+        received(fromCipherSpec);
+        if ( cipherSpec.getIV() != null ) {
+            received(CipherTextFlags.INITVECTOR);
+        }
+    }
+    
+    /** Create a {@code CipherText} object from what is supposed to be a
+     *  portable serialized byte array, given in network byte order, that
+     *  represents a valid, previously serialized {@code CipherText} object
+     *  using {@link #asPortableSerializedByteArray()}.
+     * @param bytes A byte array created via
+     *              {@code CipherText.asPortableSerializedByteArray()}
+     * @return A {@code CipherText} object reconstructed from the byte array.
+     * @throws EncryptionException
+     * @see #asPortableSerializedByteArray()
+     */     // DISCUSS: BTW, I detest this name. Suggestions???
+    public static CipherText fromPortableSerializedBytes(byte[] bytes)
+            throws EncryptionException
+    {
+        CipherTextSerializer cts = new CipherTextSerializer(bytes);
+        return cts.asCipherText();
+    }
+
+    /////////////////////////  P U B L I C   M E T H O D S  ////////////////////
+
+	/**
+	 * Obtain the String representing the cipher transformation used to encrypt
+	 * the plaintext. The cipher transformation represents the cipher algorithm,
+	 * the cipher mode, and the padding scheme used to do the encryption. An
+	 * example would be "AES/CBC/PKCS5Padding". See Appendix A in the
+	 * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
+	 * Java Cryptography Architecture Reference Guide</a>
+	 * for information about standard supported cipher transformation names.
+	 * <p>
+	 * The cipher transformation name is usually sufficient to be passed to
+	 * {@link javax.crypto.Cipher#getInstance(String)} to create a
+	 * <code>Cipher</code> object to decrypt the ciphertext.
+	 * 
+	 * @return The cipher transformation name used to encrypt the plaintext
+	 * 		   resulting in this ciphertext.
+	 */
+    public String getCipherTransformation() {
+        return cipherSpec_.getCipherTransformation();
+    }
+	
+	/**
+	 * Obtain the name of the cipher algorithm used for encrypting the
+	 * plaintext.
+	 * 
+	 * @return The name as the cryptographic algorithm used to perform the
+	 * 		   encryption resulting in this ciphertext.
+	 */
+    public String getCipherAlgorithm() {
+        return cipherSpec_.getCipherAlgorithm();
+    }
+	
+	/**
+	 * Retrieve the key size used with the cipher algorithm that was used to
+	 * encrypt data to produce this ciphertext.
+	 * 
+	 * @return The key size in bits. We work in bits because that's the crypto way!
+	 */
+    public int getKeySize() {
+        return cipherSpec_.getKeySize();
+    }
+	
+	/**
+	 * Retrieve the block size (in bytes!) of the cipher used for encryption.
+	 * (Note: If an IV is used, this will also be the IV length.)
+	 * 
+	 * @return The block size in bytes. (Bits, bytes! It's confusing I know. Blame
+	 * 									the cryptographers; we've just following
+	 * 									convention.)
+	 */
+    public int getBlockSize() {
+        return cipherSpec_.getBlockSize();
+    }
+	
+	/**
+	 * Get the name of the cipher mode used to encrypt some plaintext.
+	 * 
+	 * @return The name of the cipher mode used to encrypt the plaintext
+	 *         resulting in this ciphertext. E.g., "CBC" for "cipher block
+	 *         chaining", "ECB" for "electronic code book", etc.
+	 */
+    public String getCipherMode() {
+        return cipherSpec_.getCipherMode();
+    }
+	
+	/**
+	 * Get the name of the padding scheme used to encrypt some plaintext.
+	 * 
+	 * @return The name of the padding scheme used to encrypt the plaintext
+	 * 		   resulting in this ciphertext. Example: "PKCS5Padding". If no
+	 * 		   padding was used "None" is returned.
+	 */
+    public String getPaddingScheme() {
+        return cipherSpec_.getPaddingScheme();
+    }
+	
+	/**
+	 * Return the initialization vector (IV) used to encrypt the plaintext
+	 * if applicable.
+	 *  
+	 * @return	The IV is returned if the cipher mode used to encrypt the
+	 * 			plaintext was not "ECB". ECB mode does not use an IV so in
+	 * 			that case, <code>null</code> is returned.
+	 */
+    public byte[] getIV() {
+        if ( isCollected(CipherTextFlags.INITVECTOR) ) {
+            return cipherSpec_.getIV();
+        } else {
+            logger.error(Logger.SECURITY_FAILURE, "IV not set yet; unable to retrieve; returning null");
+            return null;
+        }
+    }
+	
+	/** 
+	 * Return true if the cipher mode used requires an IV. Usually this will
+	 * be true unless ECB mode (which should be avoided whenever possible) is
+	 * used.
+	 */
+    public boolean requiresIV() {
+        return cipherSpec_.requiresIV();
+    }
+	
+	/**
+	 * Get the raw ciphertext byte array resulting from encrypting some
+	 * plaintext.
+	 * 
+	 * @return A copy of the raw ciphertext as a byte array.
+	 */
+	public byte[] getRawCipherText() {
+	    if ( isCollected(CipherTextFlags.CIPHERTEXT) ) {
+	        byte[] copy = new byte[ raw_ciphertext_.length ];
+	        System.arraycopy(raw_ciphertext_, 0, copy, 0, raw_ciphertext_.length);
+	        return copy;
+	    } else {
+	        logger.error(Logger.SECURITY_FAILURE, "Raw ciphertext not set yet; unable to retrieve; returning null");
+	        return null;
+	    }
+	}
+	
+	/**
+	 * Get number of bytes in raw ciphertext. Zero is returned if ciphertext has not
+	 * yet been stored.
+	 * 
+	 * @return The number of bytes of raw ciphertext; 0 if no raw ciphertext has been stored.
+	 */
+	public int getRawCipherTextByteLength() {
+	    if ( raw_ciphertext_ != null ) {
+	        return raw_ciphertext_.length;
+	    } else {
+	        return 0;
+	    }
+	}
+
+	/**
+	 * Return a base64-encoded representation of the raw ciphertext alone. Even
+	 * in the case where an IV is used, the IV is not prepended before the
+	 * base64-encoding is performed.
+	 * <p>
+	 * If there is a need to store an encrypted value, say in a database, this
+	 * is <i>not</i> the method you should use unless you are using a <i>fixed</i>
+	 * IV or are planning on retrieving the IV and storing it somewhere separately
+	 * (e.g., a different database column). If you are <i>not</i> using a fixed IV
+	 * (which is <strong>highly</strong> discouraged), you should normally use
+	 * {@link #getEncodedIVCipherText()} instead.
+	 * </p>
+	 * @see #getEncodedIVCipherText()
+	 */
+	public String getBase64EncodedRawCipherText() {
+	    return ESAPI.encoder().encodeForBase64(getRawCipherText(),false);
+	}
+	
+	/**
+	 * Return the ciphertext as a base64-encoded <code>String</code>. If an
+	 * IV was used, the IV if first prepended to the raw ciphertext before
+	 * base64-encoding. If an IV is not used, then this method returns the same
+	 * value as {@link #getBase64EncodedRawCipherText()}.
+	 * <p>
+	 * Generally, this is the method that you should use unless you only
+	 * are using a fixed IV and a storing that IV separately, in which case
+	 * using {@link #getBase64EncodedRawCipherText()} can reduce the storage
+	 * overhead.
+	 * </p>
+	 * @return The base64-encoded ciphertext or base64-encoded IV + ciphertext.
+	 * @see #getBase64EncodedRawCipherText()
+	 */
+	public String getEncodedIVCipherText() {
+	    if ( isCollected(CipherTextFlags.INITVECTOR) && isCollected(CipherTextFlags.CIPHERTEXT) ) {
+	        // First concatenate IV + raw ciphertext
+	        byte[] iv = getIV();
+	        byte[] raw = getRawCipherText();
+	        byte[] ivPlusCipherText = new byte[ iv.length + raw.length ];
+	        System.arraycopy(iv, 0, ivPlusCipherText, 0, iv.length);
+	        System.arraycopy(raw, 0, ivPlusCipherText, iv.length, raw.length);
+	        // Then return the base64 encoded result
+	        return ESAPI.encoder().encodeForBase64(ivPlusCipherText, false);
+	    } else {
+	        logger.error(Logger.SECURITY_FAILURE, "Raw ciphertext and/or IV not set yet; unable to retrieve; returning null");
+	        return null;
+	    }
+	}
+
+	/**
+	 * Compute and store the Message Authentication Code (MAC) if the ESAPI property
+	 * {@code Encryptor.CipherText.useMAC} is set to {@code true}. If it
+	 * is, the MAC is conceptually calculated as:
+	 * <pre>
+	 * 		authKey = DerivedKey(secret_key, "authenticate")
+	 * 		HMAC-SHA1(authKey, IV + secret_key)
+	 * </pre>
+	 * where derived key is an HMacSHA1, possibly repeated multiple times.
+	 * (See {@link org.owasp.esapi.crypto.CryptoHelper#computeDerivedKey(SecretKey, int, String)}
+	 * for details.)
+	 * </p><p>
+	 * <b>Perceived Benefits</b>: There are certain cases where if an attacker
+	 * is able to change the IV. When one uses a authenticity key that is
+	 * derived from the "master" key, it also makes it possible to know when
+	 * the incorrect key was attempted to be used to decrypt the ciphertext.
+	 * </p><p>
+	 * <b>NOTE:</b> The purpose of this MAC (which is always computed by the
+	 * ESAPI reference model implementing {@code Encryptor}) is to ensure the
+	 * authenticity of the IV and ciphertext. Among other things, this prevents
+	 * an adversary from substituting the IV with one of their own choosing.
+	 * Because we don't know whether or not the recipient of this {@code CipherText}
+	 * object will want to validate the authenticity or not, the reference
+	 * implementation of {@code Encryptor} always computes it and includes it.
+	 * The recipient of the ciphertext can then choose whether or not to validate
+	 * it.
+	 * 
+	 * @param authKey The secret key that is used for proving authenticity of
+	 * 				the IV and ciphertext. This key should be derived from
+	 * 				the {@code SecretKey} passed to the
+	 * 				{@link Encryptor#encrypt(javax.crypto.SecretKey, PlainText)}
+	 *				and
+	 *				{@link Encryptor#decrypt(javax.crypto.SecretKey, CipherText)}
+	 *				methods or the "master" key when those corresponding
+	 *				encrypt / decrypt methods are used. This authenticity key
+	 *				should be the same length and for the same cipher algorithm
+	 *				as this {@code SecretKey}. The method
+	 *				{@link org.owasp.esapi.crypto.CryptoHelper#computeDerivedKey(SecretKey, int, String)}
+	 *				is a secure way to produce this derived key.
+	 */		// DISCUSS - Cryptographers David Wagner, Ian Grigg, and others suggest
+			// computing authenticity using derived key and HmacSHA1 of IV + ciphertext.
+			// However they also argue that what should be returned and treated as
+			// (i.e., stored as) ciphertext would be something like this:
+			//		len_of_raw_ciphertext + IV + raw_ciphertext + MAC
+			// TODO: Need to do something like this for custom serialization and then
+	        // document order / format so it can be used by other ESAPI implementations.
+	public void computeAndStoreMAC(SecretKey authKey) {
+	    assert !macComputed() : "Programming error: Can't store message integrity code " +
+	                            "while encrypting; computeAndStoreMAC() called multiple times.";
+	    assert collectedAll() : "Have not collected all required information to compute and store MAC.";
+	    byte[] result = computeMAC(authKey);
+	    if ( result != null ) {
+	        storeSeparateMAC(result);
+	    }
+	    // If 'result' is null, we already logged this in computeMAC().
+	}
+	
+	/**
+	 * Same as {@link #computeAndStoreMAC(SecretKey)} but this is only used by
+	 * {@code CipherTextSerializeer}. (Has package level access.)
+	 */ // CHECKME: For this to be "safe", it requires ESAPI jar to be sealed.
+	void storeSeparateMAC(byte[] macValue) {
+	    if ( !macComputed() ) {
+	        separate_mac_ = new byte[ macValue.length ];
+	        CryptoHelper.copyByteArray(macValue, separate_mac_);
+	        assert macComputed();
+	    }
+	}
+	
+	/**
+	 * Validate the message authentication code (MAC) associated with the ciphertext.
+	 * This is mostly meant to ensure that an attacker has not replaced the IV
+	 * or raw ciphertext with something arbitrary. Note however that it will
+	 * <i>not</i> detect the case where an attacker simply substitutes one
+	 * valid ciphertext with another ciphertext.
+	 * 
+	 * @param authKey The secret key that is used for proving authenticity of
+	 * 				the IV and ciphertext. This key should be derived from
+	 * 				the {@code SecretKey} passed to the
+	 * 				{@link Encryptor#encrypt(javax.crypto.SecretKey, PlainText)}
+	 *				and
+	 *				{@link Encryptor#decrypt(javax.crypto.SecretKey, CipherText)}
+	 *				methods or the "master" key when those corresponding
+	 *				encrypt / decrypt methods are used. This authenticity key
+	 *				should be the same length and for the same cipher algorithm
+	 *				as this {@code SecretKey}. The method
+	 *				{@link org.owasp.esapi.crypto.CryptoHelper#computeDerivedKey(SecretKey, int, String)}
+	 *				is a secure way to produce this derived key.
+	 * @return True if the ciphertext has not be tampered with, and false otherwise.
+	 */
+	public boolean validateMAC(SecretKey authKey) {
+	    boolean requiresMAC = ESAPI.securityConfiguration().useMACforCipherText();
+
+	    if (  requiresMAC && macComputed() ) {  // Uses MAC and it was computed
+	        // Calculate MAC from HMAC-SHA1(nonce, IV + plaintext) and
+	        // compare to stored value (separate_mac_). If same, then return true,
+	        // else return false.
+	        byte[] mac = computeMAC(authKey);
+	        assert mac.length == separate_mac_.length : "MACs are of differnt lengths. Should both be the same.";
+	        return CryptoHelper.arrayCompare(mac, separate_mac_); // Safe compare!!!
+	    } else if ( ! requiresMAC ) {           // Doesn't require a MAC
+	        return true;
+	    } else {
+	    		// This *used* to be the case (for versions 2.0 and 2.0.1) where we tried to
+	    		// accomodate the deprecated decrypt() method from ESAPI 1.4. Unfortunately,
+	    		// that was an EPIC FAIL. (See Google Issue # 306 for details.)
+	        logger.warning(Logger.SECURITY_FAILURE, "MAC may have been tampered with (e.g., length set to 0).");
+	        return false;    // Deprecated decrypt() method removed, so now return false.
+	    }
+	}
+	
+	/**
+	 * Return this {@code CipherText} object as a portable (i.e., network byte
+	 * ordered) serialized byte array. Note this is <b>not</b> the same as
+	 * returning a serialized object using Java serialization. Instead this
+	 * is a representation that all ESAPI implementations will use to pass
+	 * ciphertext between different programming language implementations.
+	 * 
+	 * @return A network byte-ordered serialized representation of this object.
+	 * @throws EncryptionException
+	 */    // DISCUSS: This method name sucks too. Suggestions???
+	public byte[] asPortableSerializedByteArray() throws EncryptionException {
+        // Check if this CipherText object is "complete", i.e., all
+        // mandatory has been collected.
+	    if ( ! collectedAll() ) {
+	        String msg = "Can't serialize this CipherText object yet as not " +
+	                     "all mandatory information has been collected";
+	        throw new EncryptionException("Can't serialize incomplete ciphertext info", msg);
+	    }
+	    
+	    // If we are supposed to be using a (separate) MAC, also make sure
+	    // that it has been computed/stored.
+	    boolean requiresMAC = ESAPI.securityConfiguration().useMACforCipherText();
+	    if (  requiresMAC && ! macComputed() ) {
+	        String msg = "Programming error: MAC is required for this cipher mode (" +
+	                     getCipherMode() + "), but MAC has not yet been " +
+	                     "computed and stored. Call the method " +
+	                     "computeAndStoreMAC(SecretKey) first before " +
+	                     "attempting serialization.";
+	        throw new EncryptionException("Can't serialize ciphertext info: Data integrity issue.",
+	                                      msg);
+	    }
+	    
+	    // OK, everything ready, so give it a shot.
+	    return new CipherTextSerializer(this).asSerializedByteArray();
+	}
+	
+    ///// Setters /////
+    /**
+     * Set the raw ciphertext.
+     * @param ciphertext    The raw ciphertext.
+     * @throws EncryptionException  Thrown if the MAC has already been computed
+     *              via {@link #computeAndStoreMAC(SecretKey)}.
+     */
+    public void setCiphertext(byte[] ciphertext)
+        throws EncryptionException
+    {
+        if ( ! macComputed() ) {
+            if ( ciphertext == null || ciphertext.length == 0 ) {
+                throw new EncryptionException("Encryption faled; no ciphertext",
+                                              "Ciphertext may not be null or 0 length!");
+            }
+            if ( isCollected(CipherTextFlags.CIPHERTEXT) ) {
+                logger.warning(Logger.SECURITY_FAILURE, "Raw ciphertext was already set; resetting.");
+            }
+            raw_ciphertext_ = new byte[ ciphertext.length ];
+            CryptoHelper.copyByteArray(ciphertext, raw_ciphertext_);
+            received(CipherTextFlags.CIPHERTEXT);
+            setEncryptionTimestamp();
+        } else {
+            String logMsg = "Programming error: Attempt to set ciphertext after MAC already computed.";
+            logger.error(Logger.SECURITY_FAILURE, logMsg);
+            throw new EncryptionException("MAC already set; cannot store new raw ciphertext", logMsg);
+        }
+    }
+    
+    /**
+     * Set the IV and raw ciphertext.
+     * @param iv            The initialization vector.
+     * @param ciphertext    The raw ciphertext.
+     * @throws EncryptionException
+     */
+    public void setIVandCiphertext(byte[] iv, byte[] ciphertext)
+        throws EncryptionException
+    {
+        if ( isCollected(CipherTextFlags.INITVECTOR) ) {
+            logger.warning(Logger.SECURITY_FAILURE, "IV was already set; resetting.");
+        }
+        if ( isCollected(CipherTextFlags.CIPHERTEXT) ) {
+            logger.warning(Logger.SECURITY_FAILURE, "Raw ciphertext was already set; resetting.");
+        }
+        if ( ! macComputed() ) {
+            if ( ciphertext == null || ciphertext.length == 0 ) {
+                throw new EncryptionException("Encryption faled; no ciphertext",
+                                              "Ciphertext may not be null or 0 length!");
+            }
+            if ( iv == null || iv.length == 0 ) {
+                if ( requiresIV() ) {
+                    throw new EncryptionException("Encryption failed -- mandatory IV missing", // DISCUSS - also log? See below.
+                                                  "Cipher mode " + getCipherMode() + " has null or empty IV");
+                }
+            } else if ( iv.length != getBlockSize() ) {
+                    throw new EncryptionException("Encryption failed -- bad parameters passed to encrypt",  // DISCUSS - also log? See below.
+                                                  "IV length does not match cipher block size of " + getBlockSize());
+            }
+            cipherSpec_.setIV(iv);
+            received(CipherTextFlags.INITVECTOR);
+            setCiphertext( ciphertext );
+        } else {
+            String logMsg = "MAC already computed from previously set IV and raw ciphertext; may not be reset -- object is immutable.";
+            logger.error(Logger.SECURITY_FAILURE, logMsg);  // Discuss: By throwing, this gets logged as warning, but it's really error! Why is an exception only a warning???
+            throw new EncryptionException("Validation of decryption failed.", logMsg);
+        }
+    }
+    
+    public int getKDFVersion() {
+    	return kdfVersion_;
+    }
+
+    public void setKDFVersion(int vers) {
+    	CryptoHelper.isValidKDFVersion(vers, false, true);
+    	kdfVersion_ = vers;
+    }
+    
+    public KeyDerivationFunction.PRF_ALGORITHMS getKDF_PRF() {
+    	return KeyDerivationFunction.convertIntToPRF(kdfPrfSelection_);
+    }
+
+    int kdfPRFAsInt() {
+    	return kdfPrfSelection_;
+    }
+    
+    public void setKDF_PRF(int prfSelection) {
+        assert prfSelection >= 0 && prfSelection <= 15 : "kdfPrf == " + prfSelection + " must be between 0 and 15.";
+    	kdfPrfSelection_ = prfSelection;
+    }
+    
+    /** Get stored time stamp representing when data was encrypted. */
+    public long getEncryptionTimestamp() {
+        return encryption_timestamp_;
+    }
+    
+    /**
+     * Set the encryption timestamp to the current system time as determined by
+     * {@code System.currentTimeMillis()}, but only if it has not been previously
+     * set. That is, this method ony has an effect the first time that it is
+     * called for this object.
+     */
+    private void setEncryptionTimestamp() {
+        // We want to skip this when it's already been set via the package
+        // level call setEncryptionTimestamp(long) done via CipherTextSerializer
+        // otherwise it gets reset to the current time. But when it's restored
+        // from a serialized CipherText object, we want to keep the original
+        // encryption timestamp.
+        if ( encryption_timestamp_ != 0 ) {
+            logger.warning(Logger.EVENT_FAILURE, "Attempt to reset non-zero " +
+                    "CipherText encryption timestamp to current time!");
+        }
+        encryption_timestamp_ = System.currentTimeMillis();
+    }
+ 
+    /**
+     * Set the encryption timestamp to the time stamp specified by the parameter.
+     * </p><p>
+     * This method is intended for use only by {@code CipherTextSerializer}.
+     * 
+     * @param timestamp The time in milliseconds since epoch time (midnight,
+     *                  January 1, 1970 GMT).
+     */ // Package level access. ESAPI jar should be sealed and signed.
+    void setEncryptionTimestamp(long timestamp) {
+        assert timestamp > 0 : "Timestamp must be greater than zero.";
+        if ( encryption_timestamp_ == 0 ) {     // Only set it if it's not yet been set.
+            logger.warning(Logger.EVENT_FAILURE, "Attempt to reset non-zero " +
+                           "CipherText encryption timestamp to " + new Date(timestamp) + "!");
+        }
+        encryption_timestamp_ = timestamp;
+    }
+    
+    /** Used in supporting {@code CipherText} serialization.
+     * @deprecated	Use {@code CipherText.cipherTextVersion} instead. Will
+     * 				disappear as of ESAPI 2.1.
+     */
+    public static long getSerialVersionUID() {
+        return CipherText.serialVersionUID;
+    }
+    
+    /** Return the separately calculated Message Authentication Code (MAC) that
+     * is computed via the {@code computeAndStoreMAC(SecretKey authKey)} method.
+     * @return The copy of the computed MAC, or {@code null} if one is not used.
+     */
+    public byte[] getSeparateMAC() {
+        if ( separate_mac_ == null ) {
+            return null;
+        }
+        byte[] copy = new byte[ separate_mac_.length ];
+        System.arraycopy(separate_mac_, 0, copy, 0, separate_mac_.length);
+        return copy;   
+    }
+    
+    /**
+     * More useful {@code toString()} method.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder( "CipherText: " );
+        String creationTime = (( getEncryptionTimestamp() == 0) ? "No timestamp available" :
+                                (new Date(getEncryptionTimestamp())).toString());
+        int n = getRawCipherTextByteLength();
+        String rawCipherText = (( n > 0 ) ? "present (" + n + " bytes)" : "absent");
+        String mac = (( separate_mac_ != null ) ? "present" : "absent");
+        sb.append("Creation time: ").append(creationTime);
+        sb.append(", raw ciphertext is ").append(rawCipherText);
+        sb.append(", MAC is ").append(mac).append("; ");
+        sb.append( cipherSpec_.toString() );
+        return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public boolean equals(Object other) {
+        boolean result = false;
+        if ( this == other )
+            return true;
+        if ( other == null )
+            return false;
+        if ( other instanceof CipherText) {
+            CipherText that = (CipherText)other;
+            if ( this.collectedAll() && that.collectedAll() ) {
+                result = (that.canEqual(this) &&
+                          this.cipherSpec_.equals(that.cipherSpec_) &&
+                            // Safe comparison, resistant to timing attacks
+                          CryptoHelper.arrayCompare(this.raw_ciphertext_, that.raw_ciphertext_) &&
+                          CryptoHelper.arrayCompare(this.separate_mac_, that.separate_mac_) &&
+                          this.encryption_timestamp_ == that.encryption_timestamp_ );
+            } else {
+                logger.warning(Logger.EVENT_FAILURE, "CipherText.equals(): Cannot compare two " +
+                               "CipherText objects that are not complete, and therefore immutable!");
+                logger.info(Logger.EVENT_FAILURE, "This CipherText: " + this.collectedAll() + ";" +
+                            "other CipherText: " + that.collectedAll());
+                logger.info(Logger.EVENT_FAILURE, "CipherText.equals(): Progress comparison: " +
+                               ((this.progress == that.progress) ? "Same" : "Different"));
+                logger.info(Logger.EVENT_FAILURE, "CipherText.equals(): Status this: " + this.progress +
+                               "; status other CipherText object: " + that.progress);
+                // CHECKME: Perhaps we should throw a RuntimeException instead???
+                return false;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public int hashCode() {
+        if ( this.collectedAll() ) {
+                logger.warning(Logger.EVENT_FAILURE, "CipherText.hashCode(): Cannot compute " +
+                               "hachCode() of incomplete CipherText object; object not immutable- " +
+                               "returning 0.");
+                // CHECKME: Throw RuntimeException instead?
+                return 0;
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append( cipherSpec_.hashCode() );
+        sb.append( encryption_timestamp_ );
+        String raw_ct = null;
+        String mac = null;
+        try {
+            raw_ct = new String(raw_ciphertext_, "UTF-8");
+                // Remember, MAC is optional even when CipherText is complete.
+            mac = new String( ((separate_mac_ != null) ? separate_mac_ : new byte[] { }), "UTF-8");
+        } catch(UnsupportedEncodingException ex) {
+            // Should never happen as UTF-8 encode supported by rt.jar,
+            // but it it does, just use default encoding.
+            raw_ct = new String(raw_ciphertext_);
+            mac = new String( ((separate_mac_ != null) ? separate_mac_ : new byte[] { }));
+        }
+        sb.append( raw_ct );
+        sb.append( mac );
+        return sb.toString().hashCode();
+    }
+
+    /**
+     * Needed for correct definition of equals for general classes.
+     * (Technically not needed for 'final' classes though like this class
+     * though; this will just allow it to work in the future should we
+     * decide to allow * sub-classing of this class.)
+     * </p><p>
+     * See {@link http://www.artima.com/lejava/articles/equality.html}
+     * for full explanation.
+     * </p>
+     */
+    protected boolean canEqual(Object other) {
+        return (other instanceof CipherText);
+    }
+
+    ////////////////////////////////////  P R I V A T E  /////////////////////////////////////////
+    
+    /**
+     * Compute a MAC, but do not store it. May set the nonce value as a
+     * side-effect.  The MAC is calculated as:
+     * <pre>
+     *      HMAC-SHA1(nonce, IV + plaintext)
+     * </pre>
+     * @param ciphertext    The ciphertext value for which the MAC is computed.
+     * @return The value for the MAC.
+     */ 
+    private byte[] computeMAC(SecretKey authKey) {
+        assert raw_ciphertext_ != null && raw_ciphertext_.length != 0 : "Raw ciphertext may not be null or empty.";
+        assert authKey != null && authKey.getEncoded().length != 0 : "Authenticity secret key may not be null or zero length.";
+        try {
+        	// IMPORTANT NOTE: The NSA review was (apparently) OK with using HmacSHA1
+        	// to calculate the MAC that ensures authenticity of the IV+ciphertext.
+        	// (Not true of calculation of the use HmacSHA1 for the KDF though.) Therefore,
+        	// we did not make this configurable. Note also that choosing an improved
+        	// MAC algorithm here would cause the overall length of the serialized ciphertext
+        	// to be just that much longer, which is probably unacceptable when encrypting
+        	// short strings.
+            SecretKey sk = new SecretKeySpec(authKey.getEncoded(), "HmacSHA1");
+            Mac mac = Mac.getInstance("HmacSHA1");
+            mac.init(sk);
+            if ( requiresIV() ) {
+                mac.update( getIV() );
+            }
+            byte[] result = mac.doFinal( getRawCipherText() );
+            return result;
+        } catch (NoSuchAlgorithmException e) {
+            logger.error(Logger.SECURITY_FAILURE, "Cannot compute MAC w/out HmacSHA1.", e);
+            return null;
+        } catch (InvalidKeyException e) {
+            logger.error(Logger.SECURITY_FAILURE, "Cannot comput MAC; invalid 'key' for HmacSHA1.", e);
+            return null;
+        }
+    }
+    
+    /**
+     * Return true if the MAC has already been computed (i.e., not null).
+     */
+    private boolean macComputed() {
+        return (separate_mac_ != null);
+    }
+
+    /**
+     * Return true if we've collected all the required pieces; otherwise false.
+     */
+    private boolean collectedAll() {
+        EnumSet<CipherTextFlags> ctFlags = null;
+        if ( requiresIV() ) {
+            ctFlags = allCtFlags;
+        } else {
+            EnumSet<CipherTextFlags> initVector = EnumSet.of(CipherTextFlags.INITVECTOR);
+            ctFlags = EnumSet.complementOf(initVector);
+        }
+        boolean result = progress.containsAll(ctFlags);  
+        return result;
+    }
+
+    /** Check if we've collected a specific flag type.
+     * @param flag  The flag type; e.g., {@code CipherTextFlags.INITVECTOR}, etc.
+     * @return  Return true if we've collected a specific flag type; otherwise false.
+     */
+    private boolean isCollected(CipherTextFlags flag) {
+        return progress.contains(flag);
+    }
+
+    /**
+     * Add the flag to the set of what we've already collected.
+     * @param flag  The flag type to be added; e.g., {@code CipherTextFlags.INITVECTOR}.
+     */
+    private void received(CipherTextFlags flag) {
+        progress.add(flag);
+    }
+    
+    /**
+     * Add all the flags from the specified set to that we've collected so far.
+     * @param ctSet A {@code EnumSet<CipherTextFlags>} containing all the flags
+     *              we wish to add.
+     */
+    private void received(EnumSet<CipherTextFlags> ctSet) {
+        Iterator<CipherTextFlags> it = ctSet.iterator();
+        while ( it.hasNext() ) {
+            received( it.next() );
+        }
+    }
+
+    /**
+     * Based on the KDF version and the selected MAC algorithm for the KDF PRF,
+     * calculate the 32-bit quantity representing these.
+     * @return	A 4-byte (octet) quantity representing the KDF version and the
+     * 			MAC algorithm used for the KDF's Pseudo-Random Function.
+     * @see <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf">Format of portable serialization of org.owasp.esapi.crypto.CipherText object (pg 2)</a>
+     */
+	public int getKDFInfo() {
+		final int unusedBit28 = 0x8000000;  // 1000000000000000000000000000
+		
+		// 		kdf version is bits 1-27, bit 28 (reserved) should be 0, and
+		//		bits 29-32 are the MAC algorithm indicating which PRF to use for the KDF.
+		int kdfVers = this.getKDFVersion();
+		assert CryptoHelper.isValidKDFVersion(kdfVers, true, false);
+		int kdfInfo = kdfVers;
+		int macAlg = kdfPRFAsInt();
+		assert macAlg >= 0 && macAlg <= 15 : "MAC algorithm indicator must be between 0 to 15 inclusion; value is: " + macAlg;
+		
+	    // Make sure bit28 is cleared. (Reserved for future use.)
+	    kdfInfo &= ~unusedBit28;
+
+	    // Set MAC algorithm bits in high (MSB) nibble.
+	    kdfInfo |= (macAlg << 28);
+
+		return kdfInfo;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextSerializer.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextSerializer.java.svn-base
new file mode 100644
index 0000000..2a6caeb
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextSerializer.java.svn-base
@@ -0,0 +1,415 @@
+package org.owasp.esapi.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.util.ByteConversionUtil;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Helper class to assist with programming language and platform independent
+ * serialization of {@link CipherText} objects. The serialization is done in
+ * network-byte order which is the same as big-endian byte order.
+ * <p>
+ * This serialization scheme is documented in
+ * <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf">
+ * <code>Format of Portable Serialization of org.owasp.esapi.crypto.CipherText Objects</code>.</a>
+ * Other serialization schemes may be desirable and could be supported (notably, RFC 5083 - Cryptographic
+ * Message Syntax (CMS) Authenticated-Enveloped-Data Content Type, or CMS' predecessor,
+ * PKCS#7 (RFC 2315)), but these serialization schemes are by comparison very complicated,
+ * and do not have extensive support for the various implementation languages which ESAPI
+ * supports. (Perhaps wishful thinking that other ESAPI implementations such as
+ * ESAPI for .NET, ESAPI for C, ESAPI for C++, etc. will all support a single, common
+ * serialization technique so they could exchange encrypted data.)
+ * 
+ * @author kevin.w.wall at gmail.com
+ *
+ */
+public class CipherTextSerializer {
+    // This should be *same* version as in CipherText & KeyDerivationFunction as
+	// these versions all need to work together.  Therefore, when one changes one
+	// one these versions, the other should be reviewed and changed as well to
+	// accommodate any differences.
+	//		Previous versions:	20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
+	//						    20130830 - Fix to issue #306 (release 2.1.0)
+	// We check that in an static initialization block (when assertions are enabled)
+	// below.
+	public  static final  int cipherTextSerializerVersion = 20130830; // Current version. Format: YYYYMMDD, max is 99991231.
+    private static final long serialVersionUID = cipherTextSerializerVersion;
+
+    private static final Logger logger = ESAPI.getLogger("CipherTextSerializer");
+    
+    private CipherText cipherText_ = null;
+    
+    // Check if versions of KeyDerivationFunction, CipherText, and
+    // CipherTextSerializer are all the same.
+    {
+    	// Ignore error about comparing identical versions and dead code.
+    	// We expect them to be, but the point is to catch us if they aren't.
+    	assert CipherTextSerializer.cipherTextSerializerVersion == CipherText.cipherTextVersion :
+            "Versions of CipherTextSerializer and CipherText are not compatible.";
+    	assert CipherTextSerializer.cipherTextSerializerVersion == KeyDerivationFunction.kdfVersion :
+    		"Versions of CipherTextSerializer and KeyDerivationFunction are not compatible.";
+    }
+    
+    public CipherTextSerializer(CipherText cipherTextObj) {
+    	if ( cipherTextObj == null ) {
+    		throw new IllegalArgumentException("CipherText object must not be null.");
+    	}
+        assert cipherTextObj != null : "CipherText object must not be null.";      
+        cipherText_ = cipherTextObj;
+    }
+    
+    /**
+     * Given byte array in network byte order (i.e., big-endian order), convert
+     * it so that a {@code CipherText} can be constructed from it.
+     * @param cipherTextSerializedBytes A serialized {@code CipherText} object
+     *          with the bytes in network byte order.
+     * @throws EncryptionException Thrown if a valid {@code CipherText} object
+     *          cannot be reconstructed from the byte array.
+     */
+    public CipherTextSerializer(byte[] cipherTextSerializedBytes)
+        throws EncryptionException /* DISCUSS: Change exception type?? */
+    {
+        cipherText_ = convertToCipherText(cipherTextSerializedBytes);
+    }
+
+    /** Return this {@code CipherText} object as a specialized, portable
+     *  serialized byte array.
+     * @return A serialization of this object. Note that this is <i>not</i> the
+     * Java serialization.
+     */
+    public byte[] asSerializedByteArray() {
+        int kdfInfo = cipherText_.getKDFInfo();
+        debug("asSerializedByteArray: kdfInfo = " + kdfInfo);
+        long timestamp = cipherText_.getEncryptionTimestamp();
+        String cipherXform = cipherText_.getCipherTransformation();
+        assert cipherText_.getKeySize() < Short.MAX_VALUE :
+                            "Key size too large. Max is " + Short.MAX_VALUE;
+        short keySize = (short) cipherText_.getKeySize();
+        assert cipherText_.getBlockSize() < Short.MAX_VALUE :
+                            "Block size too large. Max is " + Short.MAX_VALUE;
+        short blockSize = (short) cipherText_.getBlockSize();
+        byte[] iv = cipherText_.getIV();
+        assert iv.length < Short.MAX_VALUE :
+                            "IV size too large. Max is " + Short.MAX_VALUE;
+        short ivLen = (short) iv.length;
+        byte[] rawCiphertext = cipherText_.getRawCipherText();
+        int ciphertextLen = rawCiphertext.length;
+        assert ciphertextLen >= 1 : "Raw ciphertext length must be >= 1 byte.";
+        byte[] mac = cipherText_.getSeparateMAC();
+        assert mac.length < Short.MAX_VALUE :
+                            "MAC length too large. Max is " + Short.MAX_VALUE;
+        short macLen = (short) mac.length;
+        
+        byte[] serializedObj = computeSerialization(kdfInfo,
+                                                    timestamp,
+                                                    cipherXform,
+                                                    keySize,
+                                                    blockSize,
+                                                    ivLen,
+                                                    iv,
+                                                    ciphertextLen,
+                                                    rawCiphertext,
+                                                    macLen,
+                                                    mac
+                                                   );
+        
+        return serializedObj;
+    }
+    
+    /**
+     * Return the actual {@code CipherText} object.
+     * @return The {@code CipherText} object that we are serializing.
+     */
+    public CipherText asCipherText() {
+    	assert cipherText_ != null;
+        return cipherText_;
+    }
+      
+    /**
+     * Take all the individual elements that make of the serialized ciphertext
+     * format and put them in order and return them as a byte array.
+     * @param kdfInfo	Info about the KDF... which PRF and the KDF version {@link #asCipherText()}.
+     * @param timestamp	Timestamp when the data was encrypted. Intended to help
+     * 					facilitate key change operations and nothing more. If it is meaningless,
+     * 					then the expectations are just that the recipient should ignore it. Mostly
+     * 					intended when encrypted data is kept long term over a period of many
+     * 					key change operations.
+     * @param cipherXform	Details of how the ciphertext was encrypted. The format used
+     * 						is the same as used by {@code javax.crypto.Cipher}, namely,
+     * 						"cipherAlg/cipherMode/paddingScheme".
+     * @param keySize	The key size used for encrypting. Intended for cipher algorithms
+     * 					supporting multiple key sizes such as triple DES (DESede) or
+     * 					Blowfish.
+     * @param blockSize	The cipher block size. Intended to support cipher algorithms
+     * 					that support variable block sizes, such as Rijndael.
+     * @param ivLen		The length of the IV.
+     * @param iv		The actual IV (initialization vector) bytes.
+     * @param ciphertextLen	The length of the raw ciphertext.
+     * @param rawCiphertext	The actual raw ciphertext itself
+     * @param macLen	The length of the MAC (message authentication code).
+     * @param mac		The MAC itself.
+     * @return	A byte array representing the serialized ciphertext.
+     */
+    private byte[] computeSerialization(int kdfInfo, long timestamp,
+                                        String cipherXform, short keySize,
+                                        short blockSize,
+                                        short ivLen, byte[] iv,
+                                        int ciphertextLen, byte[] rawCiphertext,
+                                        short macLen, byte[] mac
+                                       )
+    {
+        debug("computeSerialization: kdfInfo = " + kdfInfo);
+        debug("computeSerialization: timestamp = " + new Date(timestamp));
+        debug("computeSerialization: cipherXform = " + cipherXform);
+        debug("computeSerialization: keySize = " + keySize);
+        debug("computeSerialization: blockSize = " + blockSize);
+        debug("computeSerialization: ivLen = " + ivLen);
+        debug("computeSerialization: ciphertextLen = " + ciphertextLen);
+        debug("computeSerialization: macLen = " + macLen);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeInt(baos, kdfInfo);
+        writeLong(baos, timestamp);
+        String[] parts = cipherXform.split("/");
+        assert parts.length == 3 : "Malformed cipher transformation";
+        writeString(baos, cipherXform); // Size of string is prepended to string
+        writeShort(baos, keySize);
+        writeShort(baos, blockSize);
+        writeShort(baos, ivLen);
+        if ( ivLen > 0 ) baos.write(iv, 0, iv.length);
+        writeInt(baos, ciphertextLen);
+        baos.write(rawCiphertext, 0, rawCiphertext.length);
+        writeShort(baos, macLen);
+        if ( macLen > 0 ) baos.write(mac, 0, mac.length);
+        return baos.toByteArray();
+    }
+    
+    // All strings are written as UTF-8 encoded byte streams with the
+    // length prepended before it as a short. The prepended length is
+    // more for the benefit of languages like C so they can pre-allocate
+    // char arrays without worrying about buffer overflows.
+    private void writeString(ByteArrayOutputStream baos, String str) {
+        byte[] bytes;
+        try {
+            assert str != null && str.length() > 0;
+            bytes = str.getBytes("UTF8");
+            assert bytes.length < Short.MAX_VALUE : "writeString: String exceeds max length";
+            writeShort(baos, (short)bytes.length);
+            baos.write(bytes, 0, bytes.length);
+        } catch (UnsupportedEncodingException e) {
+            // Should never happen. UTF8 is built into the rt.jar. We don't use native encoding as
+            // a fall-back because that simply is not guaranteed to be portable across Java
+            // platforms and could cause really bizarre errors way downstream.
+            logger.error(Logger.EVENT_FAILURE, "Ignoring caught UnsupportedEncodingException " +
+                           "converting string to UTF8 encoding. Results suspect. Corrupt rt.jar????");
+        }
+    }
+    
+    private String readString(ByteArrayInputStream bais, short sz)
+        throws NullPointerException, IOException
+    {
+        byte[] bytes = new byte[sz];
+        int ret = bais.read(bytes, 0, sz);
+        assert ret == sz : "readString: Failed to read " + sz + " bytes.";
+        return new String(bytes, "UTF8");
+    }
+    
+    private void writeShort(ByteArrayOutputStream baos, short s) {
+        byte[] shortAsByteArray = ByteConversionUtil.fromShort(s);
+        assert shortAsByteArray.length == 2;
+        baos.write(shortAsByteArray, 0, 2);
+    }
+    
+    private short readShort(ByteArrayInputStream bais)
+        throws NullPointerException, IndexOutOfBoundsException
+    {
+        byte[] shortAsByteArray = new byte[2];
+        int ret = bais.read(shortAsByteArray, 0, 2);
+        assert ret == 2 : "readShort: Failed to read 2 bytes.";
+        return ByteConversionUtil.toShort(shortAsByteArray);
+    }
+    
+    private void writeInt(ByteArrayOutputStream baos, int i) {
+        byte[] intAsByteArray = ByteConversionUtil.fromInt(i);
+        baos.write(intAsByteArray, 0, 4);
+    }
+    
+    private int readInt(ByteArrayInputStream bais)
+        throws NullPointerException, IndexOutOfBoundsException
+    {
+        byte[] intAsByteArray = new byte[4];
+        int ret = bais.read(intAsByteArray, 0, 4);
+        assert ret == 4 : "readInt: Failed to read 4 bytes.";
+        return ByteConversionUtil.toInt(intAsByteArray);
+    }
+    
+    private void writeLong(ByteArrayOutputStream baos, long l) {
+        byte[] longAsByteArray = ByteConversionUtil.fromLong(l);
+        assert longAsByteArray.length == 8;
+        baos.write(longAsByteArray, 0, 8);
+    }
+    
+    private long readLong(ByteArrayInputStream bais)
+        throws NullPointerException, IndexOutOfBoundsException
+    {
+        byte[] longAsByteArray = new byte[8];
+        int ret = bais.read(longAsByteArray, 0, 8);
+        assert ret == 8 : "readLong: Failed to read 8 bytes.";
+        return ByteConversionUtil.toLong(longAsByteArray);
+    }
+    
+    /** Convert the serialized ciphertext byte array to a {@code CipherText}
+     * object.
+     * @param cipherTextSerializedBytes	The serialized ciphertext as a byte array.
+     * @return The corresponding {@code CipherText} object.
+     * @throws EncryptionException	Thrown if the byte array data is corrupt or
+     * 				there are version mismatches, etc.
+     */
+    private CipherText convertToCipherText(byte[] cipherTextSerializedBytes)
+        throws EncryptionException
+    {
+        try {
+        	assert cipherTextSerializedBytes != null : "cipherTextSerializedBytes cannot be null.";
+        	assert cipherTextSerializedBytes.length > 0 : "cipherTextSerializedBytes must be > 0 in length.";
+            ByteArrayInputStream bais = new ByteArrayInputStream(cipherTextSerializedBytes);
+            int kdfInfo = readInt(bais);
+            debug("kdfInfo: " + kdfInfo);
+            int kdfPrf = (kdfInfo >>> 28);
+            debug("kdfPrf: " + kdfPrf);
+            assert kdfPrf >= 0 && kdfPrf <= 15 : "kdfPrf == " + kdfPrf + " must be between 0 and 15.";
+            int kdfVers = ( kdfInfo & 0x07ffffff);
+
+            // First do a quick sanity check on the argument. Previously this was an assertion.
+            if ( ! CryptoHelper.isValidKDFVersion(kdfVers, false, false) ) {
+            	// TODO: Clean up. Use StringBuilder. Good enough for now.
+            	String logMsg = "KDF version read from serialized ciphertext (" + kdfVers + ") is out of range. " +
+            				    "Valid range for KDF version is [" + KeyDerivationFunction.originalVersion + ", " +
+            				    "99991231].";
+            	// This should never happen under actual circumstances (barring programming errors; but we've
+            	// tested the code, right?), so it is likely an attempted attack. Thus don't get the originator
+            	// of the suspect ciphertext too much info. They ought to know what they sent anyhow.
+            	throw new EncryptionException("Version info from serialized ciphertext not in valid range.",
+            				 "Likely tampering with KDF version on serialized ciphertext." + logMsg);
+            }
+            
+            debug("convertToCipherText: kdfPrf = " + kdfPrf + ", kdfVers = " + kdfVers);
+            if ( ! versionIsCompatible( kdfVers) ) {
+            	throw new EncryptionException("This version of ESAPI does is not compatible with the version of ESAPI that encrypted your data.",
+            			"KDF version " + kdfVers + " from serialized ciphertext not compatibile with current KDF version of " + 
+            			KeyDerivationFunction.kdfVersion);
+            }
+            long timestamp = readLong(bais);
+            debug("convertToCipherText: timestamp = " + new Date(timestamp));
+            short strSize = readShort(bais);
+            debug("convertToCipherText: length of cipherXform = " + strSize);
+            String cipherXform = readString(bais, strSize);
+            debug("convertToCipherText: cipherXform = " + cipherXform);
+            String[] parts = cipherXform.split("/");
+            assert parts.length == 3 : "Malformed cipher transformation";
+            String cipherMode = parts[1];
+            if ( ! CryptoHelper.isAllowedCipherMode(cipherMode) ) {
+                String msg = "Cipher mode " + cipherMode + " is not an allowed cipher mode";
+                throw new EncryptionException(msg, msg);
+            }
+            short keySize = readShort(bais);
+            debug("convertToCipherText: keySize = " + keySize);
+            short blockSize = readShort(bais);
+            debug("convertToCipherText: blockSize = " + blockSize);
+            short ivLen = readShort(bais);
+            debug("convertToCipherText: ivLen = " + ivLen);
+            byte[] iv = null;
+            if ( ivLen > 0 ) {
+                iv = new byte[ivLen];
+                bais.read(iv, 0, iv.length);
+            }
+            int ciphertextLen = readInt(bais);
+            debug("convertToCipherText: ciphertextLen = " + ciphertextLen);
+            assert ciphertextLen > 0 : "convertToCipherText: Invalid cipher text length";
+            byte[] rawCiphertext = new byte[ciphertextLen];
+            bais.read(rawCiphertext, 0, rawCiphertext.length);
+            short macLen = readShort(bais);
+            debug("convertToCipherText: macLen = " + macLen);
+            byte[] mac = null;
+            if ( macLen > 0 ) {
+                mac = new byte[macLen];
+                bais.read(mac, 0, mac.length);
+            }
+
+            CipherSpec cipherSpec = new CipherSpec(cipherXform, keySize);
+            cipherSpec.setBlockSize(blockSize);
+            cipherSpec.setIV(iv);
+            debug("convertToCipherText: CipherSpec: " + cipherSpec);
+            CipherText ct = new CipherText(cipherSpec);
+            if ( ! (ivLen > 0 && ct.requiresIV()) ) {
+                    throw new EncryptionException("convertToCipherText: Mismatch between IV length and cipher mode.",
+                    						      "Possible tampering of serialized ciphertext?");
+            }
+            ct.setCiphertext(rawCiphertext);
+              // Set this *AFTER* setting raw ciphertext because setCiphertext()
+              // method also sets encryption time.
+            ct.setEncryptionTimestamp(timestamp);
+            if ( macLen > 0 ) {
+                ct.storeSeparateMAC(mac);
+            }
+            	// Fixed in ESAPI crypto version 20130839. Previously is didn't really matter
+            	// because there was only one version (20110203) and it defaulted to that
+            	// version, which was the current version. But we don't want that as now there
+            	// are two versions and we could be decrypting data encrypted using the previous
+            	// version.
+            ct.setKDF_PRF(kdfPrf);
+            ct.setKDFVersion(kdfVers);
+            return ct;
+        } catch(EncryptionException ex) {
+            throw new EncryptionException("Cannot deserialize byte array into CipherText object",
+                                          "Cannot deserialize byte array into CipherText object",
+                                          ex);
+        } catch (IOException e) {
+            throw new EncryptionException("Cannot deserialize byte array into CipherText object",
+                    "Cannot deserialize byte array into CipherText object", e);
+        }
+    }
+
+    /** Check to see if we can support the KSF version that was extracted from
+     *  the serialized ciphertext. In particular, we assume that if we have a
+     *  newer version of KDF than we can support it as we assume that we have
+     *  built in backward compatibility.
+     *  
+     *  At this point (ESAPI 2.1.0, KDF version 20130830), all we need to check
+     *  if the version is either the current version or the previous version as
+     *  both versions work the same. This checking may get more complicated in
+     *  the future.
+     *  
+     *  @param readKdfVers	The version information extracted from the serialized
+     *  					ciphertext.
+     */
+    private static boolean versionIsCompatible(int readKdfVers) {
+    	// We've checked elsewhere for this, so assertion is OK here.
+    	assert readKdfVers > 0 : "Extracted KDF version is negative!";
+    	
+		switch ( readKdfVers ) {
+		case KeyDerivationFunction.originalVersion:		// First version
+			return true;
+		// Add new versions here; hard coding is OK...
+		// case YYYYMMDD:
+		//	return true;
+		case KeyDerivationFunction.kdfVersion:			// Current version
+			return true;
+		default:
+			return false;
+		}
+	}
+
+	private void debug(String msg) {
+        if ( logger.isDebugEnabled() ) {
+            logger.debug(Logger.EVENT_SUCCESS, msg);
+        }
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoDiscoverer.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoDiscoverer.java.svn-base
new file mode 100644
index 0000000..e42c8cf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoDiscoverer.java.svn-base
@@ -0,0 +1,75 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Chris Schmidt (chris.schmidt at owasp.org)
+ * @created 2010
+ */
+package org.owasp.esapi.crypto;
+
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class CryptoDiscoverer {
+	private static String EOL = System.getProperty("line.separator", "\n");
+	
+    public static void main(String... args) {
+        String provider = ".*";
+        String algorithm = ".*";
+
+        if ( args.length > 0 ) {
+            if ( args[0].equals( "--help" ) ) {
+                usage();
+                System.exit(0);
+            }
+
+            List<String> argList = Arrays.asList( args );
+
+            int argIdx = argList.indexOf("--provider");
+            if ( argIdx > -1 && argList.size() > (argIdx + 1) ) {
+                provider = argList.get(argIdx+1);
+            }
+
+            argIdx = argList.indexOf("--algorithm");
+            if ( argIdx > -1 && argList.size() > (argIdx + 1) ) {
+                algorithm = argList.get(argIdx+1);
+            }
+        }
+
+        Pattern providerPattern = Pattern.compile(provider);
+        Pattern algorithmPattern = Pattern.compile(algorithm);
+
+        System.out.println("Searching for Providers Matching: " + provider );
+        System.out.println("Searching for Algorithms Matching: " + algorithm );
+        System.out.println();
+
+        for (Provider p : Security.getProviders()) {
+            if ( providerPattern.matcher(p.getName()).matches()) {
+                System.out.println("Provider: " + p.getName());
+                for (Provider.Service service : p.getServices()) {
+                    if ( algorithmPattern.matcher(service.getAlgorithm()).matches()) {
+                        System.out.println("\tAlgorithm: " + service.getAlgorithm());
+                    }
+                }
+            }
+        }
+    }
+
+    private static void usage() {
+        System.out.println("CryptoDiscoverer - Discover or Query for available Crypto Providers and Algorithms");
+        System.out.println(EOL + "\t--help\t\t\t\t\tShows this message" + EOL +
+        		"\t--provider <regex>\t\tSearch for particular Provider" + EOL +
+                "\t--algorithm <regex>\t\tSearch for a particular Algorithm" + EOL + EOL);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoHelper.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoHelper.java.svn-base
new file mode 100644
index 0000000..024150b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoHelper.java.svn-base
@@ -0,0 +1,395 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.crypto;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Class to provide some convenience methods for encryption, decryption, etc.
+ * </p><p>
+ * All the cryptographic operations use the default cryptographic properties;
+ * e.g., default cipher transformation, default key size, default IV type (where
+ * applicable), etc.
+ * 
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class CryptoHelper {
+	
+	private static final Logger logger = ESAPI.getLogger("CryptoHelper");
+
+	// TODO: Also consider supplying implementation of RFC 2898 / PKCS#5 PBKDF2
+	//		 in this file as well??? Maybe save for ESAPI 2.1 or 3.0.
+	/**
+	 * Generate a random secret key appropriate to the specified cipher algorithm
+	 * and key size.
+	 * @param alg	The cipher algorithm or cipher transformation. (If the latter is
+	 * 				passed, the cipher algorithm is determined from it.) Cannot be
+	 * 				null or empty.
+	 * @param keySize	The key size, in bits.
+	 * @return	A random {@code SecretKey} is returned.
+	 * @throws EncryptionException Thrown if cannot create secret key conforming to
+	 * 				requested algorithm with requested size. Typically this is caused by
+	 * 				specifying an unavailable algorithm or invalid key size.
+	 */
+	public static SecretKey generateSecretKey(String alg, int keySize)
+		throws EncryptionException
+	{
+		assert alg != null : "Algorithm must not be null.";			// NPE if null and assertions disabled.
+		assert !alg.equals("") : "Algorithm must not be empty";	// NoSuchAlgorithmExeption if empty & assertions disabled.
+		assert keySize > 0 : "Key size must be positive.";	// Usually should be even multiple of 8, but not strictly required by alg.
+		// Don't use CipherSpec here to get algorithm as this may cause assertion
+		// to fail (when enabled) if only algorithm name is passed to us.
+		String[] cipherSpec = alg.split("/");
+		String cipherAlg = cipherSpec[0];
+		try {
+		    // Special case for things like PBEWithMD5AndDES or PBEWithSHA1AndDESede.
+		    // In such cases, the key generator should only request an instance of "PBE".
+		    if ( cipherAlg.toUpperCase().startsWith("PBEWITH") ) {
+		        cipherAlg = "PBE";
+		    }
+			KeyGenerator kgen =
+				KeyGenerator.getInstance( cipherAlg );
+			kgen.init(keySize);
+			return kgen.generateKey();
+		} catch (NoSuchAlgorithmException e) {
+			throw new EncryptionException("Failed to generate random secret key",
+					"Invalid algorithm. Failed to generate secret key for " + alg + " with size of " + keySize + " bits.", e);
+		} catch (InvalidParameterException e) {
+			throw new EncryptionException("Failed to generate random secret key - invalid key size specified.",
+					"Invalid key size. Failed to generate secret key for " + alg + " with size of " + keySize + " bits.", e);
+		}
+	}
+
+	/**
+	 * The method is ESAPI's Key Derivation Function (KDF) that computes a
+	 * derived key from the {@code keyDerivationKey} for either
+	 * encryption / decryption or for authentication.
+	 * <p>
+	 * <b>CAUTION:</b> If this algorithm for computing derived keys from the
+	 * key derivation key is <i>ever</i> changed, we risk breaking backward compatibility of being
+	 * able to decrypt data previously encrypted with earlier / different versions
+	 * of this method. Therefore, do not change this unless you are 100% certain that
+	 * what you are doing will NOT change either of the derived keys for
+	 * ANY "key derivation key" AT ALL!!!
+	 * <p>
+	 * <b>NOTE:</b> This method is generally not intended to be called separately.
+	 * It is used by ESAPI's reference crypto implementation class {@code JavaEncryptor}
+	 * and might be useful for someone implementing their own replacement class, but
+	 * generally it is not something that is useful to application client code.
+	 * 
+	 * @param keyDerivationKey  A key used as an input to a key derivation function
+	 *                          to derive other keys. This is the key that generally
+	 *                          is created using some key generation mechanism such as
+	 *                          {@link #generateSecretKey(String, int)}. The
+	 *                          "input" key from which the other keys are derived.
+	 * 							The derived key will have the same algorithm type
+	 * 							as this key.
+	 * @param keySize		The cipher's key size (in bits) for the {@code keyDerivationKey}.
+	 * 						Must have a minimum size of 56 bits and be an integral multiple of 8-bits.
+	 * 						<b>Note:</b> The derived key will have the same size as this.
+	 * @param purpose		The purpose for the derived key. Must be either the
+	 * 						string "encryption" or "authenticity". Use "encryption" for
+     *                      creating a derived key to use for confidentiality, and "authenticity"
+     *                      for a derived key to use with a MAC to ensure message authenticity.
+	 * @return				The derived {@code SecretKey} to be used according
+	 * 						to the specified purpose. Note that this serves the same purpose
+	 * 						as "label" in section 5.1 of NIST SP 800-108.
+	 * @throws NoSuchAlgorithmException		The {@code keyDerivationKey} has an unsupported
+	 * 						encryption algorithm or no current JCE provider supports
+	 * 						"HmacSHA1".
+	 * @throws EncryptionException		If "UTF-8" is not supported as an encoding, then
+	 * 						this is thrown with the original {@code UnsupportedEncodingException}
+	 * 						as the cause. (NOTE: This should never happen as "UTF-8" is supposed to
+	 * 						be a common encoding supported by all Java implementations. Support
+	 * 					    for it is usually in rt.jar.)
+	 * @throws InvalidKeyException 	Likely indicates a coding error. Should not happen.
+	 * @throws EncryptionException  Throw for some precondition violations.
+	 * @deprecated Use{@code KeyDerivationFunction} instead. This method will be removed as of
+	 * 			   ESAPI release 2.1 so if you are using this, please change your code.
+	 */
+	public static SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, String purpose)
+			throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException
+	{
+        // These really should be turned into actual runtime checks and an
+        // IllegalArgumentException should be thrown if they are violated.
+		assert keyDerivationKey != null : "Key derivation key cannot be null.";
+			// We would choose a larger minimum key size, but we want to be
+			// able to accept DES for legacy encryption needs.
+		assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
+		assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
+		assert purpose != null;
+		assert purpose.equals("encryption") || purpose.equals("authenticity") :
+			"Purpose must be \"encryption\" or \"authenticity\".";
+
+		// DISCUSS: Should we use HmacSHA1 (what we were using) or the HMAC defined by
+		//			Encryptor.KDF.PRF instead? Either way, this is not compatible with
+		//			previous ESAPI versions. JavaEncryptor doesn't use this any longer.
+		KeyDerivationFunction kdf = new KeyDerivationFunction(
+											KeyDerivationFunction.PRF_ALGORITHMS.HmacSHA1);
+		return kdf.computeDerivedKey(keyDerivationKey, keySize, purpose);
+	}
+
+	/**
+	 * Return true if specified cipher mode is one of those specified in the
+	 * {@code ESAPI.properties} file that supports both confidentiality
+	 * <b>and</b> authenticity (i.e., a "combined cipher mode" as NIST refers
+	 * to it).
+	 * @param cipherMode The specified cipher mode to be used for the encryption
+	 *                   or decryption operation.
+	 * @return true if the specified cipher mode is in the comma-separated list
+	 *         of cipher modes supporting both confidentiality and authenticity;
+	 *         otherwise false.
+	 * @see org.owasp.esapi.SecurityConfiguration#getCombinedCipherModes()
+	 */
+	public static boolean isCombinedCipherMode(String cipherMode)
+	{
+	    assert cipherMode != null : "Cipher mode may not be null";
+	    assert ! cipherMode.equals("") : "Cipher mode may not be empty string";
+	    List<String> combinedCipherModes =
+	        ESAPI.securityConfiguration().getCombinedCipherModes();
+	    return combinedCipherModes.contains( cipherMode );
+	}
+
+	/**
+     * Return true if specified cipher mode is one that may be used for
+     * encryption / decryption operations via {@link org.owasp.esapi.Encryptor}.
+     * @param cipherMode The specified cipher mode to be used for the encryption
+     *                   or decryption operation.
+     * @return true if the specified cipher mode is in the comma-separated list
+     *         of cipher modes supporting both confidentiality and authenticity;
+     *         otherwise false.
+     * @see #isCombinedCipherMode(String)
+     * @see org.owasp.esapi.SecurityConfiguration#getCombinedCipherModes()
+     * @see org.owasp.esapi.SecurityConfiguration#getAdditionalAllowedCipherModes()
+     */
+	public static boolean isAllowedCipherMode(String cipherMode)
+	{
+	    if ( isCombinedCipherMode(cipherMode) ) {
+	        return true;
+	    }
+	    List<String> extraCipherModes =
+	        ESAPI.securityConfiguration().getAdditionalAllowedCipherModes();
+	    return extraCipherModes.contains( cipherMode );
+	}
+	
+    /**
+     * Check to see if a Message Authentication Code (MAC) is required
+     * for a given {@code CipherText} object and the current ESAPI.property
+     * settings. A MAC is considered "required" if the specified
+     * {@code CipherText} was not encrypted by one of the preferred
+     * "combined" cipher modes (e.g., CCM or GCM) and the setting of the
+     * current ESAPI properties for the property
+     * {@code Encryptor.CipherText.useMAC} is set to {@code true}. (Normally,
+     * the setting for {@code Encryptor.CipherText.useMAC} should be set to
+     * {@code true} unless FIPS 140-2 compliance is required. See
+     * <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html">
+     * User Guide for Symmetric Encryption in ESAPI 2.0</a> and the section
+     * on using ESAPI with FIPS for further details.
+     *
+     * @param ct    The specified {@code CipherText} object to check to see if
+     *              it requires a MAC.
+     * @return      True if a MAC is required, false if it is not required.
+     */
+    public static boolean isMACRequired(CipherText ct) {
+        boolean preferredCipherMode =
+            CryptoHelper.isCombinedCipherMode( ct.getCipherMode() );
+        boolean wantsMAC = ESAPI.securityConfiguration().useMACforCipherText();
+
+        // The preferred "combined" cipher modes such as CCM, GCM, etc. do
+        // not require a MAC as a MAC would be superfluous and just require
+        // additional computing time.
+        return ( !preferredCipherMode && wantsMAC );
+    }
+	
+    /**
+     * If a Message Authentication Code (MAC) is required for the specified
+     * {@code CipherText} object, then attempt to validate the MAC that
+     * should be embedded within the {@code CipherText} object by using a
+     * derived key based on the specified {@code SecretKey}.
+     *
+     * @param sk    The {@code SecretKey} used to derived a key to check
+     *              the authenticity via the MAC.
+     * @param ct    The {@code CipherText} that we are checking for a
+     *              valid MAC. 
+     *
+     * @return  True is returned if a MAC is required and it is valid as
+     *          verified using a key derived from the specified
+     *          {@code SecretKey} or a MAC is not required. False is returned
+     *          otherwise.
+     */
+    public static boolean isCipherTextMACvalid(SecretKey sk, CipherText ct)
+    {
+        if ( CryptoHelper.isMACRequired( ct ) ) {
+            try {
+                SecretKey authKey = CryptoHelper.computeDerivedKey( sk, ct.getKeySize(), "authenticity");
+                boolean validMAC = ct.validateMAC( authKey );
+                return validMAC;
+            } catch (Exception ex) {
+                // Error on side of security. If this fails and can't verify MAC
+                // assume it is invalid. Note that CipherText.toString() does not
+                // print the actual ciphertext.
+                logger.warning(Logger.SECURITY_FAILURE, "Unable to validate MAC for ciphertext " + ct, ex);
+                return false;
+            }
+        }
+        return true;
+    }
+    
+	/**
+	 * Overwrite a byte array with a specified byte. This is frequently done
+	 * to a plaintext byte array so the sensitive data is not lying around
+	 * exposed in memory.
+	 * @param bytes	The byte array to be overwritten.
+	 * @param x The byte array {@code bytes} is overwritten with this byte.
+	 */
+	public static void overwrite(byte[] bytes, byte x)
+	{
+		Arrays.fill(bytes, x);
+	}
+	
+	/**
+	 * Overwrite a byte array with the byte containing '*'. That is, call
+	 * <pre>
+	 * 		overwrite(bytes, (byte)'*');
+	 * </pre>
+	 * @param bytes The byte array to be overwritten.
+	 */
+	public static void overwrite(byte[] bytes)
+	{
+		overwrite(bytes, (byte)'*');
+	}
+	
+	// These provide for a bit more type safety when copying bytes around.
+	/**
+	 * Same as {@code System.arraycopy(src, 0, dest, 0, length)}.
+	 * 
+     * @param      src      the source array.
+     * @param      dest     the destination array.
+     * @param      length   the number of array elements to be copied.
+     * @exception  IndexOutOfBoundsException  if copying would cause
+     *               access of data outside array bounds.
+     * @exception  NullPointerException if either <code>src</code> or
+     *               <code>dest</code> is <code>null</code>.
+	 */
+	public static void copyByteArray(final byte[] src, byte[] dest, int length)
+	{
+		System.arraycopy(src, 0, dest, 0, length);
+	}
+	
+	/**
+	 * Same as {@code copyByteArray(src, dest, src.length)}.
+     * @param      src      the source array.
+     * @param      dest     the destination array.
+     * @exception  IndexOutOfBoundsException  if copying would cause
+     *               access of data outside array bounds.
+     * @exception  NullPointerException if either <code>src</code> or
+     *               <code>dest</code> is <code>null</code>.
+	 */
+	public static void copyByteArray(final byte[] src, byte[] dest)
+	{
+		copyByteArray(src, dest, src.length);
+	}
+	
+	/**
+	 * A "safe" array comparison that is not vulnerable to side-channel
+	 * "timing attacks". All comparisons of non-null, equal length bytes should
+	 * take same amount of time. We use this for cryptographic comparisons.
+	 * 
+	 * @param b1   A byte array to compare.
+	 * @param b2   A second byte array to compare.
+	 * @return     {@code true} if both byte arrays are null or if both byte
+	 *             arrays are identical or have the same value; otherwise
+	 *             {@code false} is returned.
+	 */
+	public static boolean arrayCompare(byte[] b1, byte[] b2) {
+	    if ( b1 == b2 ) {
+	        return true;
+	    }
+	    if ( b1 == null || b2 == null ) {
+	        return (b1 == b2);
+	    }
+	    if ( b1.length != b2.length ) {
+	        return false;
+	    }
+	    
+	    int result = 0;
+	    // Make sure to go through ALL the bytes. We use the fact that if
+	    // you XOR any bit stream with itself the result will be all 0 bits,
+	    // which in turn yields 0 for the result.
+	    for(int i = 0; i < b1.length; i++) {
+	        // XOR the 2 current bytes and then OR with the outstanding result.
+	        result |= (b1[i] ^ b2[i]);
+	    }
+	    return (result == 0) ? true : false;
+	}
+  
+	/**
+	 * Is this particular KDF version number one that is sane? For that, we
+	 * just make sure it is inbounds of the valid range which is:
+	 * <pre>
+	 *     [20110203, 99991231]
+	 * </pre>
+	 * @param kdfVers	KDF version # that we are checking. Generally this is
+	 * 				extracted from the serialized {@code CipherText}.
+	 * @param restrictToCurrent	If this is set, we do an additional check
+	 *				to see if the KDF version is a later version than the
+	 *				one that this current ESAPI version supports.
+	 * @param throwIfError	Instead of returning {@code false} in the case of
+	 * 				an error, throw an {@code IllegalArgumentException}
+	 * @return	True if in range, false otherwise (except if {@code throwIfError}
+	 * 			is true.}
+	 */
+	public static boolean isValidKDFVersion(int kdfVers, boolean restrictToCurrent,
+											boolean throwIfError)
+				throws IllegalArgumentException
+	{
+		boolean ret = true;
+		
+		if ( kdfVers < KeyDerivationFunction.originalVersion || kdfVers > 99991231 ) {
+			ret = false;
+		} else if ( restrictToCurrent ) {
+			ret = ( kdfVers <= KeyDerivationFunction.kdfVersion );
+		}
+		if ( ret ) {
+			return ret;				// True
+		} else {					// False, so throw or not.
+			logger.warning(Logger.SECURITY_FAILURE, "Possible data tampering. Encountered invalid KDF version #. " +
+						   ( throwIfError ? "Throwing IllegalArgumentException" : "" ));
+			if ( throwIfError ) {	
+				throw new IllegalArgumentException("Version (" + kdfVers + ") invalid. " +
+					"Must be date in format of YYYYMMDD between " + KeyDerivationFunction.originalVersion + "and 99991231.");
+			}
+		}
+		return false;
+	}
+	
+    /**
+     * Prevent public, no-argument CTOR from being auto-generated. Public CTOR
+     * is not needed as all methods are static.
+     */
+    private CryptoHelper() {
+    	;	// Prevent default CTOR from being auto-created.
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoToken.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoToken.java.svn-base
new file mode 100644
index 0000000..4276079
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/CryptoToken.java.svn-base
@@ -0,0 +1,722 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2010 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and
+ * accept the LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @created 2010
+ */
+package org.owasp.esapi.crypto;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.ValidationException;
+
+///// IMPORTANT NOTE: Never print / log attribute *values* as they
+/////                 may be sensitive. Also, do not log the CryptoToken
+/////				  itself as it generally is used as an authentication token.
+
+// OPEN ISSUE: Assertions vs. IllegalArgumentException must be resolved. I prefer
+//             assertions for preconditions, which is more in line with Design-by-Contract
+//             and Eiffel. There are a few places where assertions are not appropriate
+//             because if they are not disabled (they are not by default), they could cause
+//             incorrect behavior (e.g., setting the expiration time to something in the
+//             past, etc.)
+
+/**
+ * Compute a cryptographically secure, encrypted token containing
+ * optional name/value pairs. The cryptographic token is computed
+ * like this:
+ * <pre>
+ *     username;expiration_time;[<attr1>;<attr2>;...;<attrN>;]
+ * </pre>
+ * where
+ * <i>username</i> is a user account name. Defaults to <anonymous> if
+ * not set and it is always converted to lower case as per the rules of the
+ * default locale. (Note this lower case conversion is consistent with the
+ * default reference implementation of ESAPI's {@code User} interface.)
+ * <br>
+ * <i>expiration_time</i> is time (in milliseconds) after which the encrypted
+ * token is considered invalid (i.e., expired). The time is stored as
+ * milliseconds since midnight, January 1, 1970 UTC, and optional attributes
+ * <br>
+ *   <i><attr1></i>;<i><attr2></i>;...<i><attrN></i>;
+ * <br>
+ * are optional semicolon (';') separated name/value pairs, where each
+ * name/value pair has the form:
+ * <pre>
+ *         name=[value]        (value may be empty, but not null)
+ * </pre>
+ * The attribute value may contain any value. However, values containing
+ * either '=' or ';' will be quoted using '\'. Likewise, values containing '\'
+ * will also be quoted using '\'. Hence if original name/value pair were
+ *             name=ab=xy\;
+ *         this would be represented as
+ *             name=ab\=xy\\\;
+ * To ensure things are "safe" (from a security perspective), attribute
+ * <i>names</i> must conform the the Java regular expression
+ * <pre>
+ *          [A-Za-z0-9_\.-]+
+ * </pre>
+ * The attribute <i>value</i> on the other hand, may be any valid string. (That
+ * is, the value is not checked, so beware!)
+ * <p>
+ * This entire semicolon-separated string is then encrypted via one of the 
+ * {@code Encryptor.encrypt()} methods and then base64-encoded, serialized
+ * IV + ciphertext + MAC representation as determined by
+ * {@code CipherTextasPortableSerializedByteArray()} is used as the
+ * resulting cryptographic token.
+ * <p>
+ * The attributes are sorted by attribute name and the attribute names
+ * must be unique. There are some restrictions on the attribute names.
+ * (See the {@link #setAttribute(String, String)} method for details.)
+ *
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class CryptoToken {
+    /** Represents an anonymous user. */
+    public static final String ANONYMOUS_USER = "<anonymous>";
+    
+    // Default expiration time
+    private static final long DEFAULT_EXP_TIME = 5 * 60 * 1000;  // 5 min == 300 milliseconds
+    private static final String DELIM = ";";                     // field delimiter
+    private static final char DELIM_CHAR = ';';                  // field delim as a char
+    private static final char QUOTE_CHAR = '\\';                 // char used to quote delimiters, '=' and itself.
+    
+        // OPEN ISSUE: Should we make these 2 regex's properties in ESAPI.properties???
+    private static final String ATTR_NAME_REGEX = "[A-Za-z0-9_.-]+"; // One or more alphanumeric, underscore, periods, or hyphens.
+    private static final String USERNAME_REGEX = "[a-z][a-z0-9_. at -]*";
+    
+    private static Logger logger = ESAPI.getLogger("CryptoToken");
+
+    private String username = ANONYMOUS_USER;        // Default user name if not set. Always lower case.
+    private long expirationTime = 0L;
+        // This probably needed be sorted. A HashMap would do as well.
+        // But this might make debugging a bit easier, so why not?
+    private TreeMap<String, String> attributes = new TreeMap<String,String>();
+    private transient SecretKey secretKey = null;
+    private Pattern attrNameRegex = Pattern.compile(ATTR_NAME_REGEX);
+    private Pattern userNameRegex = Pattern.compile(USERNAME_REGEX);
+    
+    /**
+     * Create a cryptographic token using default secret key from the
+     * <b>ESAPI.properties</b> property <b>Encryptor.MasterKey</b>. 
+     */
+    public CryptoToken() {
+        secretKey = getDefaultSecretKey(
+                            ESAPI.securityConfiguration().getEncryptionAlgorithm()
+                        );
+        long now = System.currentTimeMillis();
+        expirationTime = now + DEFAULT_EXP_TIME;
+    }
+
+    // Create using specified SecretKey
+    /**
+     * Create a cryptographic token using specified {@code SecretKey}.
+     * 
+     * @param skey  The specified {@code SecretKey} to use to encrypt the token.
+     */
+    public CryptoToken(SecretKey skey) {
+        assert skey != null : "SecretKey may not be null.";
+        secretKey = skey;
+        long now = System.currentTimeMillis();
+        expirationTime = now + DEFAULT_EXP_TIME;
+    }
+
+    /** 
+     * Create using previously encrypted token encrypted with default secret
+     * key from <b>ESAPI.properties</b>.
+     * @param token A previously encrypted token returned by one of the
+     *              {@code getToken()} or {@code updateToken()} methods. The
+     *              token <i>must</i> have been previously encrypted using the
+     *              using default secret key from the <b>ESAPI.properties</b>
+     *              property <b>Encryptor.MasterKey</b>.
+     * @throws EncryptionException  Thrown if they are any problems while decrypting
+     *                              the token using the default secret key from
+     *                              <b>ESAPI.properties</b> or if the decrypted
+     *                              token is not properly formatted.
+     */
+    public CryptoToken(String token) throws EncryptionException {
+        secretKey = getDefaultSecretKey(
+                ESAPI.securityConfiguration().getEncryptionAlgorithm()
+            );
+        try {
+            decryptToken(secretKey, token);
+        } catch (EncodingException e) {
+            throw new EncryptionException("Decryption of token failed. Token improperly encoded or encrypted with different key.",
+                                          "Can't decrypt token because not correctly encoded or encrypted with different key.", e);
+        }
+        assert username != null : "Programming error: Decrypted token found username null.";
+        assert expirationTime > 0 : "Programming error: Decrypted token found expirationTime <= 0.";
+    }
+
+    /** 
+     * Create cryptographic token using previously encrypted token that was
+     * encrypted with specified secret key.
+     * 
+     * @param token A previously encrypted token returned by one of the
+     *              {@code getToken()} or {@code updateToken()} methods.
+     * @throws EncryptionException  Thrown if they are any problems while decrypting
+     *                              the token using the default secret key from
+     *                              <b>ESAPI.properties</b> or if the decrypted
+     *                              token is not properly formatted.
+     */
+    // token is a previously encrypted token (i.e., CryptoToken.getToken())
+    // with different SecretKey other than the one in ESAPI.properties
+    public CryptoToken(SecretKey skey, String token) throws EncryptionException {
+        assert skey != null : "SecretKey may not be null.";
+        assert token != null : "Token may not be null";
+        secretKey = skey;
+        try {
+            decryptToken(secretKey, token);
+        } catch (EncodingException e) {
+            throw new EncryptionException("Decryption of token failed. Token improperly encoded.",
+                                          "Can't decrypt token because not correctly encoded.", e);
+        }
+        assert username != null : "Programming error: Decrypted token found username null.";
+        assert expirationTime > 0 : "Programming error: Decrypted token found expirationTime <= 0.";
+    }
+
+    /**
+     * Retrieve the user account name associated with this {@code CryptoToken}
+     * object.
+     * @return  The user account name. The string represented by
+     *          {@link #ANONYMOUS_USER} is returned if
+     *          {@link #setUserAccountName(String)} was never called.
+     */
+    public String getUserAccountName() {
+        return ( (username != null) ? username : ANONYMOUS_USER );
+    }
+    
+    /**
+     * Set the user account name associated with this cryptographic token
+     * object. The user account name is converted to lower case.
+     * @param userAccountName   The user account name.
+     * @throws ValidationException  Thrown if user account name is not valid, i.e.,
+     *                              if it doesn't conform to the regular expression
+     *                              given by "[a-z][a-z0-9_. at -]*". (Note that the
+     *                              parameter {@code userAccountName} is first converted
+     *                              to lower case before checked against the regular
+     *                              expression.)
+     */
+    public void setUserAccountName(String userAccountName) throws ValidationException {
+        assert userAccountName != null : "User account name may not be null.";
+        
+        // Converting to lower case first allows a simpler regex.
+        String userAcct = userAccountName.toLowerCase();
+        
+        // Check to make sure that attribute name is valid as per our regex.
+        Matcher userNameChecker = userNameRegex.matcher(userAcct);
+        if ( userNameChecker.matches() ) {
+            username = userAcct;
+        } else {
+            throw new ValidationException("Invalid user account name encountered.",
+                                          "User account name " + userAccountName +
+                                              " does not match regex " +
+                                              USERNAME_REGEX + " after conversion to lowercase.");
+        }
+    }
+
+    /** Check if token has expired yet.
+     * @return  True if token has expired; false otherwise.
+     */
+    public boolean isExpired() {
+        return System.currentTimeMillis() > expirationTime;
+    }
+    
+    /**
+     * Set expiration time to expire in 'interval' seconds (NOT milliseconds).
+     * @param intervalSecs  Number of seconds in the future from current date/time
+     *                  	to set expiration. Must be positive.
+     */
+    public void setExpiration(int intervalSecs) throws IllegalArgumentException
+    {
+        int intervalMillis = intervalSecs * 1000;   // Need to convert secs to millisec.
+        
+        // Don't want to use assertion here, because if they are disabled,
+        // this would result in setting the expiration time prior to the
+        // current time, hence it would already be expired.
+        if ( intervalMillis <= 0) {
+            throw new IllegalArgumentException("intervalSecs argument, converted to millisecs, must be > 0.");
+        }
+        // Check for arithmetic overflow here. In reality, this condition
+        // should never happen, but we want to avoid it--even theoretically--
+        // since otherwise, it could have security implications.
+        long now = System.currentTimeMillis();
+        preAdd(now, intervalMillis);
+        expirationTime = now + intervalMillis;
+    }
+    
+    /**
+     * Set expiration time for a specific date/time.
+     * @param expirationDate    The date/time at which the token will fail. Must
+     *                          be after the current date/time.
+     * @throws IllegalArgumentException Thrown if the parameter is null.
+     */
+    public void setExpiration(Date expirationDate) throws IllegalArgumentException
+    {
+        if ( expirationDate == null ) {
+            throw new IllegalArgumentException("expirationDate may not be null.");
+        }
+        long curTime = System.currentTimeMillis();
+        long expTime = expirationDate.getTime();
+        if ( expTime <= curTime ) {
+            throw new IllegalArgumentException("Expiration date must be after current date/time.");
+        }        
+        expirationTime = expTime;
+    }
+    
+    /**
+     * Return the expiration time in milliseconds since epoch time (midnight,
+     * January 1, 1970 UTC).
+     * @return  The current expiration time.
+     */
+    public long getExpiration() {
+        assert expirationTime > 0L : "Programming error: Expiration time <= 0";
+        return expirationTime;
+    }
+    
+    /**
+     * Return the expiration time as a {@code Date}.
+     * @return The {@code Date} object representing the expiration time.
+     */
+    public Date getExpirationDate() {
+        return new Date( getExpiration() );
+    }
+
+    /**
+     * Set a name/value pair as an attribute.
+     * @param name  The attribute name
+     * @param value The attribute value
+     * @throws ValidationException  Thrown if the attribute name is not properly
+     *                              formed. That is, the attribute name does not
+     *                              match the regular expression "[A-Za-z0-9_.-]+".
+     */
+    public void setAttribute(String name, String value) throws ValidationException {
+        if ( name == null || name.length() == 0 ) {
+            // CHECKME: Should this be an IllegalArgumentException instead? I
+            // would prefer an assertion here and state this as a precondition
+            // in the Javadoc.
+            throw new ValidationException("Null or empty attribute NAME encountered",
+                                          "Attribute NAMES may not be null or empty string.");
+        }
+        if ( value == null ) {
+            // CHECKME: Should this be an IllegalArgumentException instead? I
+            // would prefer an assertion here and state this as a precondition
+            // in the Javadoc.
+            throw new ValidationException("Null attribute VALUE encountered for attr name " + name,
+                                          "Attribute VALUE may not be null; attr name: " + name);            
+        }
+        // NOTE: OTOH, it *is* VALID if the _value_ is empty! Null values cause too much trouble
+        // to make it worth the effort of getting it to work consistently.
+
+        // Check to make sure that attribute name is valid as per our regex.
+        Matcher attrNameChecker = attrNameRegex.matcher(name);
+        if ( attrNameChecker.matches() ) {
+            attributes.put(name, value);
+        } else {
+            throw new ValidationException("Invalid attribute name encountered.",
+                                          "Attribute name " + name + " does not match regex " +
+                                          ATTR_NAME_REGEX);
+        }
+    }
+    
+    /**
+     * Add the specified collection of attributes to the current attributes.
+     * If there are duplicate attributes specified, they will replace any
+     * existing ones.
+     * 
+     * @param attrs Name/value pairs of attributes to add or replace the existing
+     *              attributes. Map must be non-null, but may be empty.
+     * @throws ValidationException Thrown if one of the keys in the specified
+     *                             parameter {@code attrs} is not a valid name.
+     *                             That is, all attribute names must match the regular
+     *                             expression ""[A-Za-z0-9_.-]+".
+     * @see #setAttribute(String, String)
+     */
+    public void addAttributes(final Map<String, String> attrs) throws ValidationException {
+        // CHECKME: Assertion vs. IllegalArgumentException
+        assert attrs != null : "Attribute map may not be null.";
+        Set< Entry<String,String> > keyValueSet = attrs.entrySet();
+        Iterator<Entry<String, String>> it = keyValueSet.iterator();
+        while( it.hasNext() ) {
+            Map.Entry<String,String> entry = it.next();
+            String key = entry.getKey();
+            String value = entry.getValue();
+            setAttribute(key, value);
+        }
+        return;
+    }
+    
+    /**
+     * Retrieve the attribute with the specified name.
+     * @param name  The attribute name.
+     * @return  The value associated with the attribute name. If attribute is not
+     *          set, then {@code null} is returned.
+     */
+    public String getAttribute(String name) {
+        return attributes.get(name);
+    }
+    
+    /**
+     * Retrieve a {@code Map} that is a clone of all the attributes. A <i>copy</i>
+     * is returned so that the attributes in {@code CrytpToken} are unaffected
+     * by alterations made the returned {@code Map}. (Otherwise, multi-threaded code
+     * could get trick.
+     * 
+     * @return  A {@code Map} of all the attributes.
+     * @see #getAttribute(String)
+     */
+    @SuppressWarnings("unchecked")
+    public Map<String, String> getAttributes() {
+        // Unfortunately, this requires a cast, which requires us to supress warnings.
+        return (Map<String, String>) attributes.clone();
+    }
+    
+    /**
+     * Removes all the attributes (if any) associated with this token. Note
+     * that this does not clear / reset the user account name or expiration time.
+     */
+    public void clearAttributes() {
+        attributes.clear();
+    }
+
+    /**
+     * Return the new encrypted token as a base64-encoded string, encrypted with
+     * the specified {@code SecretKey} which may be a different key than what the
+     * token was originally encrypted with. E.g.,
+     * <pre>
+     *   Alice:
+     *      SecretKey aliceSecretKey = ...; // Shared with Bob
+     *      CryptoToken cryptoToken = new CryptoToken(skey1);
+     *      cryptoToken.setUserAccountName("kwwall");
+     *      cryptoToken.setAttribute("role", "admin");
+     *      cryptoToken.setAttribute("state", "Ohio");
+     *      String token = cryptoToken.getToken(); // Encrypted with skey1
+     *      // send token to Bob ...
+     *  --------------------------------------------------------------------
+     *  Bob:
+     *      ...
+     *      SecretKey aliceSecretKey = ...  // Shared with Alice
+     *      SecretKey bobSecretKey = ...;   // Shared with Carol
+     *      CryptoToken cryptoToken = new CryptoToken(aliceSecretKey, tokenFromAlice);
+     *      
+     *      // Re-encrypt for Carol using my (Bob's) key...
+     *      String tokenForCarol = cryptoToken.getToken(bobSecretKey);
+     *      // send tokenForCarol to Carol ...
+     *      // use token ourselves
+     *  --------------------------------------------------------------------
+     *  Carol:
+     *      ...
+     *      SecretKey bobSecretKey = ...;   // Shared with Bob.
+     *      CryptoToken cryptoToken = new CryptoToken(bobSecretKey, tokenFromBob);
+     *      if ( ! cryptoToken.isExpired() ) {
+     *          String userName = cryptoToken.getUserAccountName();
+     *          String roleName = cryptoToken.getAttribute("role");
+     *          if ( roleName != null && roleName.equalsIgnoreCase("admin") ) {
+     *              // grant admin access...
+     *              ...
+     *          }
+     *      }
+     *      ...
+     * </pre>
+     * @param skey  The specified key to (re)encrypt the token.
+     * @return The newly encrypted token.
+     */
+    public String getToken(SecretKey skey) throws EncryptionException {
+        return createEncryptedToken(skey);
+    }
+    
+    /**
+     * Update the (current) expiration time by adding the specified number of
+     * seconds to it and then re-encrypting with the current {@code SecretKey}
+     * that was used to construct this object.
+     * 
+     * @param additionalSecs    The additional number of seconds to add to the
+     *                          current expiration time. This number must be
+     *                          >= 0 or otherwise an {@code IllegalArgumentException}
+     *                          is thrown.
+     * @return  The re-encrypted token with the updated expiration time is returned.
+     * @throws  IllegalArgumentException    Thrown if parameter {@code additionalSecs}
+     *                                      is less than 0.
+     * @throws  EncryptionException         Thrown if the encryption fails.
+     * @throws ValidationException          Thrown if the token will have already expired
+     *                                      even after adding the specified number of
+     *                                      additional seconds.
+     * @throws  ArithmeticException         If additional seconds is large enough such
+     *                                      that it would cause an arithmetic overflow
+     *                                      with a long (the current expiration time)
+     *                                      when added to the {@code additionalSecs}
+     *                                      parameter.
+     */
+    public String updateToken(int additionalSecs) throws EncryptionException, ValidationException {
+        if ( additionalSecs < 0) {
+            throw new IllegalArgumentException("additionalSecs argument must be >= 0.");
+        }
+        
+        // Avoid integer overflow. This could happen if one first calls
+        // setExpiration(Date) with a date far into the future. We want
+        // to avoid overflows as they might lead to security vulnerabilities.
+        long curExpTime = getExpiration();
+        preAdd(curExpTime, additionalSecs * 1000);
+            // Note: Can't use setExpiration(int) here was this needs a
+            //       'long'. Could convert to Date first, and use
+            //       setExpiration(Date) but that hardly seems worth the trouble.
+        expirationTime = curExpTime + (additionalSecs * 1000);
+        
+        if ( isExpired() ) {
+            // Too bad there is no ProcrastinationException ;-)
+            expirationTime = curExpTime;    // Restore the original value (which still may
+                                            // be expired.
+            throw new ValidationException("Token timed out.",
+                    "Cryptographic token not increased to sufficient value to prevent timeout.");
+            
+        }
+            // Don't change anything else (user acct name, attributes, skey, etc.)
+        return getToken();
+    }
+
+    /**
+     * Return the new encrypted token as a base64-encoded string, encrypted with
+     * the specified {@code SecretKey} with which this object was constructed.
+     * 
+     * @return The newly encrypted token.
+     * @see #getToken(SecretKey)
+     */
+    public String getToken() throws EncryptionException {
+        return createEncryptedToken(secretKey);
+    }
+   
+    // Create the actual encrypted token based on the specified SecretKey.
+    // This method will ensure that the decrypted token always ends with an
+    // unquoted delimiter.
+    private String createEncryptedToken(SecretKey skey) throws EncryptionException {
+        StringBuilder sb = new StringBuilder( getUserAccountName() + DELIM);
+        // CHECKME: Should we check here to see if token has already expired
+        //          and refuse to encrypt it (by throwing exception) if it has???
+        //          If so, then updateToken() should also be revisited.
+        sb.append( getExpiration() ).append( DELIM );
+        sb.append( getQuotedAttributes() );
+        
+        Encryptor encryptor = ESAPI.encryptor();
+        CipherText ct = encryptor.encrypt(skey, new PlainText( sb.toString() ) );
+        String b64 =
+            ESAPI.encoder().encodeForBase64(ct.asPortableSerializedByteArray(),
+                                            false);
+        return b64;
+    }
+    
+    // Return a string of all the attributes, properly quoted. This is used in
+    // creating the encrypted token. Note that this method ensures that the
+    // quoted attribute string always ends with an (quoted) delimiter.
+    private String getQuotedAttributes() {
+        StringBuilder sb = new StringBuilder();
+        Set< Entry<String,String> > keyValueSet = attributes.entrySet();
+        Iterator<Entry<String, String>> it = keyValueSet.iterator();
+        while( it.hasNext() ) {
+            Map.Entry<String,String> entry = it.next();
+            String key = entry.getKey();
+            String value = entry.getValue();
+            // Because attribute values may be confidential, we don't want to log them!
+            logger.debug(Logger.EVENT_UNSPECIFIED, "   " + key + " -> <not shown>");
+            sb.append(key + "=" + quoteAttributeValue( value ) + DELIM);
+        }
+        return sb.toString();
+    }
+    
+    // Do NOT define a toString() method as there may be sensitive
+    // information contained in the attribute names. If we absolutely
+    // need this, then only return the username and expiration time, and
+    // _maybe_ the attribute names, but not there values. And obviously,
+    // we NEVER want to include the SecretKey should we decide to do this.
+    /*
+     * public String toString() { return null; }
+     */
+    
+    
+    // Quote any special characters in value.
+    private static String quoteAttributeValue(String value) {
+        assert value != null : "Program error: Value should not be null."; // Empty is OK.
+        StringBuilder sb = new StringBuilder();
+        char[] charArray = value.toCharArray();
+        for( int i = 0; i < charArray.length; i++ ) {
+            char c = charArray[i];
+            if ( c == QUOTE_CHAR || c == '=' || c == DELIM_CHAR ) {
+                sb.append(QUOTE_CHAR).append(c);
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+    
+    // Parse the possibly quoted value and return the unquoted value.
+    private static String parseQuotedValue(String quotedValue) {
+        StringBuilder sb = new StringBuilder();
+        char[] charArray = quotedValue.toCharArray();
+        for( int i = 0; i < charArray.length; i++ ) {
+            char c = charArray[i];
+            if ( c == QUOTE_CHAR ) {
+                i++;    // Skip past quote character.
+                sb.append( charArray[i] );
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+    
+    /*
+     * Decrypt the encrypted token and parse it into the individual components.
+     * The string should always end with a semicolon (;) even when there are
+     * no attributes set.
+     * <p>
+     * Example of how quoted string might look:
+     * <pre>
+     *                            v              v  v            v     v
+     *  kwwall;1291183520293;abc=x\=yx;xyz=;efg=a\;a\;;bbb=quotes\\tuff\;;
+              |             |         |    |          |                  |
+     *
+     * </pre>
+     */
+    private void decryptToken(SecretKey skey, String b64token) throws EncryptionException, EncodingException {
+        byte[] token = null;
+        try {
+            token = ESAPI.encoder().decodeFromBase64(b64token);
+        } catch (IOException e) {
+            // CHECKME: Not clear if we should log the actual token itself. It's encrypted,
+            //          but could be arbitrarily long, especially since it is not valid
+            //          encoding. OTOH, it may help debugging as sometimes it may be a simple
+            //          case like someone failing to apply some other type of encoding
+            //          consistently (e.g., URL encoding), in which case logging this should
+            //          make this pretty obvious once a few of these are logged.
+            throw new EncodingException("Invalid base64 encoding.",
+                                          "Invalid base64 encoding. Encrypted token was: " + b64token);
+        }
+        CipherText ct = CipherText.fromPortableSerializedBytes(token);
+        Encryptor encryptor = ESAPI.encryptor();
+        PlainText pt = encryptor.decrypt(skey, ct);
+        String str = pt.toString();
+        assert str.endsWith(DELIM) : "Programming error: Expecting decrypted token to end with delim char, " + DELIM_CHAR;
+        char[] charArray = str.toCharArray();
+        int prevPos = -1;                // Position of previous unquoted delimiter.
+        int fieldNo = 0;
+        ArrayList<Object> fields = new ArrayList<Object>();
+        int lastPos = charArray.length;
+        for ( int curPos = 0; curPos < lastPos; curPos++ ) {
+            boolean quoted = false;
+            char curChar = charArray[curPos];
+            if ( curChar == QUOTE_CHAR ) {
+                // Found a case where we have quoted character. We need to skip
+                // over this and set the current character to the next character.
+                curPos++;
+                if ( curChar != lastPos ) {
+                    curChar = charArray[ curPos + 1 ];
+                    quoted = true;
+                } else {
+                    // Last position will always be a delimiter character that
+                    // should be treated as unquoted.
+                    curChar = DELIM_CHAR;
+                }
+            }
+            if ( curChar == DELIM_CHAR && !quoted ) {
+                // We found an actual (unquoted) field delimiter.
+                String record = str.substring(prevPos + 1, curPos);
+                fields.add( record );
+                fieldNo++;
+                prevPos = curPos;
+            } 
+        }
+        
+        Object[] objArray = fields.toArray();
+        assert fieldNo == objArray.length : "Program error: Mismatch of delimited field count.";
+        logger.debug(Logger.EVENT_UNSPECIFIED, "Found " + objArray.length + " fields.");
+        assert objArray.length >= 2 : "Missing mandatory fields from decrypted token (username &/or expiration time).";
+        username = ((String)(objArray[0])).toLowerCase();
+        String expTime = (String)objArray[1];
+        expirationTime = Long.parseLong(expTime);
+        
+        for( int i = 2; i < objArray.length; i++ ) {
+            String nvpair = (String)objArray[i];
+            int equalsAt = nvpair.indexOf("=");
+            if ( equalsAt == -1 ) {
+                throw new EncryptionException("Invalid attribute encountered in decrypted token.",
+                        "Malformed attribute name/value pair (" + nvpair + ") found in decrypted token.");
+            }
+            String name = nvpair.substring(0, equalsAt);
+            String quotedValue = nvpair.substring(equalsAt + 1);
+            String value = parseQuotedValue( quotedValue );
+            // Because attribute values may be confidential, we don't want to log them!
+            logger.debug(Logger.EVENT_UNSPECIFIED, "Attribute[" + i + "]: name=" + name + ", value=<not shown>");
+
+            // Check to make sure that attribute name is valid as per our regex.
+            Matcher attrNameChecker = attrNameRegex.matcher(name);
+            if ( attrNameChecker.matches() ) {
+                attributes.put(name, value);
+            } else {
+                throw new EncryptionException("Invalid attribute name encountered in decrypted token.",
+                                              "Invalid attribute name encountered in decrypted token; " +
+                                              "attribute name " + name + " does not match regex " +
+                                              ATTR_NAME_REGEX);
+            }
+            attributes.put(name, value);
+        }
+        return;
+    }
+    
+    private SecretKey getDefaultSecretKey(String encryptAlgorithm) {
+        assert encryptAlgorithm != null : "Encryption algorithm cannot be null";
+        byte[] skey = ESAPI.securityConfiguration().getMasterKey();
+        assert skey != null : "Can't obtain master key, Encryptor.MasterKey";
+        assert skey.length >= 7 :
+                        "Encryptor.MasterKey must be at least 7 bytes. " +
+                        "Length is: " + skey.length + " bytes.";
+        // Set up secretKeySpec for use for symmetric encryption and decryption,
+        // and set up the public/private keys for asymmetric encryption /
+        // decryption.
+        return new SecretKeySpec(skey, encryptAlgorithm );
+    }
+    
+    // Check precondition to see if addition of two operands will result in
+    // arithmetic overflow. Note that the operands are of two different
+    // integral types. I.e., check to see if:
+    //      long result = leftLongValue + rightIntValue
+    // would cause arithmetic overflow.
+    // Note: We know that as we use it here, leftLongValue will always be > 0,
+    //       so arithmetic underflow should never be possible, but we check for
+    //       it anyhow.
+    // Package level access to allow this to be used by other classes in this package.
+    static final void preAdd(long leftLongValue, int rightIntValue) throws ArithmeticException {
+        if ( rightIntValue > 0 && ( (leftLongValue + rightIntValue) < leftLongValue) ) {
+            throw new ArithmeticException("Arithmetic overflow for addition.");
+        }
+        if ( rightIntValue < 0 && ( (leftLongValue + rightIntValue) > leftLongValue) ) {
+            throw new ArithmeticException("Arithmetic underflow for addition.");
+        }
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/KeyDerivationFunction.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/KeyDerivationFunction.java.svn-base
new file mode 100644
index 0000000..17635ce
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/KeyDerivationFunction.java.svn-base
@@ -0,0 +1,479 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2011 - The OWASP Foundation
+ */
+package org.owasp.esapi.crypto;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.ConfigurationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.DefaultSecurityConfiguration;
+import org.owasp.esapi.util.ByteConversionUtil;
+
+/**
+ * This class implements a Key Derivation Function (KDF) and supporting methods.
+ * A KDF is a function with which an input key (called the Key Derivation Key,
+ * or KDK) and other input data are used to securely generate (i.e., derive)
+ * keying material that can be employed by cryptographic algorithms.
+ * <p>
+ * <b>Acknowledgments</b>:
+ * ESAPI's KDF is patterned after suggestions first made by cryptographer
+ * Dr. David A. Wagner and later extended to follow KDF in counter mode
+ * as specified by section 5.1 of NIST SP 800-108. Jeffrey Walton and the NSA
+ * also made valuable suggestions regarding the modeling of the method,
+ * {@link #computeDerivedKey(SecretKey, int, String)}.
+ *
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class KeyDerivationFunction {
+	/**
+	 * Used to support backward compatibility. {@code kdfVersion} is used as the
+	 * version for the serialized encrypted ciphertext on all the "encrypt"
+	 * operations. This static field should be the same as
+	 * {@link CipherText#cipherTextVersion} and
+	 * {@link CipherTextSerializer#cipherTextSerializerVersion} to make sure
+	 * that these classes are all kept in-sync in order to support backward
+	 * compatibility of previously encrypted data.
+	 * <pre>
+	 * Previous versions:	20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
+	 *					    20130830 - Fix to issue #306 (release 2.1.0)
+	 * </pre>
+	 * @see CipherTextSerializer#asSerializedByteArray()
+	 * @see CipherText#asPortableSerializedByteArray()
+	 * @see CipherText#fromPortableSerializedBytes(byte[])
+	 */
+	public  static final int  originalVersion  = 20110203;	 // First version. Do not change. EVER!
+	public  static final int  kdfVersion       = 20130830;   // Current version. Format: YYYYMMDD, max is 99991231.
+	private static final long serialVersionUID = kdfVersion; // Format: YYYYMMDD
+	
+    // Pseudo-random function algorithms suitable for NIST KDF in counter mode.
+	// Note that HmacMD5 is intentionally omitted here!!!
+    public enum PRF_ALGORITHMS {
+    		// SHA-1, 160-bits
+        HmacSHA1(0, 160, "HmacSHA1"),
+        	// SHA-2 candidates, 256-, 384-, and 512-bits
+        HmacSHA256(1, 256, "HmacSHA256"),
+        HmacSHA384(2, 384, "HmacSHA384"),
+        HmacSHA512(3, 512, "HmacSHA512");
+        	// Reserved for SHA-3 winner, 224-, 256-, 384-, and 512-bits
+        	// Names not yet known. Will use standard JCE alg names here.
+        	//
+        	// E.g., might be something like
+        	//		HmacSHA3_224(4, 224, "HmacSHA3-224"),
+        	//		HmacSHA3_256(5, 256, "HmacSHA3-256"),
+        	//		HmacSHA3_384(6, 384, "HmacSHA3-385"),
+        	//		HmacSHA3_512(7, 512, "HmacSHA3-512");
+        // Reserved for future use -- values 8 through 15
+        //  Most likely these might be some other strong contenders that
+        //  were are based on HMACs from the NIST SHA-3 finalists.
+        
+        private final byte value;	// Value stored in serialized encrypted data to represent PRF
+        private final short bits;
+        private final String algName;
+        
+        PRF_ALGORITHMS(int value, int bits, String algName) {
+        	this.value = (byte) value;
+        	this.bits  = (short) bits;
+        	this.algName = algName;
+        }
+        
+        public byte getValue() { return value; }
+        public short getBits() { return bits; }
+        public String getAlgName() { return algName; }
+    }
+
+	private static final Logger logger = ESAPI.getLogger("KeyDerivationFunction");
+
+	private String prfAlg_ = null;
+	private int version_ = kdfVersion;
+	private String context_ = "";
+
+    // Check if versions of KeyDerivationFunction, CipherText, and
+    // CipherTextSerializer are all the same.
+    {
+    	// Ignore error about comparing identical versions and dead code.
+    	// We expect them to be, but the point is to catch us if they aren't.
+    	assert CipherTextSerializer.cipherTextSerializerVersion == CipherText.cipherTextVersion :
+            "Versions of CipherTextSerializer and CipherText are not compatible.";
+    	assert CipherTextSerializer.cipherTextSerializerVersion == KeyDerivationFunction.kdfVersion :
+    		"Versions of CipherTextSerializer and KeyDerivationFunction are not compatible.";
+    }
+    
+	/**
+	 * Construct a {@code KeyDerivationFunction}.
+	 * @param prfAlg	Specifies a supported algorithm.
+	 */
+	public KeyDerivationFunction(KeyDerivationFunction.PRF_ALGORITHMS prfAlg) {
+		this.prfAlg_ = prfAlg.getAlgName();
+	}
+
+	/**
+	 * Construct a {@code KeyDerivationFunction} based on the
+	 * <b>ESAPI.property</b> property, {@code Encryptor.KDF.PRF}.
+	 */
+	public KeyDerivationFunction() {			
+		String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		if ( ! KeyDerivationFunction.isValidPRF(prfName) ) {
+    		throw new ConfigurationException("Algorithm name " + prfName +
+    							" not a valid algorithm name for property " +
+    							DefaultSecurityConfiguration.KDF_PRF_ALG);
+		}
+		prfAlg_ = prfName;
+	}
+
+	/**
+	 * Return the name of the algorithm for the Pseudo Random Function (PRF)
+	 * that is being used.
+	 * @return	The PRF algorithm name.
+	 */
+	public String getPRFAlgName() {
+		return prfAlg_;		
+	}
+	
+	/**
+	 * Package level method for use by {@code CipherText} class to get default
+	 * 
+	 */
+	static int getDefaultPRFSelection() {
+		String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getAlgName().equals(prfName) ) {
+				return prf.getValue();
+			}
+		}
+		throw new ConfigurationException("Algorithm name " + prfName +
+				" not a valid algorithm name for property " +
+				DefaultSecurityConfiguration.KDF_PRF_ALG);
+	}
+	
+	/**
+	 * Set version so backward compatibility can be supported. Used to set the
+	 * version to some previous version so that previously encrypted data can
+	 * be decrypted.
+	 * @param version	Date as a integer, in format of YYYYMMDD. Maximum
+	 * 					version date is 99991231 (December 31, 9999).
+	 * @throws	IllegalArgumentException	If {@code version} is not within
+	 * 					the valid range of [20110203, 99991231].
+	 */
+	public void setVersion(int version) throws IllegalArgumentException {
+		CryptoHelper.isValidKDFVersion(version, false, true);
+		this.version_ = version;
+	}
+
+	/**
+	 * Return the version used for backward compatibility.
+	 * @return	The KDF version #, in format YYYYMMDD, used for supporting
+	 * 			backward compatibility.
+	 */
+	public int getVersion() {
+		return version_;
+	}
+	
+	
+	// TODO: IMPORTANT NOTE: In a future release (hopefully starting in 2.1.1),
+	// we will be using the 'context' to mix in some additional things. At a
+	// minimum, we will be using the KDF version (version_) so that downgrade version
+	// attacks are not possible. Other candidates are the cipher xform and
+	// the timestamp.
+	/**
+	 * Set the 'context' as specified by NIST Special Publication 800-108. NIST
+	 * defines 'context' as "A binary string containing the information related
+	 * to the derived keying material. It may include identities of parties who
+	 * are deriving and/or using the derived keying material and, optionally, a 
+	 * once known by the parties who derive the keys." NIST SP 800-108 seems to
+	 * imply that while 'context' is recommended, that it is optional. In section
+	 * 7.6 of NIST 800-108, NIST uses "SHOULD" rather than "MUST":
+	 * <blockquote>
+	 * "Derived keying material should be bound to all relying
+	 * entities and other information to identify the derived
+	 * keying material. This is called context binding.
+	 * In particular, the identity (or identifier, as the term
+	 * is defined in [NIST SP 800-56A , sic] and [NIST SP
+	 * 800-56B , sic]) of each entity that will access (meaning
+	 * derive, hold, use, and/or distribute) any segment of
+	 * the keying material should be included in the Context
+	 * string input to the KDF, provided that this information
+	 * is known by each entity who derives the keying material."
+	 * </blockquote>
+	 * The ISO/IEC's KDF2 uses a similar construction for their KDF and there
+	 * 'context' data is not specified at all. Therefore, ESAPI 2.0's
+	 * reference implementation, {@code JavaEncryptor}, chooses not to use
+	 * 'context' at all.
+	 * 
+	 * @param context	Optional binary string containing information related to
+	 * 					the derived keying material. By default (if this method
+	 * 					is never called), the empty string is used. May have any
+	 * 					value but {@code null}.
+	 */
+	public void setContext(String context) {
+		if ( context == null ) {
+			throw new IllegalArgumentException("Context may not be null.");
+		}
+		context_ = context;
+	}
+	
+	/**
+	 * Return the optional 'context' that typically contains information
+	 * related to the keying material, such as the identities of the message
+	 * sender and recipient.
+	 * @see #setContext(String)
+	 * @return The 'context' is returned.
+	 */
+	public String getContext() {
+		return context_;
+	}
+
+	/**
+	 * The method is ESAPI's Key Derivation Function (KDF) that computes a
+	 * derived key from the {@code keyDerivationKey} for either
+	 * encryption / decryption or for authentication.
+	 * <p>
+	 * <b>CAUTION:</b> If this algorithm for computing derived keys from the
+	 * key derivation key is <i>ever</i> changed, we risk breaking backward compatibility of being
+	 * able to decrypt data previously encrypted with earlier / different versions
+	 * of this method. Therefore, do not change this unless you are 100% certain that
+	 * what you are doing will NOT change either of the derived keys for
+	 * ANY "key derivation key" AT ALL!!!
+	 * <p>
+	 * <b>NOTE:</b> This method is generally not intended to be called separately.
+	 * It is used by ESAPI's reference crypto implementation class {@code JavaEncryptor}
+	 * and might be useful for someone implementing their own replacement class, but
+	 * generally it is not something that is useful to application client code.
+	 * 
+	 * @param keyDerivationKey  A key used as an input to a key derivation function
+	 *                          to derive other keys. This is the key that generally
+	 *                          is created using some key generation mechanism such as
+	 *                          {@link CryptoHelper#generateSecretKey(String, int)}. The
+	 *                          "input" key from which the other keys are derived.
+	 * 							The derived key will have the same algorithm type
+	 * 							as this key. This KDK cannot be null.
+	 * @param keySize		The cipher's key size (in bits) for the {@code keyDerivationKey}.
+	 * 						Must have a minimum size of 56 bits and be an integral multiple of 8-bits.
+	 * 						<b>Note:</b> The derived key will have the same size as this.
+	 * @param purpose		The purpose for the derived key. For the ESAPI reference implementation,
+	 * 						{@code JavaEncryptor}, this <i>must</i> be either the string "encryption" or
+	 * 						"authenticity", where "encryption" is used for creating a derived key to use
+	 * 						for confidentiality, and "authenticity" is used for creating a derived key to
+	 * 						use with a MAC to ensure message authenticity. However, since parameter serves
+	 * 						the same purpose as the "Label" in section 5.1 of NIST SP 800-108, it really can
+	 * 						be set to anything other than {@code null} or an empty string when called outside
+	 * 						of {@code JavaEncryptor}.
+	 * @return				The derived {@code SecretKey} to be used according
+	 * 						to the specified purpose. 
+	 * @throws NoSuchAlgorithmException		The {@code keyDerivationKey} has an unsupported
+	 * 						encryption algorithm or no current JCE provider supports
+	 * 						"HmacSHA1".
+	 * @throws EncryptionException		If "UTF-8" is not supported as an encoding, then
+	 * 						this is thrown with the original {@code UnsupportedEncodingException}
+	 * 						as the cause. (NOTE: This should never happen as "UTF-8" is supposed to
+	 * 						be a common encoding supported by all Java implementations. Support
+	 * 					    for it is usually in rt.jar.)
+	 * @throws InvalidKeyException 	Likely indicates a coding error. Should not happen.
+	 * @throws EncryptionException  Throw for some precondition violations.
+	 */
+	public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, String purpose)
+			throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException
+	{
+		// Acknowledgments: David Wagner first suggested this approach, I (Kevin Wall)
+		//				    stumbled upon NIST SP 800-108 and used it as a basis to
+		//				    extend it. Later it was changed that conforms more closely
+		//					to section 5.1 of NIST SP 800-108 based on feedback from
+		//					Jeffrey Walton.
+		//
+        // These probably should be turned into actual runtime checks and an
+        // IllegalArgumentException should be thrown if they are violated.
+		assert keyDerivationKey != null : "Key derivation key cannot be null.";
+			// We would choose a larger minimum key size, but we want to be
+			// able to accept DES for legacy encryption needs.
+		assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
+		assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
+		assert purpose != null && !purpose.equals("") : "Purpose may not be null or empty.";
+
+		keySize = calcKeySize( keySize );	// Safely convert to whole # of bytes.
+		byte[] derivedKey = new byte[ keySize ];
+		byte[] label;				    	// Same purpose as NIST SP 800-108's "label" in section 5.1.
+		byte[] context;						// See setContext() for details.
+		try {
+			label = purpose.getBytes("UTF-8");
+			context = context_.getBytes("UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			throw new EncryptionException("Encryption failure (internal encoding error: UTF-8)",
+					 "UTF-8 encoding is NOT supported as a standard byte encoding: " + e.getMessage(), e);
+		}
+		
+			// Note that keyDerivationKey is going to be some SecretKey like an AES or
+			// DESede key, but not an HmacSHA1 key. That means it is not likely
+			// going to be 20 bytes but something different. Experiments show
+			// that doesn't really matter though as the SecretKeySpec CTOR on
+			// the following line still returns the appropriate sized key for
+			// HmacSHA1. So, if keyDerivationKey was originally (say) a 56-bit
+            // DES key, then there is apparently some key-stretching going on here
+            // under the hood to create 'sk' so that it is 20 bytes. I cannot vouch
+            // for how secure this key-stretching is. Worse, it might not be specified
+            // as to *how* it is done and left to each JCE provider.
+		SecretKey sk = new SecretKeySpec(keyDerivationKey.getEncoded(), "HmacSHA1");
+		Mac mac = null;
+
+		try {
+			mac = Mac.getInstance("HmacSHA1");
+			mac.init(sk);
+		} catch( InvalidKeyException ex ) {
+			logger.error(Logger.SECURITY_FAILURE,
+					"Created HmacSHA1 Mac but SecretKey sk has alg " +
+					sk.getAlgorithm(), ex);
+			throw ex;
+		}
+		
+		// Repeatedly call of HmacSHA1 hash until we've collected enough bits
+		// for the derived key. The first time through, we calculate the HmacSHA1
+		// on the "purpose" string, but subsequent calculations are performed
+		// on the previous result.
+		int ctr = 1;		// Iteration counter for NIST 800-108
+		int totalCopied = 0;
+		int destPos = 0;
+		int len = 0;
+		byte[] tmpKey = null;	// Do not declare inside do-while loop!!!
+		do {
+			//
+			// This is to make our KDF more along the line of NIST's.
+			// NIST's Special Publication 800-108 performs the following in
+            // the iterative loop of Section 5.1:
+            //       n := number of blocks required to fulfill request
+            //       for i = 1 to n, do
+            //           K(i) := PRF(KDK, [i]2 || Label || 0x00 || Context || [L]2)
+            //           result(i) := result(i-1) || K(i)
+            //       end
+            // where '||' is represents bit string concatenation, and PRF is
+            // an NIST approved pseudo-random function (such as an HMAC),
+            // KDK is the key derivation key, [i]2 is the big-endian binary
+            // representation of the iteration, and [L]2 is the bits
+            // requested by the caller, and 0x00 represents a null byte
+            // used as a separation indicator.  However, other sections of this
+            // document (Section 7.6) implies that Context is to be an
+            // optional field (based on NIST's use of the word SHOULD
+            // rather than MUST)
+            // 
+			mac.update( ByteConversionUtil.fromInt( ctr++ ) );
+			mac.update(label);
+			mac.update((byte) '\0');
+			mac.update(context); // This is problematic for us. See Jeff Walton's
+								  // analysis of ESAPI 2.0's KDF for details.
+								  // Maybe for 2.1, we'll see; 2.0 too close to GA.
+			
+	            // According to the Javadoc for Mac.doFinal(byte[]),
+	            // "A call to this method resets this Mac object to the state it was
+	            // in when previously initialized via a call to init(Key) or
+	            // init(Key, AlgorithmParameterSpec). That is, the object is reset
+	            // and available to generate another MAC from the same key, if
+	            // desired, via new calls to update and doFinal." Therefore, we do
+	            // not do an explicit reset().
+			tmpKey = mac.doFinal( ByteConversionUtil.fromInt( keySize ) );
+			
+			if ( tmpKey.length >= keySize ) {
+				len = keySize;
+			} else {
+				len = Math.min(tmpKey.length, keySize - totalCopied);
+			}
+			System.arraycopy(tmpKey, 0, derivedKey, destPos, len);
+			label = tmpKey;
+			totalCopied += tmpKey.length;
+			destPos += len;
+		} while( totalCopied < keySize );
+		
+		// Don't leave remnants of the partial key in memory. (Note: we could
+		// not do this if tmpKey were declared in the do-while loop.
+		for ( int i = 0; i < tmpKey.length; i++ ) {
+			tmpKey[i] = '\0';
+		}
+		tmpKey = null;	// Make it immediately eligible for GC.
+		
+        // Convert it back into a SecretKey of the appropriate type.
+		return new SecretKeySpec(derivedKey, keyDerivationKey.getAlgorithm());
+	}
+
+	/**
+	 * Check if specified algorithm name is a valid PRF that can be used.
+	 * @param prfAlgName	Name of the PRF algorithm; e.g., "HmacSHA1", "HmacSHA384", etc.
+	 * @return	True if {@code prfAlgName} is supported, otherwise false.
+	 */
+	public static boolean isValidPRF(String prfAlgName) {
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getAlgName().equals(prfAlgName) ) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public static PRF_ALGORITHMS convertNameToPRF(String prfAlgName) {
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getAlgName().equals(prfAlgName) ) {
+				return prf;
+			}
+		}
+		throw new IllegalArgumentException("Algorithm name " + prfAlgName +
+				" not a valid PRF algorithm name for the ESAPI KDF.");
+	}
+	
+	public static PRF_ALGORITHMS convertIntToPRF(int selection) {
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getValue() == selection ) {
+				return prf;
+			}
+		}
+		throw new IllegalArgumentException("No KDF PRF algorithm found for value name " + selection);		
+	}
+	
+    /**
+     * Calculate the size of a key. The key size is given in bits, but we
+     * can only allocate them by octets (i.e., bytes), so make sure we
+     * round up to the next whole number of octets to have room for all
+     * the bits. For example, a key size of 9 bits would require 2 octets
+     * to store it.
+     *
+     * @param ks    The key size, in bits.
+     * @return      The key size, in octets, large enough to accommodate
+     *              {@code ks} bits.
+     */
+    private static int calcKeySize(int ks) {
+        assert ks > 0 : "Key size must be > 0 bits.";
+        int numBytes = 0;
+        int n = ks/8;
+        int rem = ks % 8;
+        if ( rem == 0 ) {
+            numBytes = n;
+        } else {
+            numBytes = n + 1;
+        }
+        return numBytes;
+    }
+
+    /**
+     * Print list of ESAPI supported pseudo-random functions for KDF and
+     * KDF version information.
+     *
+     * @param args  Required, but not used.
+     */
+	public static final void main(String args[]) {
+		System.out.println("Supported pseudo-random functions for KDF (version: " + kdfVersion + ")");
+		System.out.println("Enum Name\tAlgorithm\t# bits");
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+		    System.out.println(prf + "\t" + prf.getAlgName() + "\t" + prf.getBits());
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/PlainText.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/PlainText.java.svn-base
new file mode 100644
index 0000000..4491404
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/PlainText.java.svn-base
@@ -0,0 +1,159 @@
+package org.owasp.esapi.crypto;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/**
+ * A class representing plaintext (versus ciphertext) as related to
+ * cryptographic systems.  This class embodies UTF-8 byte-encoding to
+ * translate between byte arrays and {@code String}s. Once constructed, this
+ * object is immutable.
+ * <p>
+ * Note: Conversion to/from UTF-8 byte-encoding can, in theory, throw
+ * an {@code UnsupportedEncodingException}. However, UTF-8 encoding
+ * should be a standard encoding for all Java installations, so an
+ * {@code UnsupportedEncodingException} never actually be thrown. Therefore,
+ * in order to to keep client code uncluttered, any possible
+ * {@code UnsupportedEncodingException}s will be first logged, and then
+ * re-thrown as a {@code RuntimeException} with the original
+ * {@code UnsupportedEncodingException} as the cause.
+ * <p>
+ * Copyright © 2009 - The OWASP Foundation
+ * </p>
+ * @author kevin.w.wall at gmail.com
+ * @see CipherText
+ * @since 2.0
+ */
+public final class PlainText implements Serializable {
+
+	private static final long serialVersionUID = 20090921;
+	private static Logger logger = ESAPI.getLogger("PlainText");
+	
+	private byte[] rawBytes = null;
+	
+	/**
+	 * Construct a {@code PlainText} object from a {@code String}.
+	 * @param str	The {@code String} that is converted to a UTF-8 encoded
+	 * 				byte array to create the {@code PlainText} object.
+	 * @throws IllegalArgumentException	If {@code str} argument is null.
+	 */
+	public PlainText(String str) {
+		try {
+		    assert str != null : "String for plaintext cannot be null.";
+		    if ( str == null ) {
+		    	throw new IllegalArgumentException("String for plaintext may not be null!");
+		    }
+			rawBytes = str.getBytes("UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			// Should never happen.
+			logger.error(Logger.EVENT_FAILURE, "PlainText(String) CTOR failed: Can't find UTF-8 byte-encoding!", e);
+			throw new RuntimeException("Can't find UTF-8 byte-encoding!", e);
+		}
+	}
+
+	/**
+	 * Construct a {@code PlainText} object from a {@code byte} array.
+	 * @param b	The {@code byte} array used to create the {@code PlainText}
+	 * 			object.
+	 */
+	public PlainText(byte[] b) {
+		// Must allow 0 length arrays though, to represent empty strings.
+		assert b != null : "Byte array representing plaintext cannot be null.";
+		    // Make copy so mutable byte array b can't change PlainText.
+		rawBytes = new byte[ b.length ];
+		System.arraycopy(b, 0, rawBytes, 0, b.length);
+	}
+	
+	/**
+	 * Convert the {@code PlainText} object to a UTF-8 encoded {@code String}.
+	 * @return	A {@code String} representing the {@code PlainText} object.
+	 */
+	public String toString() {
+		try {
+			return new String(rawBytes, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			// Should never happen.
+			logger.error(Logger.EVENT_FAILURE, "PlainText.toString() failed: Can't find UTF-8 byte-encoding!", e);
+			throw new RuntimeException("Can't find UTF-8 byte-encoding!", e);
+		}
+	}
+	
+	/**
+	 * Convert the {@code PlainText} object to a byte array.
+	 * @return	A byte array representing the {@code PlainText} object.
+	 */
+	public byte[] asBytes() {
+	    byte[] bytes = new byte[ rawBytes.length ];
+	    System.arraycopy(rawBytes, 0, bytes, 0, rawBytes.length);
+		return bytes;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override public boolean equals(Object anObject) {
+
+        if ( this == anObject ) return true;
+        if ( anObject == null ) return false;
+        boolean result = false;
+        if ( anObject instanceof PlainText ) {
+            PlainText that = (PlainText)anObject;
+            result = ( that.canEqual(this) &&
+                    ( this.toString().equals( that.toString() ) )
+                  );
+        }
+        return result;
+	}
+	
+	/**
+	 * Same as {@code this.toString().hashCode()}.
+	 * @return	{@code this.toString().hashCode()}.
+	 */
+	@Override public int hashCode() {
+		return this.toString().hashCode();
+	}
+	
+	/**
+	 * Return the length of the UTF-8 encoded byte array representing this
+	 * object. Note that if this object was constructed with the constructor
+	 * {@code PlainText(String str)}, then this length might not necessarily
+	 * agree with {@code str.length()}.
+	 * 
+	 * @return	The length of the UTF-8 encoded byte array representing this
+	 * 			object.
+	 */
+	public int length() {
+		return rawBytes.length;
+	}
+	
+	// DISCUSS: Should we set 'rawBytes' to null??? Won't make it eligible for
+	//			GC unless this PlainText object is set to null which can't do
+	//			from here. If we set it to null, most methods will cause
+	//			NullPointerException to be thrown. Also will have to change a
+	//			lot of JUnit tests.
+	/**
+	 * First overwrite the bytes of plaintext with the character '*'.
+	 */
+	public void overwrite() {
+		CryptoHelper.overwrite( rawBytes );
+		// rawBytes = null;					// See above comment re: discussion.
+	}
+	
+    /**
+     * Needed for correct definition of equals for general classes.
+     * (Technically not needed for 'final' classes though like this class
+     * though; this will just allow it to work in the future should we
+     * decide to allow * sub-classing of this class.)
+     * </p><p>
+     * See <a href="http://www.artima.com/lejava/articles/equality.html">
+     * How to write an Equality Method in Java</a>
+     * for full explanation.
+     * </p>
+     */
+    protected boolean canEqual(Object other) {
+        return (other instanceof PlainText);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/SecurityProviderLoader.java.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/SecurityProviderLoader.java.svn-base
new file mode 100644
index 0000000..cacc9c9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/SecurityProviderLoader.java.svn-base
@@ -0,0 +1,284 @@
+package org.owasp.esapi.crypto;
+
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Hashtable;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/** 
+ * This class provides a generic static method that loads a
+ * {@code java.security.Provider} either by some generic name
+ * (i.e., {@code Provider.getName()}) or by a fully-qualified class name.
+ * It is intended to be called dynamically by an application to add a
+ * specific JCE provider at runtime.
+ * </p><p>
+ * If the {@code ESAPI.properties} file has a the property
+ * {@code ESAPI.PreferredJCEProvider} defined to either a recognized
+ * JCE provider (see below for list) or a fully qualified path name of
+ * that JCE provider's {@code Provider} class, then the reference implementation
+ * of ESAPI cryptography ({@code org.owasp.esapi.reference.crypto.JavaEncryptor})
+ * tries to load this specified JCE provider via
+ * {@link SecurityProviderLoader#insertProviderAt(String,int)}.
+ * </p>
+ */
+public class SecurityProviderLoader  {
+    private static Logger logger = ESAPI.getLogger("SecurityProviderLoader");
+    private static Hashtable<String, String> jceProviders;
+
+    static {
+        //
+        // Load the table with known providers. We load the (short) JCE name
+        // and the corresponding provider class. We don't 'new' the actual
+        // class name here because that would mean we would have to have all
+        // these jars. Instead we use reflection and do it dynamically only
+        // when SecurityProviderLoader.insertProviderAt() is called because
+        // presumably they will have the jar in their classpath for the
+        // provider they wish to use.
+        //
+        jceProviders = new Hashtable<String,String>();
+            // SunJCE is installed by default and should always be available
+            // with Sun-based JREs. As of JDK 1.3 and later, it is part of the
+            // standard JRE install.
+        jceProviders.put("SunJCE", "com.sun.crypto.provider.SunJCE");
+        
+            // IBMJCE is default for WebSphere and is used by IBM JDKs. They
+            // also have IBMJCEFIPS, but not sure if this is *always* provided
+            // with WebSphere or just an add-on, hence not including it. IBMJCEFIPS
+        	// is a FIPS 140-2 compliant JCE provider from IBM.
+        jceProviders.put("IBMJCE", "com.ibm.crypto.provider.IBMJCE");
+        // jceProviders.put("IBMJCEFIPS", "com.ibm.crypto.fips.provider.IBMJCEFIPS");
+        
+            // GnuCrypto is JCE provider for GNU Compiler for Java (GCJ)
+        jceProviders.put("GnuCrypto", "gnu.crypto.jce.GnuCrypto");
+
+            // Bouncy Castle -- http://www.bouncycastle.org/ - FOSS, maintained.
+        jceProviders.put("BC",
+                         "org.bouncycastle.jce.provider.BouncyCastleProvider");
+
+            // IAIK -- http://jce.iaik.tugraz.at/ -- No longer free.
+        jceProviders.put("IAIK", "iaik.security.provider.IAIK");
+
+            // IBM FIPS 140-2 compliant provider -- Commercial
+            // See above comments.
+        // jceProviders.put("IBMJCEFIPS", "com.ibm.crypto.fips.provider.IBMJCEFIPS");
+
+            // RSA FIPS 142-2 compliant provider -- Commercial
+        // jceProviders.put("RSA", "com.rsa.jsafe.crypto.CryptoJ");
+
+            // Cryptix -- http://www.cryptix.org/ - FOSS, not maintained.
+            // Cryptix JCE code signing cert expired 2009/08/29 and was not
+            // renewed.
+        jceProviders.put("CryptixCrypto", "cryptix.jce.provider.CryptixCrypto");
+        jceProviders.put("Cryptix", "cryptix.jce.provider.CryptixCrypto");
+
+            // ABA - FOSS, not maintained - Google for it, or maybe search for
+            // old copy at http://www.archive.org/
+        jceProviders.put("ABA", "au.net.aba.crypto.provider.ABAProvider");
+    }
+
+    /** 
+     * This methods adds a provider to the {@code SecurityManager}
+     * either by some generic name or by the class name. 
+     * </p><p>
+     * The following generic JCE provider names are built-in:
+     * <ul>
+     * <li>SunJCE</li>
+     * <li>IBMJCE [for WebSphere]</li>
+     * <li>GnuCrypto [for use with GNU Compiler for Java, i.e., gcj]</li>
+     * <li>BC [i.e., Bouncy Castle]</li>
+     * <li>IAIK</li>
+     * <li>CryptixCrypto (or Cryptix)
+     * <li>ABA
+     * </ul>
+     * Note that neither Cryptix or ABA are actively maintained so
+     * it is recommended that you do not start using them for ESAPI
+     * unless your application already has a dependency on them. Furthermore,
+     * the Cryptix JCE jars likely will not work as the Cryptix code signing
+     * certificate has expired as of August 28, 2009. (This likely is true
+     * for ABA, but I can't even find a copy to download!). Lastly, the IAIK
+     * provider is no longer offered as free, open source. It is not a
+     * commercial product. See {@link "http://jce.iaik.tugraz.at/"} for
+     * details. While some older versions were offered free, it is not clear
+     * whether the accompanying license still allows you to use it, and if
+     * it does, whether or not the code signing certificate used to sign
+     * their JCE jar(s) has expired are not.  Therefore, if you are looking
+     * for a FOSS alternative to SunJCE, Bouncy Castle
+     * ({@link "http://www.bouncycastle.org/"} is probably your best bet. The
+     * BC provider does support many the "combined cipher modes" that provide
+     * both confidentiality and authenticity. (See the {@code ESAPI.properties}
+     * property {@code Encryptor.cipher_modes.combined_modes} for details.)
+     * </p><p>
+     * For those working in the U.S. federal government, it should be noted
+     * that <i>none</i> of the providers listed here are considered validated
+     * by NIST's Cryptographic Module Validation Program and are therefore
+     * <b>not</b> considered FIPS 140-2 compliant. There are a few approved
+     * JCE compatible Java libraries that are on NIST's CMVP list, but this
+     * list changes constantly so they are not listed here. For further details
+     * on NIST's CMVP, see
+     * {@link "http://csrc.nist.gov/groups/STM/cmvp/index.html"}.
+     * </p><p>
+     * Finally, if you wish to use some other JCE provider not recognized above,
+     * you must specify the provider's fully-qualified class name (which in
+     * turn must have a public, no argument constructor).
+     * </p><p>
+     * The application must be given the {@code SecurityPermission} with a
+     * value of {@code insertProvider.<provider_name>} (where
+     * <provider_name> is the name of the algorithm provider if
+     * a security manager is installed.
+     * </p>
+     *
+     * @param algProvider Name of the JCE algorithm provider. If the name
+     *                    contains a ".", this is interpreted as the name
+     *                    of a {@code java.security.Provider} class name.
+     * @param pos         The preference position (starting at 1) that the
+     *                    caller would like for this provider. If you wish
+     *                    for it to be installed as the <i>last</i> provider
+     *                    (as of the time of this call), set {@code pos} to -1.
+     * @return The actual preference position at which the provider was added,
+     *         or -1 if the provider was not added because it is already
+     *         installed.
+     * @exception NoSuchProviderException - thrown if the provider class
+     *         could not be loaded or added to the {@code SecurityManager} or
+     *         any other reason for failure.
+     */
+    public static int insertProviderAt(String algProvider, int pos)
+        throws NoSuchProviderException
+    {
+        // We assume that if the algorithm provider contains a ".", then
+        // we interpret this as a crypto provider class name and dynamically
+        // add the provider. If it's one of the special ones we know about,
+        // we also dynamically create it. Otherwise, we assume the provider
+        // is in the "java.security" file.
+        Class<?> providerClass = null;
+        String clzName = null;
+        Provider cryptoProvider = null;
+        assert (pos == -1 || pos >= 1) : "Position pos must be -1 or integer >= 1";
+        try {
+            // Does algProvider look like a class name?
+            if (algProvider.indexOf('.') != -1) {
+                clzName = algProvider;
+            } else if ( jceProviders.containsKey(algProvider) ) {
+                // One of the special cases we know about.
+                clzName = jceProviders.get(algProvider);
+            } else {
+                throw new NoSuchProviderException("Unable to locate Provider class for " +
+                             "provider " + algProvider + ". Try using fully qualified class name " +
+                             "or check provider name for typos. Builtin provider names are: " +
+                             jceProviders.toString());
+            }
+
+            providerClass = Class.forName(clzName);
+            cryptoProvider = (Provider)providerClass.newInstance();
+
+            // Found from above. Note that Security.insertProviderAt() can
+            // throw a SecurityException if a Java SecurityManager is
+            // installed and application doesn't have appropriate
+            // permissions in policy file.
+            //
+            // However, since SecurityException is a RuntimeException it
+            // doesn't need to be explicitly declared on the throws clause.
+            // The application must be given the SecurityPermission with
+            // a value of "insertProvider.<provider_name>" (where
+            // <provider_name> is the name of the algorithm provider) if
+            // a SecurityManager is installed.
+            int ret;
+            if ( pos == -1 ) {      // Special case: Means place _last_.
+                ret = Security.addProvider(cryptoProvider);
+            } else {
+                ret = Security.insertProviderAt(cryptoProvider, pos);
+            }
+            if ( ret == -1 ) {
+                // log INFO that provider was already loaded.
+                String msg = "JCE provider '" + algProvider + "' already loaded";
+                if (pos == -1) {
+                    // The just wanted it available (loaded last) and it is, so
+                    // this is not critical.
+                    logger.always(Logger.SECURITY_SUCCESS, msg);
+                } else {
+                    // In this case, it's a warning because it may have already
+                    // been loaded, but *after* the position they requested.
+                    // For example, if they were trying to load a FIPS 140-2
+                    // compliant JCE provider at the first position and it was
+                    // already loaded at position 3, then this is not FIPS 140-2
+                    // compliant. Therefore, we make it a warning and a failure.
+                	// Also log separately using 'always' in case warnings suppressed
+                	// as per NSA suggestion.
+                    logger.warning(Logger.SECURITY_FAILURE, msg);
+                    logger.always(Logger.SECURITY_FAILURE, "(audit) " + msg);
+                }
+            } else {
+            	// As per NSA suggestion.
+                logger.always(Logger.SECURITY_AUDIT,
+                        "Successfully loaded preferred JCE provider " +
+                        algProvider + " at position " + pos);
+            }
+            return ret;
+        } catch(SecurityException ex) {
+            // CHECKME: Log security event here too? This is a RuntimeException.
+        	// It would only be thrown if a SecurityManager is installed that
+        	// prohibits Security.addProvider() or Security.insertProviderAt()
+        	// by the current user of this thread. Will log it here. Can always
+        	// be ignored.
+        	logger.always(Logger.SECURITY_FAILURE, "Failed to load preferred JCE provider " +
+        				  algProvider + " at position " + pos, ex);
+            throw ex;
+        } catch(Exception ex) {
+            // Possibilities include: ClassNotFoundException,
+            //                        InstantiationException, and others???
+            //
+            // Log an error & re-throw original message as NoSuchProviderException,
+            // since that what it probably really implied here. This probably a configuration
+        	// error (e.g., classpath problem, etc.) so we use EVENT_FAILURE rather than
+        	// SECURITY_FAILURE here.
+            logger.error(Logger.EVENT_FAILURE, "Failed to insert failed crypto " +
+                    " provider " + algProvider + " at position " + pos, ex);
+            throw new NoSuchProviderException("Failed to insert crypto " +
+                                       " provider for " + algProvider +
+                                       "; exception msg: " + ex.toString());
+        }
+    }
+    
+    /**
+     * Load the preferred JCE provider for ESAPI based on the <b>ESAPI.properties</b>
+     * property {@code Encryptor.PreferredJCEProvider}. If this property is null
+     * (i.e., unset) or set to an empty string, then no JCE provider is inserted
+     * at the "preferred" position and thus the Java VM continues to use whatever
+     * the default it was using for this (generally specified in the file
+     * {@code $JAVA_HOME/jre/security/java.security}).
+     * @return The actual preference position at which the provider was added,
+     *         (which is expected to be 1) or -1 if the provider was not added
+     *         because it is already installed at some other position. -1 is also
+     *         returned if the {@code Encryptor.PreferredJCEProvider} was not set
+     *         or set to an empty string, i.e., if the application <i>has</i> no
+     *         preferred JCE provider.
+     * @exception NoSuchProviderException - thrown if the provider class
+     *         could not be loaded or added to the {@code SecurityManager} or
+     *         any other reason for failure.
+     * @see <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.htm">
+     *      ESAPI 2.0 Symmetric Encryption User Guide</a>
+     */
+    public static int loadESAPIPreferredJCEProvider() throws NoSuchProviderException
+    {
+        String prefJCEProvider =
+            ESAPI.securityConfiguration().getPreferredJCEProvider();
+        try {
+            // If unset or set to empty string, then don't try to change it.
+            if ( prefJCEProvider == null || prefJCEProvider.trim().length() == 0) {
+            		// Always log, per NSA suggestion.
+                logger.always(Logger.SECURITY_AUDIT, "No Encryptor.PreferredJCEProvider specified.");
+                return -1;  // Unchanged; it is, whatever it is.
+            } else {
+                return insertProviderAt(prefJCEProvider, 1);
+            }
+        } catch (NoSuchProviderException ex) {
+            // Will already have logged with exception msg.
+        	String msg = "failed to load *preferred* " + "JCE crypto provider, " + prefJCEProvider;
+        	logger.always(Logger.SECURITY_AUDIT, msg);	// Per NSA suggestion.
+            logger.error(Logger.SECURITY_FAILURE, msg);
+            throw ex;
+        }
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..fcca55e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/.svn/text-base/package.html.svn-base
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body bgcolor="white">
+This package contains ESAPI cryptography-related classes used throughout
+ESAPI.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/crypto/CipherSpec.java b/src/main/java/org/owasp/esapi/crypto/CipherSpec.java
new file mode 100644
index 0000000..556ef8e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/CipherSpec.java
@@ -0,0 +1,388 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+// If we had PRIVATE packages, e.g., org.owasp.esapi.util.pvt, this would belong
+// there. CipherText uses this, but doesn't expose it directly.
+package org.owasp.esapi.crypto;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+
+import javax.crypto.Cipher;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.util.NullSafe;
+
+
+/**
+ * Specifies all the relevant configuration data needed in constructing and
+ * using a {@link javax.crypto.Cipher} except for the encryption key.
+ * </p><p>
+ * The "setters" all return a reference to {@code this} so that they can be
+ * strung together.
+ * </p><p>
+ * Note: While this is a useful class in it's own right, it should primarily be
+ * regarded as an implementation class to use with ESAPI encryption, especially
+ * the reference implementation. It is <i>not</i> intended to be used directly
+ * by application developers, but rather only by those either extending ESAPI
+ * or in the ESAPI reference implementation. Use <i>directly</i> by application
+ * code is not recommended or supported.
+ * 
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public final class CipherSpec implements Serializable {
+
+	private static final long serialVersionUID = 20090822;	// version, in YYYYMMDD format
+	
+	private String  cipher_xform_   = ESAPI.securityConfiguration().getCipherTransformation();
+	private int     keySize_        = ESAPI.securityConfiguration().getEncryptionKeyLength(); // In bits
+	private int     blockSize_      = 16;   // In bytes! I.e., 128 bits!!!
+	private byte[]  iv_             = null;
+	
+	// Cipher transformation component. Format is ALG/MODE/PADDING
+    private enum CipherTransformationComponent { ALG, MODE, PADDING }
+
+	/**
+	 * CTOR that explicitly sets everything.
+	 * @param cipherXform	The cipher transformation
+	 * @param keySize		The key size (in bits).
+	 * @param blockSize		The block size (in bytes).
+	 * @param iv			The initialization vector. Null if not applicable.
+	 */
+	public CipherSpec(String cipherXform, int keySize, int blockSize, final byte[] iv) {
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+		setBlockSize(blockSize);
+		setIV(iv);
+	}
+	
+	/**
+	 * CTOR that sets everything but IV.
+	 * @param cipherXform	The cipher transformation
+	 * @param keySize		The key size (in bits).
+	 * @param blockSize		The block size (in bytes).
+	 */
+	public CipherSpec(String cipherXform, int keySize, int blockSize) {
+		// Note: Do NOT use
+		//			this(cipherXform, keySize, blockSize, null);
+		// because of assertion in setIV().
+		//
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+		setBlockSize(blockSize);
+	}
+	
+	/** CTOR that sets everything but block size and IV. */
+	public CipherSpec(String cipherXform, int keySize) {
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+	}
+	
+	/** CTOR that sets everything except block size. */
+	public CipherSpec(String cipherXform, int keySize, final byte[] iv) {
+		setCipherTransformation(cipherXform);
+		setKeySize(keySize);
+		setIV(iv);
+	}
+
+	/** CTOR that sets everything except for the cipher key size and possibly
+	 *  the IV. (IV may not be applicable--e.g., with ECB--or may not have
+	 *  been specified yet.
+	 */
+	public CipherSpec(final Cipher cipher) {
+		setCipherTransformation(cipher.getAlgorithm(), true);
+		setBlockSize(cipher.getBlockSize());
+		if ( cipher.getIV() != null ) {
+			setIV(cipher.getIV());
+		}
+	}
+	
+	/** CTOR that sets everything. */
+	public CipherSpec(final Cipher cipher, int keySize) {
+		this(cipher);
+		setKeySize(keySize);
+	}
+	
+	/* CTOR that sets only the IV and uses defaults for everything else. */
+	public CipherSpec(final byte[] iv) {
+		setIV(iv);
+	}
+	
+	/**
+	 * Default CTOR. Creates a cipher specification for 128-bit cipher
+	 * transformation of "AES/CBC/PKCS5Padding" and a {@code null} IV.
+	 */
+	public CipherSpec() {
+		// All defaults
+	}
+
+	/**
+	 * Set the cipher transformation for this {@code CipherSpec}.
+	 * @param cipherXform	The cipher transformation string; e.g., "DESede/CBC/PKCS5Padding".
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setCipherTransformation(String cipherXform) {
+		setCipherTransformation(cipherXform, false);
+		return this;
+	}
+
+	/**
+	 * Set the cipher transformation for this {@code CipherSpec}. This is only
+	 * used by the CTOR {@code CipherSpec(Cipher)} and {@code CipherSpec(Cipher, int)}.
+	 * @param cipherXform	The cipher transformation string; e.g.,
+	 * 						"DESede/CBC/PKCS5Padding". May not be null or empty.
+	 * @param fromCipher If true, the cipher transformation was set via
+	 * 					 {@code Cipher.getAlgorithm()} which may only return the
+	 * 					 actual algorithm. In that case we check and if all 3 parts
+	 * 					 were not specified, then we specify the parts that were
+	 * 					 based on "ECB" as the default cipher mode and "NoPadding"
+	 * 					 as the default padding scheme.
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	private CipherSpec setCipherTransformation(String cipherXform, boolean fromCipher) {
+		if ( ! StringUtilities.notNullOrEmpty(cipherXform, true) ) {	// Yes, really want '!' here.
+			throw new IllegalArgumentException("Cipher transformation may not be null or empty string (after trimming whitespace).");
+		}
+		int parts = cipherXform.split("/").length;
+		assert ( !fromCipher ? (parts == 3) : true ) :
+			"Malformed cipherXform (" + cipherXform + "); must have form: \"alg/mode/paddingscheme\"";
+		if ( fromCipher && (parts != 3)  ) {
+				// Indicates cipherXform was set based on Cipher.getAlgorithm()
+				// and thus may not be a *complete* cipher transformation.
+			if ( parts == 1 ) {
+				// Only algorithm was given.
+				cipherXform += "/ECB/NoPadding";
+			} else if ( parts == 2 ) {
+				// Only algorithm and mode was given.
+				cipherXform += "/NoPadding";
+			} else if ( parts == 3 ) {
+				// All three parts provided. Do nothing. Could happen if not compiled with
+				// assertions enabled.
+				;	// Do nothing - shown only for completeness.
+			} else {
+				// Should never happen unless Cipher implementation is totally screwed up.
+				throw new IllegalArgumentException("Cipher transformation '" +
+								cipherXform + "' must have form \"alg/mode/paddingscheme\"");
+			}
+		} else if ( !fromCipher && parts != 3 ) {
+			throw new IllegalArgumentException("Malformed cipherXform (" + cipherXform +
+											   "); must have form: \"alg/mode/paddingscheme\"");
+		}
+		assert cipherXform.split("/").length == 3 : "Implementation error setCipherTransformation()";
+		this.cipher_xform_ = cipherXform;
+		return this;
+	}
+	
+	/**
+	 * Get the cipher transformation.
+	 * @return	The cipher transformation {@code String}.
+	 */
+	public String getCipherTransformation() {
+		return cipher_xform_;
+	}
+
+	/**
+	 * Set the key size for this {@code CipherSpec}.
+	 * @param keySize	The key size, in bits. Must be positive integer.
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setKeySize(int keySize) {
+		assert keySize > 0 : "keySize must be > 0; keySize=" + keySize;
+		this.keySize_ = keySize;
+		return this;
+	}
+
+	/**
+	 * Retrieve the key size, in bits.
+	 * @return	The key size, in bits, is returned.
+	 */
+	public int getKeySize() {
+		return keySize_;
+	}
+
+	/**
+	 * Set the block size for this {@code CipherSpec}.
+	 * @param blockSize	The block size, in bytes. Must be positive integer.
+	 * @return	This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setBlockSize(int blockSize) {
+		assert blockSize > 0 : "blockSize must be > 0; blockSize=" + blockSize;
+		this.blockSize_ = blockSize;
+		return this;
+	}
+
+	/**
+	 * Retrieve the block size, in bytes.
+	 * @return	The block size, in bytes, is returned.
+	 */
+	public int getBlockSize() {
+		return blockSize_;
+	}
+
+	/**
+	 * Retrieve the cipher algorithm.
+	 * @return	The cipher algorithm.
+	 */
+	public String getCipherAlgorithm() {
+		return getFromCipherXform(CipherTransformationComponent.ALG);
+	}
+	
+	/**
+	 * Retrieve the cipher mode.
+	 * @return	The cipher mode.
+	 */
+	public String getCipherMode() {
+		return getFromCipherXform(CipherTransformationComponent.MODE);
+	}
+	
+	/**
+	 * Retrieve the cipher padding scheme.
+	 * @return	The padding scheme is returned.
+	 */
+	public String getPaddingScheme() {
+		return getFromCipherXform(CipherTransformationComponent.PADDING);
+	}
+	
+	/**
+	 * Retrieve the initialization vector (IV).
+	 * @return	The IV as a byte array.
+	 */
+	public byte[] getIV() {
+		return iv_;
+	}
+	
+	/**
+	 * Set the initialization vector (IV).
+	 * @param iv	The byte array to set as the IV. A copy of the IV is saved.
+	 * 				This parameter is ignored if the cipher mode does not
+	 * 				require an IV.
+	 * @return		This current {@code CipherSpec} object.
+	 */
+	public CipherSpec setIV(final byte[] iv) {
+		assert requiresIV() && (iv != null && iv.length != 0) : "Required IV cannot be null or 0 length";
+		// Don't store a reference, but make a copy!
+		if ( iv != null ) {	// Allow null IV for ECB mode.
+			iv_ = new byte[ iv.length ];
+			CryptoHelper.copyByteArray(iv, iv_);
+		}
+		return this;
+	}
+
+	/**
+	 * Return true if the cipher mode requires an IV.
+	 * @return True if the cipher mode requires an IV, otherwise false.
+	 * */
+	public boolean requiresIV() {
+		
+		String cm = getCipherMode();
+		
+		// Add any other cipher modes supported by JCE but not requiring IV.
+		// ECB is the only one I'm aware of that doesn't. Mode is not case
+		// sensitive.
+		if ( "ECB".equalsIgnoreCase(cm) ) {
+			return false;
+		}
+		return true;
+	}
+	
+	/**
+	 * Override {@code Object.toString()} to provide something more useful.
+	 * @return A meaningful string describing this object.
+	 */
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder("CipherSpec: ");
+		sb.append( getCipherTransformation() ).append("; keysize= ").append( getKeySize() );
+		sb.append(" bits; blocksize= ").append( getBlockSize() ).append(" bytes");
+		byte[] iv = getIV();
+		String ivLen = null;
+		if ( iv != null ) {
+			ivLen = "" + iv.length;	// Convert length to a string
+		} else {
+			ivLen = "[No IV present (not set or not required)]";
+		}
+		sb.append("; IV length = ").append( ivLen ).append(" bytes.");
+		return sb.toString();
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object other) {
+        boolean result = false;
+        if ( this == other )
+            return true;
+        if ( other == null )
+            return false;
+        if ( other instanceof CipherSpec) {
+            CipherSpec that = (CipherSpec)other;
+            result = (that.canEqual(this) &&
+                      NullSafe.equals(this.cipher_xform_, that.cipher_xform_) &&
+                      this.keySize_ == that.keySize_ &&
+                      this.blockSize_ == that.blockSize_ &&
+                        // Comparison safe from timing attacks.
+                      CryptoHelper.arrayCompare(this.iv_, that.iv_) );
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        StringBuilder sb = new StringBuilder();
+        sb.append( getCipherTransformation() );
+        sb.append( "" + getKeySize() );
+        sb.append( "" + getBlockSize() );
+        byte[] iv = getIV();
+        if ( iv != null && iv.length > 0 ) {
+            String ivStr = null;
+            try {
+                ivStr = new String(iv, "UTF-8");
+            }
+            catch(UnsupportedEncodingException ex) {
+                // Should never happen as UTF-8 encode supported by rt.jar,
+                // but it it does, just use default encoding.
+                ivStr = new String(iv);
+            }
+            sb.append( ivStr );
+        }
+        return sb.toString().hashCode();
+    }
+
+    /**
+     * Needed for correct definition of equals for general classes.
+     * (Technically not needed for 'final' classes like this class though; this
+     * will just allow it to work in the future should we decide to allow
+     * sub-classing of this class.)
+     * </p><p>
+     * See <a href="http://www.artima.com/lejava/articles/equality.html">
+     * How to write an Equality Method in Java</a>
+     * for full explanation.
+     * </p>
+     */
+    protected boolean canEqual(Object other) {
+        return (other instanceof CipherSpec);
+    }	
+	
+	/**
+	 * Split the current cipher transformation and return the requested part. 
+	 * @param component The component of the cipher transformation to return.
+	 * @return The cipher algorithm, cipher mode, or padding, as requested.
+	 */
+	private String getFromCipherXform(CipherTransformationComponent component) {
+        int part = component.ordinal();
+		String[] parts = getCipherTransformation().split("/");
+		assert parts.length == 3 : "Invalid cipher transformation: " + getCipherTransformation();	
+		return parts[part];
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java
new file mode 100644
index 0000000..47e6fc7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java
@@ -0,0 +1,873 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.crypto;
+
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.Iterator;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.EncryptionException;
+
+// CHECKME: Some of these assertions probably should be actual runtime checks
+//          with suitable exceptions to account for cases where programmers
+//          accidentally pass in byte arrays that are not really serialized
+//          CipherText objects (note: as per asPortableSerializedByteArra()).
+//          However, not sure what exception time is really suitable here.
+//          It probably should be a sub-class of RuntimeException, but
+//          IllegalArguementException doesn't really make sense here. Suggestions?
+
+/**
+ * A {@code Serializable} interface representing the result of encrypting
+ * plaintext and some additional information about the encryption algorithm,
+ * the IV (if pertinent), and an optional Message Authentication Code (MAC).
+ * </p><p>
+ * Note that while this class is {@code Serializable} in the usual Java sense,
+ * ESAPI uses {@link #asPortableSerializedByteArray()} for serialization. Not
+ * only is this serialization somewhat more compact, it is also portable
+ * across other ESAPI programming language implementations. However, Java
+ * serialization is supported in the event that one wishes to store
+ * {@code CipherText} in an {@code HttpSession} object.
+ * </p><p>
+ * Copyright © 2009 - The OWASP Foundation
+ * </p>
+ * @author kevin.w.wall at gmail.com
+ * @see PlainText
+ * @see org.owasp.esapi.Encryptor
+ * @since 2.0
+ */
+public final class CipherText implements Serializable {	
+    // NOTE: Do NOT change this in future versions, unless you are knowingly
+    //       making changes to the class that will render this class incompatible
+    //       with previously serialized objects from older versions of this class.
+	//		 If this is done, that you must provide for supporting earlier ESAPI versions.
+    //       Be wary making incompatible changes as discussed at:
+    //          http://java.sun.com/javase/6/docs/platform/serialization/spec/version.html#6678
+    //       Any incompatible change in the serialization of CipherText *must* be
+    //       reflected in the class CipherTextSerializer.
+    // This should be *same* version as in CipherTextSerializer and KeyDerivationFunction.
+	// If one changes, the other should as well to accommodate any differences.
+	//		Previous versions:	20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
+	//						    20130830 - Fix to issue #306 (release 2.1.0)
+	public  static final int cipherTextVersion = 20130830; // Format: YYYYMMDD, max is 99991231.
+		// Required by Serializable classes.
+	private static final long serialVersionUID = cipherTextVersion; // Format: YYYYMMDD
+	
+	private static final Logger logger = ESAPI.getLogger("CipherText");
+    
+    private CipherSpec cipherSpec_           = null;
+    private byte[]     raw_ciphertext_       = null;
+    private byte[]     separate_mac_         = null;
+    private long       encryption_timestamp_ = 0;
+    private int		   kdfVersion_           = KeyDerivationFunction.kdfVersion;
+    private int		   kdfPrfSelection_      = KeyDerivationFunction.getDefaultPRFSelection();
+
+    // All the various pieces that can be set, either directly or indirectly
+    // via CipherSpec.
+    private enum CipherTextFlags {
+        ALGNAME, CIPHERMODE, PADDING, KEYSIZE, BLOCKSIZE, CIPHERTEXT, INITVECTOR
+    }
+
+    // If we have everything set, we compare it to this using '==' which javac
+    // specially overloads for this.
+    private final EnumSet<CipherTextFlags> allCtFlags =
+        EnumSet.of(CipherTextFlags.ALGNAME,    CipherTextFlags.CIPHERMODE,
+                   CipherTextFlags.PADDING,    CipherTextFlags.KEYSIZE,
+                   CipherTextFlags.BLOCKSIZE,  CipherTextFlags.CIPHERTEXT,
+                   CipherTextFlags.INITVECTOR);
+    
+    // These are all the pieces we collect when passed a CipherSpec object.
+    private final EnumSet<CipherTextFlags> fromCipherSpec =
+        EnumSet.of(CipherTextFlags.ALGNAME,    CipherTextFlags.CIPHERMODE,
+                   CipherTextFlags.PADDING,    CipherTextFlags.KEYSIZE,
+                   CipherTextFlags.BLOCKSIZE);
+
+    // How much we've collected so far. We start out with having collected nothing.
+    private EnumSet<CipherTextFlags> progress = EnumSet.noneOf(CipherTextFlags.class);
+    
+    // Check if versions of KeyDerivationFunction, CipherText, and
+    // CipherTextSerializer are all the same.
+    {
+    	// Ignore error about comparing identical versions and dead code.
+    	// We expect them to be, but the point is to catch us if they aren't.
+    	assert CipherTextSerializer.cipherTextSerializerVersion == CipherText.cipherTextVersion :
+            "Versions of CipherTextSerializer and CipherText are not compatible.";
+    	assert CipherTextSerializer.cipherTextSerializerVersion == KeyDerivationFunction.kdfVersion :
+    		"Versions of CipherTextSerializer and KeyDerivationFunction are not compatible.";
+    }
+
+    ///////////////////////////  C O N S T R U C T O R S  /////////////////////////
+    
+    /**
+     * Default CTOR. Takes all the defaults from the ESAPI.properties, or
+     * default values from initial values from this class (when appropriate)
+     * when they are not set in ESAPI.properties.
+     */
+    public CipherText() {
+        cipherSpec_ = new CipherSpec(); // Uses default for everything but IV.
+        received(fromCipherSpec);
+    }
+    
+    /**
+     * Construct from a {@code CipherSpec} object. Still needs to have
+     * {@link #setCiphertext(byte[])} or {@link #setIVandCiphertext(byte[], byte[])}
+     * called to be usable.
+     * 
+     * @param cipherSpec The cipher specification to use.
+     */
+    public CipherText(final CipherSpec cipherSpec) {
+        cipherSpec_  = cipherSpec;
+        received(fromCipherSpec);
+        if ( cipherSpec.getIV() != null ) {
+            received(CipherTextFlags.INITVECTOR);
+        }
+    }
+    
+    /**
+     * Construct from a {@code CipherSpec} object and the raw ciphertext.
+     * 
+     * @param cipherSpec The cipher specification to use.
+     * @param cipherText The raw ciphertext bytes to use.
+     * @throws EncryptionException  Thrown if {@code cipherText} is null or
+     *                   empty array.
+     */
+    public CipherText(final CipherSpec cipherSpec, byte[] cipherText)
+        throws EncryptionException
+    {
+        cipherSpec_ = cipherSpec;
+        setCiphertext(cipherText);
+        received(fromCipherSpec);
+        if ( cipherSpec.getIV() != null ) {
+            received(CipherTextFlags.INITVECTOR);
+        }
+    }
+    
+    /** Create a {@code CipherText} object from what is supposed to be a
+     *  portable serialized byte array, given in network byte order, that
+     *  represents a valid, previously serialized {@code CipherText} object
+     *  using {@link #asPortableSerializedByteArray()}.
+     * @param bytes A byte array created via
+     *              {@code CipherText.asPortableSerializedByteArray()}
+     * @return A {@code CipherText} object reconstructed from the byte array.
+     * @throws EncryptionException
+     * @see #asPortableSerializedByteArray()
+     */     // DISCUSS: BTW, I detest this name. Suggestions???
+    public static CipherText fromPortableSerializedBytes(byte[] bytes)
+            throws EncryptionException
+    {
+        CipherTextSerializer cts = new CipherTextSerializer(bytes);
+        return cts.asCipherText();
+    }
+
+    /////////////////////////  P U B L I C   M E T H O D S  ////////////////////
+
+	/**
+	 * Obtain the String representing the cipher transformation used to encrypt
+	 * the plaintext. The cipher transformation represents the cipher algorithm,
+	 * the cipher mode, and the padding scheme used to do the encryption. An
+	 * example would be "AES/CBC/PKCS5Padding". See Appendix A in the
+	 * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
+	 * Java Cryptography Architecture Reference Guide</a>
+	 * for information about standard supported cipher transformation names.
+	 * <p>
+	 * The cipher transformation name is usually sufficient to be passed to
+	 * {@link javax.crypto.Cipher#getInstance(String)} to create a
+	 * <code>Cipher</code> object to decrypt the ciphertext.
+	 * 
+	 * @return The cipher transformation name used to encrypt the plaintext
+	 * 		   resulting in this ciphertext.
+	 */
+    public String getCipherTransformation() {
+        return cipherSpec_.getCipherTransformation();
+    }
+	
+	/**
+	 * Obtain the name of the cipher algorithm used for encrypting the
+	 * plaintext.
+	 * 
+	 * @return The name as the cryptographic algorithm used to perform the
+	 * 		   encryption resulting in this ciphertext.
+	 */
+    public String getCipherAlgorithm() {
+        return cipherSpec_.getCipherAlgorithm();
+    }
+	
+	/**
+	 * Retrieve the key size used with the cipher algorithm that was used to
+	 * encrypt data to produce this ciphertext.
+	 * 
+	 * @return The key size in bits. We work in bits because that's the crypto way!
+	 */
+    public int getKeySize() {
+        return cipherSpec_.getKeySize();
+    }
+	
+	/**
+	 * Retrieve the block size (in bytes!) of the cipher used for encryption.
+	 * (Note: If an IV is used, this will also be the IV length.)
+	 * 
+	 * @return The block size in bytes. (Bits, bytes! It's confusing I know. Blame
+	 * 									the cryptographers; we've just following
+	 * 									convention.)
+	 */
+    public int getBlockSize() {
+        return cipherSpec_.getBlockSize();
+    }
+	
+	/**
+	 * Get the name of the cipher mode used to encrypt some plaintext.
+	 * 
+	 * @return The name of the cipher mode used to encrypt the plaintext
+	 *         resulting in this ciphertext. E.g., "CBC" for "cipher block
+	 *         chaining", "ECB" for "electronic code book", etc.
+	 */
+    public String getCipherMode() {
+        return cipherSpec_.getCipherMode();
+    }
+	
+	/**
+	 * Get the name of the padding scheme used to encrypt some plaintext.
+	 * 
+	 * @return The name of the padding scheme used to encrypt the plaintext
+	 * 		   resulting in this ciphertext. Example: "PKCS5Padding". If no
+	 * 		   padding was used "None" is returned.
+	 */
+    public String getPaddingScheme() {
+        return cipherSpec_.getPaddingScheme();
+    }
+	
+	/**
+	 * Return the initialization vector (IV) used to encrypt the plaintext
+	 * if applicable.
+	 *  
+	 * @return	The IV is returned if the cipher mode used to encrypt the
+	 * 			plaintext was not "ECB". ECB mode does not use an IV so in
+	 * 			that case, <code>null</code> is returned.
+	 */
+    public byte[] getIV() {
+        if ( isCollected(CipherTextFlags.INITVECTOR) ) {
+            return cipherSpec_.getIV();
+        } else {
+            logger.error(Logger.SECURITY_FAILURE, "IV not set yet; unable to retrieve; returning null");
+            return null;
+        }
+    }
+	
+	/** 
+	 * Return true if the cipher mode used requires an IV. Usually this will
+	 * be true unless ECB mode (which should be avoided whenever possible) is
+	 * used.
+	 */
+    public boolean requiresIV() {
+        return cipherSpec_.requiresIV();
+    }
+	
+	/**
+	 * Get the raw ciphertext byte array resulting from encrypting some
+	 * plaintext.
+	 * 
+	 * @return A copy of the raw ciphertext as a byte array.
+	 */
+	public byte[] getRawCipherText() {
+	    if ( isCollected(CipherTextFlags.CIPHERTEXT) ) {
+	        byte[] copy = new byte[ raw_ciphertext_.length ];
+	        System.arraycopy(raw_ciphertext_, 0, copy, 0, raw_ciphertext_.length);
+	        return copy;
+	    } else {
+	        logger.error(Logger.SECURITY_FAILURE, "Raw ciphertext not set yet; unable to retrieve; returning null");
+	        return null;
+	    }
+	}
+	
+	/**
+	 * Get number of bytes in raw ciphertext. Zero is returned if ciphertext has not
+	 * yet been stored.
+	 * 
+	 * @return The number of bytes of raw ciphertext; 0 if no raw ciphertext has been stored.
+	 */
+	public int getRawCipherTextByteLength() {
+	    if ( raw_ciphertext_ != null ) {
+	        return raw_ciphertext_.length;
+	    } else {
+	        return 0;
+	    }
+	}
+
+	/**
+	 * Return a base64-encoded representation of the raw ciphertext alone. Even
+	 * in the case where an IV is used, the IV is not prepended before the
+	 * base64-encoding is performed.
+	 * <p>
+	 * If there is a need to store an encrypted value, say in a database, this
+	 * is <i>not</i> the method you should use unless you are using a <i>fixed</i>
+	 * IV or are planning on retrieving the IV and storing it somewhere separately
+	 * (e.g., a different database column). If you are <i>not</i> using a fixed IV
+	 * (which is <strong>highly</strong> discouraged), you should normally use
+	 * {@link #getEncodedIVCipherText()} instead.
+	 * </p>
+	 * @see #getEncodedIVCipherText()
+	 */
+	public String getBase64EncodedRawCipherText() {
+	    return ESAPI.encoder().encodeForBase64(getRawCipherText(),false);
+	}
+	
+	/**
+	 * Return the ciphertext as a base64-encoded <code>String</code>. If an
+	 * IV was used, the IV if first prepended to the raw ciphertext before
+	 * base64-encoding. If an IV is not used, then this method returns the same
+	 * value as {@link #getBase64EncodedRawCipherText()}.
+	 * <p>
+	 * Generally, this is the method that you should use unless you only
+	 * are using a fixed IV and a storing that IV separately, in which case
+	 * using {@link #getBase64EncodedRawCipherText()} can reduce the storage
+	 * overhead.
+	 * </p>
+	 * @return The base64-encoded ciphertext or base64-encoded IV + ciphertext.
+	 * @see #getBase64EncodedRawCipherText()
+	 */
+	public String getEncodedIVCipherText() {
+	    if ( isCollected(CipherTextFlags.INITVECTOR) && isCollected(CipherTextFlags.CIPHERTEXT) ) {
+	        // First concatenate IV + raw ciphertext
+	        byte[] iv = getIV();
+	        byte[] raw = getRawCipherText();
+	        byte[] ivPlusCipherText = new byte[ iv.length + raw.length ];
+	        System.arraycopy(iv, 0, ivPlusCipherText, 0, iv.length);
+	        System.arraycopy(raw, 0, ivPlusCipherText, iv.length, raw.length);
+	        // Then return the base64 encoded result
+	        return ESAPI.encoder().encodeForBase64(ivPlusCipherText, false);
+	    } else {
+	        logger.error(Logger.SECURITY_FAILURE, "Raw ciphertext and/or IV not set yet; unable to retrieve; returning null");
+	        return null;
+	    }
+	}
+
+	/**
+	 * Compute and store the Message Authentication Code (MAC) if the ESAPI property
+	 * {@code Encryptor.CipherText.useMAC} is set to {@code true}. If it
+	 * is, the MAC is conceptually calculated as:
+	 * <pre>
+	 * 		authKey = DerivedKey(secret_key, "authenticate")
+	 * 		HMAC-SHA1(authKey, IV + secret_key)
+	 * </pre>
+	 * where derived key is an HMacSHA1, possibly repeated multiple times.
+	 * (See {@link org.owasp.esapi.crypto.CryptoHelper#computeDerivedKey(SecretKey, int, String)}
+	 * for details.)
+	 * </p><p>
+	 * <b>Perceived Benefits</b>: There are certain cases where if an attacker
+	 * is able to change the IV. When one uses a authenticity key that is
+	 * derived from the "master" key, it also makes it possible to know when
+	 * the incorrect key was attempted to be used to decrypt the ciphertext.
+	 * </p><p>
+	 * <b>NOTE:</b> The purpose of this MAC (which is always computed by the
+	 * ESAPI reference model implementing {@code Encryptor}) is to ensure the
+	 * authenticity of the IV and ciphertext. Among other things, this prevents
+	 * an adversary from substituting the IV with one of their own choosing.
+	 * Because we don't know whether or not the recipient of this {@code CipherText}
+	 * object will want to validate the authenticity or not, the reference
+	 * implementation of {@code Encryptor} always computes it and includes it.
+	 * The recipient of the ciphertext can then choose whether or not to validate
+	 * it.
+	 * 
+	 * @param authKey The secret key that is used for proving authenticity of
+	 * 				the IV and ciphertext. This key should be derived from
+	 * 				the {@code SecretKey} passed to the
+	 * 				{@link Encryptor#encrypt(javax.crypto.SecretKey, PlainText)}
+	 *				and
+	 *				{@link Encryptor#decrypt(javax.crypto.SecretKey, CipherText)}
+	 *				methods or the "master" key when those corresponding
+	 *				encrypt / decrypt methods are used. This authenticity key
+	 *				should be the same length and for the same cipher algorithm
+	 *				as this {@code SecretKey}. The method
+	 *				{@link org.owasp.esapi.crypto.CryptoHelper#computeDerivedKey(SecretKey, int, String)}
+	 *				is a secure way to produce this derived key.
+	 */		// DISCUSS - Cryptographers David Wagner, Ian Grigg, and others suggest
+			// computing authenticity using derived key and HmacSHA1 of IV + ciphertext.
+			// However they also argue that what should be returned and treated as
+			// (i.e., stored as) ciphertext would be something like this:
+			//		len_of_raw_ciphertext + IV + raw_ciphertext + MAC
+			// TODO: Need to do something like this for custom serialization and then
+	        // document order / format so it can be used by other ESAPI implementations.
+	public void computeAndStoreMAC(SecretKey authKey) {
+	    assert !macComputed() : "Programming error: Can't store message integrity code " +
+	                            "while encrypting; computeAndStoreMAC() called multiple times.";
+	    assert collectedAll() : "Have not collected all required information to compute and store MAC.";
+	    byte[] result = computeMAC(authKey);
+	    if ( result != null ) {
+	        storeSeparateMAC(result);
+	    }
+	    // If 'result' is null, we already logged this in computeMAC().
+	}
+	
+	/**
+	 * Same as {@link #computeAndStoreMAC(SecretKey)} but this is only used by
+	 * {@code CipherTextSerializeer}. (Has package level access.)
+	 */ // CHECKME: For this to be "safe", it requires ESAPI jar to be sealed.
+	void storeSeparateMAC(byte[] macValue) {
+	    if ( !macComputed() ) {
+	        separate_mac_ = new byte[ macValue.length ];
+	        CryptoHelper.copyByteArray(macValue, separate_mac_);
+	        assert macComputed();
+	    }
+	}
+	
+	/**
+	 * Validate the message authentication code (MAC) associated with the ciphertext.
+	 * This is mostly meant to ensure that an attacker has not replaced the IV
+	 * or raw ciphertext with something arbitrary. Note however that it will
+	 * <i>not</i> detect the case where an attacker simply substitutes one
+	 * valid ciphertext with another ciphertext.
+	 * 
+	 * @param authKey The secret key that is used for proving authenticity of
+	 * 				the IV and ciphertext. This key should be derived from
+	 * 				the {@code SecretKey} passed to the
+	 * 				{@link Encryptor#encrypt(javax.crypto.SecretKey, PlainText)}
+	 *				and
+	 *				{@link Encryptor#decrypt(javax.crypto.SecretKey, CipherText)}
+	 *				methods or the "master" key when those corresponding
+	 *				encrypt / decrypt methods are used. This authenticity key
+	 *				should be the same length and for the same cipher algorithm
+	 *				as this {@code SecretKey}. The method
+	 *				{@link org.owasp.esapi.crypto.CryptoHelper#computeDerivedKey(SecretKey, int, String)}
+	 *				is a secure way to produce this derived key.
+	 * @return True if the ciphertext has not be tampered with, and false otherwise.
+	 */
+	public boolean validateMAC(SecretKey authKey) {
+	    boolean requiresMAC = ESAPI.securityConfiguration().useMACforCipherText();
+
+	    if (  requiresMAC && macComputed() ) {  // Uses MAC and it was computed
+	        // Calculate MAC from HMAC-SHA1(nonce, IV + plaintext) and
+	        // compare to stored value (separate_mac_). If same, then return true,
+	        // else return false.
+	        byte[] mac = computeMAC(authKey);
+	        assert mac.length == separate_mac_.length : "MACs are of differnt lengths. Should both be the same.";
+	        return CryptoHelper.arrayCompare(mac, separate_mac_); // Safe compare!!!
+	    } else if ( ! requiresMAC ) {           // Doesn't require a MAC
+	        return true;
+	    } else {
+	    		// This *used* to be the case (for versions 2.0 and 2.0.1) where we tried to
+	    		// accomodate the deprecated decrypt() method from ESAPI 1.4. Unfortunately,
+	    		// that was an EPIC FAIL. (See Google Issue # 306 for details.)
+	        logger.warning(Logger.SECURITY_FAILURE, "MAC may have been tampered with (e.g., length set to 0).");
+	        return false;    // Deprecated decrypt() method removed, so now return false.
+	    }
+	}
+	
+	/**
+	 * Return this {@code CipherText} object as a portable (i.e., network byte
+	 * ordered) serialized byte array. Note this is <b>not</b> the same as
+	 * returning a serialized object using Java serialization. Instead this
+	 * is a representation that all ESAPI implementations will use to pass
+	 * ciphertext between different programming language implementations.
+	 * 
+	 * @return A network byte-ordered serialized representation of this object.
+	 * @throws EncryptionException
+	 */    // DISCUSS: This method name sucks too. Suggestions???
+	public byte[] asPortableSerializedByteArray() throws EncryptionException {
+        // Check if this CipherText object is "complete", i.e., all
+        // mandatory has been collected.
+	    if ( ! collectedAll() ) {
+	        String msg = "Can't serialize this CipherText object yet as not " +
+	                     "all mandatory information has been collected";
+	        throw new EncryptionException("Can't serialize incomplete ciphertext info", msg);
+	    }
+	    
+	    // If we are supposed to be using a (separate) MAC, also make sure
+	    // that it has been computed/stored.
+	    boolean requiresMAC = ESAPI.securityConfiguration().useMACforCipherText();
+	    if (  requiresMAC && ! macComputed() ) {
+	        String msg = "Programming error: MAC is required for this cipher mode (" +
+	                     getCipherMode() + "), but MAC has not yet been " +
+	                     "computed and stored. Call the method " +
+	                     "computeAndStoreMAC(SecretKey) first before " +
+	                     "attempting serialization.";
+	        throw new EncryptionException("Can't serialize ciphertext info: Data integrity issue.",
+	                                      msg);
+	    }
+	    
+	    // OK, everything ready, so give it a shot.
+	    return new CipherTextSerializer(this).asSerializedByteArray();
+	}
+	
+    ///// Setters /////
+    /**
+     * Set the raw ciphertext.
+     * @param ciphertext    The raw ciphertext.
+     * @throws EncryptionException  Thrown if the MAC has already been computed
+     *              via {@link #computeAndStoreMAC(SecretKey)}.
+     */
+    public void setCiphertext(byte[] ciphertext)
+        throws EncryptionException
+    {
+        if ( ! macComputed() ) {
+            if ( ciphertext == null || ciphertext.length == 0 ) {
+                throw new EncryptionException("Encryption faled; no ciphertext",
+                                              "Ciphertext may not be null or 0 length!");
+            }
+            if ( isCollected(CipherTextFlags.CIPHERTEXT) ) {
+                logger.warning(Logger.SECURITY_FAILURE, "Raw ciphertext was already set; resetting.");
+            }
+            raw_ciphertext_ = new byte[ ciphertext.length ];
+            CryptoHelper.copyByteArray(ciphertext, raw_ciphertext_);
+            received(CipherTextFlags.CIPHERTEXT);
+            setEncryptionTimestamp();
+        } else {
+            String logMsg = "Programming error: Attempt to set ciphertext after MAC already computed.";
+            logger.error(Logger.SECURITY_FAILURE, logMsg);
+            throw new EncryptionException("MAC already set; cannot store new raw ciphertext", logMsg);
+        }
+    }
+    
+    /**
+     * Set the IV and raw ciphertext.
+     * @param iv            The initialization vector.
+     * @param ciphertext    The raw ciphertext.
+     * @throws EncryptionException
+     */
+    public void setIVandCiphertext(byte[] iv, byte[] ciphertext)
+        throws EncryptionException
+    {
+        if ( isCollected(CipherTextFlags.INITVECTOR) ) {
+            logger.warning(Logger.SECURITY_FAILURE, "IV was already set; resetting.");
+        }
+        if ( isCollected(CipherTextFlags.CIPHERTEXT) ) {
+            logger.warning(Logger.SECURITY_FAILURE, "Raw ciphertext was already set; resetting.");
+        }
+        if ( ! macComputed() ) {
+            if ( ciphertext == null || ciphertext.length == 0 ) {
+                throw new EncryptionException("Encryption faled; no ciphertext",
+                                              "Ciphertext may not be null or 0 length!");
+            }
+            if ( iv == null || iv.length == 0 ) {
+                if ( requiresIV() ) {
+                    throw new EncryptionException("Encryption failed -- mandatory IV missing", // DISCUSS - also log? See below.
+                                                  "Cipher mode " + getCipherMode() + " has null or empty IV");
+                }
+            } else if ( iv.length != getBlockSize() ) {
+                    throw new EncryptionException("Encryption failed -- bad parameters passed to encrypt",  // DISCUSS - also log? See below.
+                                                  "IV length does not match cipher block size of " + getBlockSize());
+            }
+            cipherSpec_.setIV(iv);
+            received(CipherTextFlags.INITVECTOR);
+            setCiphertext( ciphertext );
+        } else {
+            String logMsg = "MAC already computed from previously set IV and raw ciphertext; may not be reset -- object is immutable.";
+            logger.error(Logger.SECURITY_FAILURE, logMsg);  // Discuss: By throwing, this gets logged as warning, but it's really error! Why is an exception only a warning???
+            throw new EncryptionException("Validation of decryption failed.", logMsg);
+        }
+    }
+    
+    public int getKDFVersion() {
+    	return kdfVersion_;
+    }
+
+    public void setKDFVersion(int vers) {
+    	CryptoHelper.isValidKDFVersion(vers, false, true);
+    	kdfVersion_ = vers;
+    }
+    
+    public KeyDerivationFunction.PRF_ALGORITHMS getKDF_PRF() {
+    	return KeyDerivationFunction.convertIntToPRF(kdfPrfSelection_);
+    }
+
+    int kdfPRFAsInt() {
+    	return kdfPrfSelection_;
+    }
+    
+    public void setKDF_PRF(int prfSelection) {
+        assert prfSelection >= 0 && prfSelection <= 15 : "kdfPrf == " + prfSelection + " must be between 0 and 15.";
+    	kdfPrfSelection_ = prfSelection;
+    }
+    
+    /** Get stored time stamp representing when data was encrypted. */
+    public long getEncryptionTimestamp() {
+        return encryption_timestamp_;
+    }
+    
+    /**
+     * Set the encryption timestamp to the current system time as determined by
+     * {@code System.currentTimeMillis()}, but only if it has not been previously
+     * set. That is, this method ony has an effect the first time that it is
+     * called for this object.
+     */
+    private void setEncryptionTimestamp() {
+        // We want to skip this when it's already been set via the package
+        // level call setEncryptionTimestamp(long) done via CipherTextSerializer
+        // otherwise it gets reset to the current time. But when it's restored
+        // from a serialized CipherText object, we want to keep the original
+        // encryption timestamp.
+        if ( encryption_timestamp_ != 0 ) {
+            logger.warning(Logger.EVENT_FAILURE, "Attempt to reset non-zero " +
+                    "CipherText encryption timestamp to current time!");
+        }
+        encryption_timestamp_ = System.currentTimeMillis();
+    }
+ 
+    /**
+     * Set the encryption timestamp to the time stamp specified by the parameter.
+     * </p><p>
+     * This method is intended for use only by {@code CipherTextSerializer}.
+     * 
+     * @param timestamp The time in milliseconds since epoch time (midnight,
+     *                  January 1, 1970 GMT).
+     */ // Package level access. ESAPI jar should be sealed and signed.
+    void setEncryptionTimestamp(long timestamp) {
+        assert timestamp > 0 : "Timestamp must be greater than zero.";
+        if ( encryption_timestamp_ == 0 ) {     // Only set it if it's not yet been set.
+            logger.warning(Logger.EVENT_FAILURE, "Attempt to reset non-zero " +
+                           "CipherText encryption timestamp to " + new Date(timestamp) + "!");
+        }
+        encryption_timestamp_ = timestamp;
+    }
+    
+    /** Used in supporting {@code CipherText} serialization.
+     * @deprecated	Use {@code CipherText.cipherTextVersion} instead. Will
+     * 				disappear as of ESAPI 2.1.
+     */
+    public static long getSerialVersionUID() {
+        return CipherText.serialVersionUID;
+    }
+    
+    /** Return the separately calculated Message Authentication Code (MAC) that
+     * is computed via the {@code computeAndStoreMAC(SecretKey authKey)} method.
+     * @return The copy of the computed MAC, or {@code null} if one is not used.
+     */
+    public byte[] getSeparateMAC() {
+        if ( separate_mac_ == null ) {
+            return null;
+        }
+        byte[] copy = new byte[ separate_mac_.length ];
+        System.arraycopy(separate_mac_, 0, copy, 0, separate_mac_.length);
+        return copy;   
+    }
+    
+    /**
+     * More useful {@code toString()} method.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder( "CipherText: " );
+        String creationTime = (( getEncryptionTimestamp() == 0) ? "No timestamp available" :
+                                (new Date(getEncryptionTimestamp())).toString());
+        int n = getRawCipherTextByteLength();
+        String rawCipherText = (( n > 0 ) ? "present (" + n + " bytes)" : "absent");
+        String mac = (( separate_mac_ != null ) ? "present" : "absent");
+        sb.append("Creation time: ").append(creationTime);
+        sb.append(", raw ciphertext is ").append(rawCipherText);
+        sb.append(", MAC is ").append(mac).append("; ");
+        sb.append( cipherSpec_.toString() );
+        return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public boolean equals(Object other) {
+        boolean result = false;
+        if ( this == other )
+            return true;
+        if ( other == null )
+            return false;
+        if ( other instanceof CipherText) {
+            CipherText that = (CipherText)other;
+            if ( this.collectedAll() && that.collectedAll() ) {
+                result = (that.canEqual(this) &&
+                          this.cipherSpec_.equals(that.cipherSpec_) &&
+                            // Safe comparison, resistant to timing attacks
+                          CryptoHelper.arrayCompare(this.raw_ciphertext_, that.raw_ciphertext_) &&
+                          CryptoHelper.arrayCompare(this.separate_mac_, that.separate_mac_) &&
+                          this.encryption_timestamp_ == that.encryption_timestamp_ );
+            } else {
+                logger.warning(Logger.EVENT_FAILURE, "CipherText.equals(): Cannot compare two " +
+                               "CipherText objects that are not complete, and therefore immutable!");
+                logger.info(Logger.EVENT_FAILURE, "This CipherText: " + this.collectedAll() + ";" +
+                            "other CipherText: " + that.collectedAll());
+                logger.info(Logger.EVENT_FAILURE, "CipherText.equals(): Progress comparison: " +
+                               ((this.progress == that.progress) ? "Same" : "Different"));
+                logger.info(Logger.EVENT_FAILURE, "CipherText.equals(): Status this: " + this.progress +
+                               "; status other CipherText object: " + that.progress);
+                // CHECKME: Perhaps we should throw a RuntimeException instead???
+                return false;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override public int hashCode() {
+        if ( this.collectedAll() ) {
+                logger.warning(Logger.EVENT_FAILURE, "CipherText.hashCode(): Cannot compute " +
+                               "hachCode() of incomplete CipherText object; object not immutable- " +
+                               "returning 0.");
+                // CHECKME: Throw RuntimeException instead?
+                return 0;
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append( cipherSpec_.hashCode() );
+        sb.append( encryption_timestamp_ );
+        String raw_ct = null;
+        String mac = null;
+        try {
+            raw_ct = new String(raw_ciphertext_, "UTF-8");
+                // Remember, MAC is optional even when CipherText is complete.
+            mac = new String( ((separate_mac_ != null) ? separate_mac_ : new byte[] { }), "UTF-8");
+        } catch(UnsupportedEncodingException ex) {
+            // Should never happen as UTF-8 encode supported by rt.jar,
+            // but it it does, just use default encoding.
+            raw_ct = new String(raw_ciphertext_);
+            mac = new String( ((separate_mac_ != null) ? separate_mac_ : new byte[] { }));
+        }
+        sb.append( raw_ct );
+        sb.append( mac );
+        return sb.toString().hashCode();
+    }
+
+    /**
+     * Needed for correct definition of equals for general classes.
+     * (Technically not needed for 'final' classes though like this class
+     * though; this will just allow it to work in the future should we
+     * decide to allow * sub-classing of this class.)
+     * </p><p>
+     * See {@link http://www.artima.com/lejava/articles/equality.html}
+     * for full explanation.
+     * </p>
+     */
+    protected boolean canEqual(Object other) {
+        return (other instanceof CipherText);
+    }
+
+    ////////////////////////////////////  P R I V A T E  /////////////////////////////////////////
+    
+    /**
+     * Compute a MAC, but do not store it. May set the nonce value as a
+     * side-effect.  The MAC is calculated as:
+     * <pre>
+     *      HMAC-SHA1(nonce, IV + plaintext)
+     * </pre>
+     * @param ciphertext    The ciphertext value for which the MAC is computed.
+     * @return The value for the MAC.
+     */ 
+    private byte[] computeMAC(SecretKey authKey) {
+        assert raw_ciphertext_ != null && raw_ciphertext_.length != 0 : "Raw ciphertext may not be null or empty.";
+        assert authKey != null && authKey.getEncoded().length != 0 : "Authenticity secret key may not be null or zero length.";
+        try {
+        	// IMPORTANT NOTE: The NSA review was (apparently) OK with using HmacSHA1
+        	// to calculate the MAC that ensures authenticity of the IV+ciphertext.
+        	// (Not true of calculation of the use HmacSHA1 for the KDF though.) Therefore,
+        	// we did not make this configurable. Note also that choosing an improved
+        	// MAC algorithm here would cause the overall length of the serialized ciphertext
+        	// to be just that much longer, which is probably unacceptable when encrypting
+        	// short strings.
+            SecretKey sk = new SecretKeySpec(authKey.getEncoded(), "HmacSHA1");
+            Mac mac = Mac.getInstance("HmacSHA1");
+            mac.init(sk);
+            if ( requiresIV() ) {
+                mac.update( getIV() );
+            }
+            byte[] result = mac.doFinal( getRawCipherText() );
+            return result;
+        } catch (NoSuchAlgorithmException e) {
+            logger.error(Logger.SECURITY_FAILURE, "Cannot compute MAC w/out HmacSHA1.", e);
+            return null;
+        } catch (InvalidKeyException e) {
+            logger.error(Logger.SECURITY_FAILURE, "Cannot comput MAC; invalid 'key' for HmacSHA1.", e);
+            return null;
+        }
+    }
+    
+    /**
+     * Return true if the MAC has already been computed (i.e., not null).
+     */
+    private boolean macComputed() {
+        return (separate_mac_ != null);
+    }
+
+    /**
+     * Return true if we've collected all the required pieces; otherwise false.
+     */
+    private boolean collectedAll() {
+        EnumSet<CipherTextFlags> ctFlags = null;
+        if ( requiresIV() ) {
+            ctFlags = allCtFlags;
+        } else {
+            EnumSet<CipherTextFlags> initVector = EnumSet.of(CipherTextFlags.INITVECTOR);
+            ctFlags = EnumSet.complementOf(initVector);
+        }
+        boolean result = progress.containsAll(ctFlags);  
+        return result;
+    }
+
+    /** Check if we've collected a specific flag type.
+     * @param flag  The flag type; e.g., {@code CipherTextFlags.INITVECTOR}, etc.
+     * @return  Return true if we've collected a specific flag type; otherwise false.
+     */
+    private boolean isCollected(CipherTextFlags flag) {
+        return progress.contains(flag);
+    }
+
+    /**
+     * Add the flag to the set of what we've already collected.
+     * @param flag  The flag type to be added; e.g., {@code CipherTextFlags.INITVECTOR}.
+     */
+    private void received(CipherTextFlags flag) {
+        progress.add(flag);
+    }
+    
+    /**
+     * Add all the flags from the specified set to that we've collected so far.
+     * @param ctSet A {@code EnumSet<CipherTextFlags>} containing all the flags
+     *              we wish to add.
+     */
+    private void received(EnumSet<CipherTextFlags> ctSet) {
+        Iterator<CipherTextFlags> it = ctSet.iterator();
+        while ( it.hasNext() ) {
+            received( it.next() );
+        }
+    }
+
+    /**
+     * Based on the KDF version and the selected MAC algorithm for the KDF PRF,
+     * calculate the 32-bit quantity representing these.
+     * @return	A 4-byte (octet) quantity representing the KDF version and the
+     * 			MAC algorithm used for the KDF's Pseudo-Random Function.
+     * @see <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf">Format of portable serialization of org.owasp.esapi.crypto.CipherText object (pg 2)</a>
+     */
+	public int getKDFInfo() {
+		final int unusedBit28 = 0x8000000;  // 1000000000000000000000000000
+		
+		// 		kdf version is bits 1-27, bit 28 (reserved) should be 0, and
+		//		bits 29-32 are the MAC algorithm indicating which PRF to use for the KDF.
+		int kdfVers = this.getKDFVersion();
+		assert CryptoHelper.isValidKDFVersion(kdfVers, true, false);
+		int kdfInfo = kdfVers;
+		int macAlg = kdfPRFAsInt();
+		assert macAlg >= 0 && macAlg <= 15 : "MAC algorithm indicator must be between 0 to 15 inclusion; value is: " + macAlg;
+		
+	    // Make sure bit28 is cleared. (Reserved for future use.)
+	    kdfInfo &= ~unusedBit28;
+
+	    // Set MAC algorithm bits in high (MSB) nibble.
+	    kdfInfo |= (macAlg << 28);
+
+		return kdfInfo;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java b/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java
new file mode 100644
index 0000000..2a6caeb
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java
@@ -0,0 +1,415 @@
+package org.owasp.esapi.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.util.ByteConversionUtil;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Helper class to assist with programming language and platform independent
+ * serialization of {@link CipherText} objects. The serialization is done in
+ * network-byte order which is the same as big-endian byte order.
+ * <p>
+ * This serialization scheme is documented in
+ * <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf">
+ * <code>Format of Portable Serialization of org.owasp.esapi.crypto.CipherText Objects</code>.</a>
+ * Other serialization schemes may be desirable and could be supported (notably, RFC 5083 - Cryptographic
+ * Message Syntax (CMS) Authenticated-Enveloped-Data Content Type, or CMS' predecessor,
+ * PKCS#7 (RFC 2315)), but these serialization schemes are by comparison very complicated,
+ * and do not have extensive support for the various implementation languages which ESAPI
+ * supports. (Perhaps wishful thinking that other ESAPI implementations such as
+ * ESAPI for .NET, ESAPI for C, ESAPI for C++, etc. will all support a single, common
+ * serialization technique so they could exchange encrypted data.)
+ * 
+ * @author kevin.w.wall at gmail.com
+ *
+ */
+public class CipherTextSerializer {
+    // This should be *same* version as in CipherText & KeyDerivationFunction as
+	// these versions all need to work together.  Therefore, when one changes one
+	// one these versions, the other should be reviewed and changed as well to
+	// accommodate any differences.
+	//		Previous versions:	20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
+	//						    20130830 - Fix to issue #306 (release 2.1.0)
+	// We check that in an static initialization block (when assertions are enabled)
+	// below.
+	public  static final  int cipherTextSerializerVersion = 20130830; // Current version. Format: YYYYMMDD, max is 99991231.
+    private static final long serialVersionUID = cipherTextSerializerVersion;
+
+    private static final Logger logger = ESAPI.getLogger("CipherTextSerializer");
+    
+    private CipherText cipherText_ = null;
+    
+    // Check if versions of KeyDerivationFunction, CipherText, and
+    // CipherTextSerializer are all the same.
+    {
+    	// Ignore error about comparing identical versions and dead code.
+    	// We expect them to be, but the point is to catch us if they aren't.
+    	assert CipherTextSerializer.cipherTextSerializerVersion == CipherText.cipherTextVersion :
+            "Versions of CipherTextSerializer and CipherText are not compatible.";
+    	assert CipherTextSerializer.cipherTextSerializerVersion == KeyDerivationFunction.kdfVersion :
+    		"Versions of CipherTextSerializer and KeyDerivationFunction are not compatible.";
+    }
+    
+    public CipherTextSerializer(CipherText cipherTextObj) {
+    	if ( cipherTextObj == null ) {
+    		throw new IllegalArgumentException("CipherText object must not be null.");
+    	}
+        assert cipherTextObj != null : "CipherText object must not be null.";      
+        cipherText_ = cipherTextObj;
+    }
+    
+    /**
+     * Given byte array in network byte order (i.e., big-endian order), convert
+     * it so that a {@code CipherText} can be constructed from it.
+     * @param cipherTextSerializedBytes A serialized {@code CipherText} object
+     *          with the bytes in network byte order.
+     * @throws EncryptionException Thrown if a valid {@code CipherText} object
+     *          cannot be reconstructed from the byte array.
+     */
+    public CipherTextSerializer(byte[] cipherTextSerializedBytes)
+        throws EncryptionException /* DISCUSS: Change exception type?? */
+    {
+        cipherText_ = convertToCipherText(cipherTextSerializedBytes);
+    }
+
+    /** Return this {@code CipherText} object as a specialized, portable
+     *  serialized byte array.
+     * @return A serialization of this object. Note that this is <i>not</i> the
+     * Java serialization.
+     */
+    public byte[] asSerializedByteArray() {
+        int kdfInfo = cipherText_.getKDFInfo();
+        debug("asSerializedByteArray: kdfInfo = " + kdfInfo);
+        long timestamp = cipherText_.getEncryptionTimestamp();
+        String cipherXform = cipherText_.getCipherTransformation();
+        assert cipherText_.getKeySize() < Short.MAX_VALUE :
+                            "Key size too large. Max is " + Short.MAX_VALUE;
+        short keySize = (short) cipherText_.getKeySize();
+        assert cipherText_.getBlockSize() < Short.MAX_VALUE :
+                            "Block size too large. Max is " + Short.MAX_VALUE;
+        short blockSize = (short) cipherText_.getBlockSize();
+        byte[] iv = cipherText_.getIV();
+        assert iv.length < Short.MAX_VALUE :
+                            "IV size too large. Max is " + Short.MAX_VALUE;
+        short ivLen = (short) iv.length;
+        byte[] rawCiphertext = cipherText_.getRawCipherText();
+        int ciphertextLen = rawCiphertext.length;
+        assert ciphertextLen >= 1 : "Raw ciphertext length must be >= 1 byte.";
+        byte[] mac = cipherText_.getSeparateMAC();
+        assert mac.length < Short.MAX_VALUE :
+                            "MAC length too large. Max is " + Short.MAX_VALUE;
+        short macLen = (short) mac.length;
+        
+        byte[] serializedObj = computeSerialization(kdfInfo,
+                                                    timestamp,
+                                                    cipherXform,
+                                                    keySize,
+                                                    blockSize,
+                                                    ivLen,
+                                                    iv,
+                                                    ciphertextLen,
+                                                    rawCiphertext,
+                                                    macLen,
+                                                    mac
+                                                   );
+        
+        return serializedObj;
+    }
+    
+    /**
+     * Return the actual {@code CipherText} object.
+     * @return The {@code CipherText} object that we are serializing.
+     */
+    public CipherText asCipherText() {
+    	assert cipherText_ != null;
+        return cipherText_;
+    }
+      
+    /**
+     * Take all the individual elements that make of the serialized ciphertext
+     * format and put them in order and return them as a byte array.
+     * @param kdfInfo	Info about the KDF... which PRF and the KDF version {@link #asCipherText()}.
+     * @param timestamp	Timestamp when the data was encrypted. Intended to help
+     * 					facilitate key change operations and nothing more. If it is meaningless,
+     * 					then the expectations are just that the recipient should ignore it. Mostly
+     * 					intended when encrypted data is kept long term over a period of many
+     * 					key change operations.
+     * @param cipherXform	Details of how the ciphertext was encrypted. The format used
+     * 						is the same as used by {@code javax.crypto.Cipher}, namely,
+     * 						"cipherAlg/cipherMode/paddingScheme".
+     * @param keySize	The key size used for encrypting. Intended for cipher algorithms
+     * 					supporting multiple key sizes such as triple DES (DESede) or
+     * 					Blowfish.
+     * @param blockSize	The cipher block size. Intended to support cipher algorithms
+     * 					that support variable block sizes, such as Rijndael.
+     * @param ivLen		The length of the IV.
+     * @param iv		The actual IV (initialization vector) bytes.
+     * @param ciphertextLen	The length of the raw ciphertext.
+     * @param rawCiphertext	The actual raw ciphertext itself
+     * @param macLen	The length of the MAC (message authentication code).
+     * @param mac		The MAC itself.
+     * @return	A byte array representing the serialized ciphertext.
+     */
+    private byte[] computeSerialization(int kdfInfo, long timestamp,
+                                        String cipherXform, short keySize,
+                                        short blockSize,
+                                        short ivLen, byte[] iv,
+                                        int ciphertextLen, byte[] rawCiphertext,
+                                        short macLen, byte[] mac
+                                       )
+    {
+        debug("computeSerialization: kdfInfo = " + kdfInfo);
+        debug("computeSerialization: timestamp = " + new Date(timestamp));
+        debug("computeSerialization: cipherXform = " + cipherXform);
+        debug("computeSerialization: keySize = " + keySize);
+        debug("computeSerialization: blockSize = " + blockSize);
+        debug("computeSerialization: ivLen = " + ivLen);
+        debug("computeSerialization: ciphertextLen = " + ciphertextLen);
+        debug("computeSerialization: macLen = " + macLen);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeInt(baos, kdfInfo);
+        writeLong(baos, timestamp);
+        String[] parts = cipherXform.split("/");
+        assert parts.length == 3 : "Malformed cipher transformation";
+        writeString(baos, cipherXform); // Size of string is prepended to string
+        writeShort(baos, keySize);
+        writeShort(baos, blockSize);
+        writeShort(baos, ivLen);
+        if ( ivLen > 0 ) baos.write(iv, 0, iv.length);
+        writeInt(baos, ciphertextLen);
+        baos.write(rawCiphertext, 0, rawCiphertext.length);
+        writeShort(baos, macLen);
+        if ( macLen > 0 ) baos.write(mac, 0, mac.length);
+        return baos.toByteArray();
+    }
+    
+    // All strings are written as UTF-8 encoded byte streams with the
+    // length prepended before it as a short. The prepended length is
+    // more for the benefit of languages like C so they can pre-allocate
+    // char arrays without worrying about buffer overflows.
+    private void writeString(ByteArrayOutputStream baos, String str) {
+        byte[] bytes;
+        try {
+            assert str != null && str.length() > 0;
+            bytes = str.getBytes("UTF8");
+            assert bytes.length < Short.MAX_VALUE : "writeString: String exceeds max length";
+            writeShort(baos, (short)bytes.length);
+            baos.write(bytes, 0, bytes.length);
+        } catch (UnsupportedEncodingException e) {
+            // Should never happen. UTF8 is built into the rt.jar. We don't use native encoding as
+            // a fall-back because that simply is not guaranteed to be portable across Java
+            // platforms and could cause really bizarre errors way downstream.
+            logger.error(Logger.EVENT_FAILURE, "Ignoring caught UnsupportedEncodingException " +
+                           "converting string to UTF8 encoding. Results suspect. Corrupt rt.jar????");
+        }
+    }
+    
+    private String readString(ByteArrayInputStream bais, short sz)
+        throws NullPointerException, IOException
+    {
+        byte[] bytes = new byte[sz];
+        int ret = bais.read(bytes, 0, sz);
+        assert ret == sz : "readString: Failed to read " + sz + " bytes.";
+        return new String(bytes, "UTF8");
+    }
+    
+    private void writeShort(ByteArrayOutputStream baos, short s) {
+        byte[] shortAsByteArray = ByteConversionUtil.fromShort(s);
+        assert shortAsByteArray.length == 2;
+        baos.write(shortAsByteArray, 0, 2);
+    }
+    
+    private short readShort(ByteArrayInputStream bais)
+        throws NullPointerException, IndexOutOfBoundsException
+    {
+        byte[] shortAsByteArray = new byte[2];
+        int ret = bais.read(shortAsByteArray, 0, 2);
+        assert ret == 2 : "readShort: Failed to read 2 bytes.";
+        return ByteConversionUtil.toShort(shortAsByteArray);
+    }
+    
+    private void writeInt(ByteArrayOutputStream baos, int i) {
+        byte[] intAsByteArray = ByteConversionUtil.fromInt(i);
+        baos.write(intAsByteArray, 0, 4);
+    }
+    
+    private int readInt(ByteArrayInputStream bais)
+        throws NullPointerException, IndexOutOfBoundsException
+    {
+        byte[] intAsByteArray = new byte[4];
+        int ret = bais.read(intAsByteArray, 0, 4);
+        assert ret == 4 : "readInt: Failed to read 4 bytes.";
+        return ByteConversionUtil.toInt(intAsByteArray);
+    }
+    
+    private void writeLong(ByteArrayOutputStream baos, long l) {
+        byte[] longAsByteArray = ByteConversionUtil.fromLong(l);
+        assert longAsByteArray.length == 8;
+        baos.write(longAsByteArray, 0, 8);
+    }
+    
+    private long readLong(ByteArrayInputStream bais)
+        throws NullPointerException, IndexOutOfBoundsException
+    {
+        byte[] longAsByteArray = new byte[8];
+        int ret = bais.read(longAsByteArray, 0, 8);
+        assert ret == 8 : "readLong: Failed to read 8 bytes.";
+        return ByteConversionUtil.toLong(longAsByteArray);
+    }
+    
+    /** Convert the serialized ciphertext byte array to a {@code CipherText}
+     * object.
+     * @param cipherTextSerializedBytes	The serialized ciphertext as a byte array.
+     * @return The corresponding {@code CipherText} object.
+     * @throws EncryptionException	Thrown if the byte array data is corrupt or
+     * 				there are version mismatches, etc.
+     */
+    private CipherText convertToCipherText(byte[] cipherTextSerializedBytes)
+        throws EncryptionException
+    {
+        try {
+        	assert cipherTextSerializedBytes != null : "cipherTextSerializedBytes cannot be null.";
+        	assert cipherTextSerializedBytes.length > 0 : "cipherTextSerializedBytes must be > 0 in length.";
+            ByteArrayInputStream bais = new ByteArrayInputStream(cipherTextSerializedBytes);
+            int kdfInfo = readInt(bais);
+            debug("kdfInfo: " + kdfInfo);
+            int kdfPrf = (kdfInfo >>> 28);
+            debug("kdfPrf: " + kdfPrf);
+            assert kdfPrf >= 0 && kdfPrf <= 15 : "kdfPrf == " + kdfPrf + " must be between 0 and 15.";
+            int kdfVers = ( kdfInfo & 0x07ffffff);
+
+            // First do a quick sanity check on the argument. Previously this was an assertion.
+            if ( ! CryptoHelper.isValidKDFVersion(kdfVers, false, false) ) {
+            	// TODO: Clean up. Use StringBuilder. Good enough for now.
+            	String logMsg = "KDF version read from serialized ciphertext (" + kdfVers + ") is out of range. " +
+            				    "Valid range for KDF version is [" + KeyDerivationFunction.originalVersion + ", " +
+            				    "99991231].";
+            	// This should never happen under actual circumstances (barring programming errors; but we've
+            	// tested the code, right?), so it is likely an attempted attack. Thus don't get the originator
+            	// of the suspect ciphertext too much info. They ought to know what they sent anyhow.
+            	throw new EncryptionException("Version info from serialized ciphertext not in valid range.",
+            				 "Likely tampering with KDF version on serialized ciphertext." + logMsg);
+            }
+            
+            debug("convertToCipherText: kdfPrf = " + kdfPrf + ", kdfVers = " + kdfVers);
+            if ( ! versionIsCompatible( kdfVers) ) {
+            	throw new EncryptionException("This version of ESAPI does is not compatible with the version of ESAPI that encrypted your data.",
+            			"KDF version " + kdfVers + " from serialized ciphertext not compatibile with current KDF version of " + 
+            			KeyDerivationFunction.kdfVersion);
+            }
+            long timestamp = readLong(bais);
+            debug("convertToCipherText: timestamp = " + new Date(timestamp));
+            short strSize = readShort(bais);
+            debug("convertToCipherText: length of cipherXform = " + strSize);
+            String cipherXform = readString(bais, strSize);
+            debug("convertToCipherText: cipherXform = " + cipherXform);
+            String[] parts = cipherXform.split("/");
+            assert parts.length == 3 : "Malformed cipher transformation";
+            String cipherMode = parts[1];
+            if ( ! CryptoHelper.isAllowedCipherMode(cipherMode) ) {
+                String msg = "Cipher mode " + cipherMode + " is not an allowed cipher mode";
+                throw new EncryptionException(msg, msg);
+            }
+            short keySize = readShort(bais);
+            debug("convertToCipherText: keySize = " + keySize);
+            short blockSize = readShort(bais);
+            debug("convertToCipherText: blockSize = " + blockSize);
+            short ivLen = readShort(bais);
+            debug("convertToCipherText: ivLen = " + ivLen);
+            byte[] iv = null;
+            if ( ivLen > 0 ) {
+                iv = new byte[ivLen];
+                bais.read(iv, 0, iv.length);
+            }
+            int ciphertextLen = readInt(bais);
+            debug("convertToCipherText: ciphertextLen = " + ciphertextLen);
+            assert ciphertextLen > 0 : "convertToCipherText: Invalid cipher text length";
+            byte[] rawCiphertext = new byte[ciphertextLen];
+            bais.read(rawCiphertext, 0, rawCiphertext.length);
+            short macLen = readShort(bais);
+            debug("convertToCipherText: macLen = " + macLen);
+            byte[] mac = null;
+            if ( macLen > 0 ) {
+                mac = new byte[macLen];
+                bais.read(mac, 0, mac.length);
+            }
+
+            CipherSpec cipherSpec = new CipherSpec(cipherXform, keySize);
+            cipherSpec.setBlockSize(blockSize);
+            cipherSpec.setIV(iv);
+            debug("convertToCipherText: CipherSpec: " + cipherSpec);
+            CipherText ct = new CipherText(cipherSpec);
+            if ( ! (ivLen > 0 && ct.requiresIV()) ) {
+                    throw new EncryptionException("convertToCipherText: Mismatch between IV length and cipher mode.",
+                    						      "Possible tampering of serialized ciphertext?");
+            }
+            ct.setCiphertext(rawCiphertext);
+              // Set this *AFTER* setting raw ciphertext because setCiphertext()
+              // method also sets encryption time.
+            ct.setEncryptionTimestamp(timestamp);
+            if ( macLen > 0 ) {
+                ct.storeSeparateMAC(mac);
+            }
+            	// Fixed in ESAPI crypto version 20130839. Previously is didn't really matter
+            	// because there was only one version (20110203) and it defaulted to that
+            	// version, which was the current version. But we don't want that as now there
+            	// are two versions and we could be decrypting data encrypted using the previous
+            	// version.
+            ct.setKDF_PRF(kdfPrf);
+            ct.setKDFVersion(kdfVers);
+            return ct;
+        } catch(EncryptionException ex) {
+            throw new EncryptionException("Cannot deserialize byte array into CipherText object",
+                                          "Cannot deserialize byte array into CipherText object",
+                                          ex);
+        } catch (IOException e) {
+            throw new EncryptionException("Cannot deserialize byte array into CipherText object",
+                    "Cannot deserialize byte array into CipherText object", e);
+        }
+    }
+
+    /** Check to see if we can support the KSF version that was extracted from
+     *  the serialized ciphertext. In particular, we assume that if we have a
+     *  newer version of KDF than we can support it as we assume that we have
+     *  built in backward compatibility.
+     *  
+     *  At this point (ESAPI 2.1.0, KDF version 20130830), all we need to check
+     *  if the version is either the current version or the previous version as
+     *  both versions work the same. This checking may get more complicated in
+     *  the future.
+     *  
+     *  @param readKdfVers	The version information extracted from the serialized
+     *  					ciphertext.
+     */
+    private static boolean versionIsCompatible(int readKdfVers) {
+    	// We've checked elsewhere for this, so assertion is OK here.
+    	assert readKdfVers > 0 : "Extracted KDF version is negative!";
+    	
+		switch ( readKdfVers ) {
+		case KeyDerivationFunction.originalVersion:		// First version
+			return true;
+		// Add new versions here; hard coding is OK...
+		// case YYYYMMDD:
+		//	return true;
+		case KeyDerivationFunction.kdfVersion:			// Current version
+			return true;
+		default:
+			return false;
+		}
+	}
+
+	private void debug(String msg) {
+        if ( logger.isDebugEnabled() ) {
+            logger.debug(Logger.EVENT_SUCCESS, msg);
+        }
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java b/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java
new file mode 100644
index 0000000..e42c8cf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java
@@ -0,0 +1,75 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Chris Schmidt (chris.schmidt at owasp.org)
+ * @created 2010
+ */
+package org.owasp.esapi.crypto;
+
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class CryptoDiscoverer {
+	private static String EOL = System.getProperty("line.separator", "\n");
+	
+    public static void main(String... args) {
+        String provider = ".*";
+        String algorithm = ".*";
+
+        if ( args.length > 0 ) {
+            if ( args[0].equals( "--help" ) ) {
+                usage();
+                System.exit(0);
+            }
+
+            List<String> argList = Arrays.asList( args );
+
+            int argIdx = argList.indexOf("--provider");
+            if ( argIdx > -1 && argList.size() > (argIdx + 1) ) {
+                provider = argList.get(argIdx+1);
+            }
+
+            argIdx = argList.indexOf("--algorithm");
+            if ( argIdx > -1 && argList.size() > (argIdx + 1) ) {
+                algorithm = argList.get(argIdx+1);
+            }
+        }
+
+        Pattern providerPattern = Pattern.compile(provider);
+        Pattern algorithmPattern = Pattern.compile(algorithm);
+
+        System.out.println("Searching for Providers Matching: " + provider );
+        System.out.println("Searching for Algorithms Matching: " + algorithm );
+        System.out.println();
+
+        for (Provider p : Security.getProviders()) {
+            if ( providerPattern.matcher(p.getName()).matches()) {
+                System.out.println("Provider: " + p.getName());
+                for (Provider.Service service : p.getServices()) {
+                    if ( algorithmPattern.matcher(service.getAlgorithm()).matches()) {
+                        System.out.println("\tAlgorithm: " + service.getAlgorithm());
+                    }
+                }
+            }
+        }
+    }
+
+    private static void usage() {
+        System.out.println("CryptoDiscoverer - Discover or Query for available Crypto Providers and Algorithms");
+        System.out.println(EOL + "\t--help\t\t\t\t\tShows this message" + EOL +
+        		"\t--provider <regex>\t\tSearch for particular Provider" + EOL +
+                "\t--algorithm <regex>\t\tSearch for a particular Algorithm" + EOL + EOL);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java
new file mode 100644
index 0000000..024150b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java
@@ -0,0 +1,395 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.crypto;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Class to provide some convenience methods for encryption, decryption, etc.
+ * </p><p>
+ * All the cryptographic operations use the default cryptographic properties;
+ * e.g., default cipher transformation, default key size, default IV type (where
+ * applicable), etc.
+ * 
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class CryptoHelper {
+	
+	private static final Logger logger = ESAPI.getLogger("CryptoHelper");
+
+	// TODO: Also consider supplying implementation of RFC 2898 / PKCS#5 PBKDF2
+	//		 in this file as well??? Maybe save for ESAPI 2.1 or 3.0.
+	/**
+	 * Generate a random secret key appropriate to the specified cipher algorithm
+	 * and key size.
+	 * @param alg	The cipher algorithm or cipher transformation. (If the latter is
+	 * 				passed, the cipher algorithm is determined from it.) Cannot be
+	 * 				null or empty.
+	 * @param keySize	The key size, in bits.
+	 * @return	A random {@code SecretKey} is returned.
+	 * @throws EncryptionException Thrown if cannot create secret key conforming to
+	 * 				requested algorithm with requested size. Typically this is caused by
+	 * 				specifying an unavailable algorithm or invalid key size.
+	 */
+	public static SecretKey generateSecretKey(String alg, int keySize)
+		throws EncryptionException
+	{
+		assert alg != null : "Algorithm must not be null.";			// NPE if null and assertions disabled.
+		assert !alg.equals("") : "Algorithm must not be empty";	// NoSuchAlgorithmExeption if empty & assertions disabled.
+		assert keySize > 0 : "Key size must be positive.";	// Usually should be even multiple of 8, but not strictly required by alg.
+		// Don't use CipherSpec here to get algorithm as this may cause assertion
+		// to fail (when enabled) if only algorithm name is passed to us.
+		String[] cipherSpec = alg.split("/");
+		String cipherAlg = cipherSpec[0];
+		try {
+		    // Special case for things like PBEWithMD5AndDES or PBEWithSHA1AndDESede.
+		    // In such cases, the key generator should only request an instance of "PBE".
+		    if ( cipherAlg.toUpperCase().startsWith("PBEWITH") ) {
+		        cipherAlg = "PBE";
+		    }
+			KeyGenerator kgen =
+				KeyGenerator.getInstance( cipherAlg );
+			kgen.init(keySize);
+			return kgen.generateKey();
+		} catch (NoSuchAlgorithmException e) {
+			throw new EncryptionException("Failed to generate random secret key",
+					"Invalid algorithm. Failed to generate secret key for " + alg + " with size of " + keySize + " bits.", e);
+		} catch (InvalidParameterException e) {
+			throw new EncryptionException("Failed to generate random secret key - invalid key size specified.",
+					"Invalid key size. Failed to generate secret key for " + alg + " with size of " + keySize + " bits.", e);
+		}
+	}
+
+	/**
+	 * The method is ESAPI's Key Derivation Function (KDF) that computes a
+	 * derived key from the {@code keyDerivationKey} for either
+	 * encryption / decryption or for authentication.
+	 * <p>
+	 * <b>CAUTION:</b> If this algorithm for computing derived keys from the
+	 * key derivation key is <i>ever</i> changed, we risk breaking backward compatibility of being
+	 * able to decrypt data previously encrypted with earlier / different versions
+	 * of this method. Therefore, do not change this unless you are 100% certain that
+	 * what you are doing will NOT change either of the derived keys for
+	 * ANY "key derivation key" AT ALL!!!
+	 * <p>
+	 * <b>NOTE:</b> This method is generally not intended to be called separately.
+	 * It is used by ESAPI's reference crypto implementation class {@code JavaEncryptor}
+	 * and might be useful for someone implementing their own replacement class, but
+	 * generally it is not something that is useful to application client code.
+	 * 
+	 * @param keyDerivationKey  A key used as an input to a key derivation function
+	 *                          to derive other keys. This is the key that generally
+	 *                          is created using some key generation mechanism such as
+	 *                          {@link #generateSecretKey(String, int)}. The
+	 *                          "input" key from which the other keys are derived.
+	 * 							The derived key will have the same algorithm type
+	 * 							as this key.
+	 * @param keySize		The cipher's key size (in bits) for the {@code keyDerivationKey}.
+	 * 						Must have a minimum size of 56 bits and be an integral multiple of 8-bits.
+	 * 						<b>Note:</b> The derived key will have the same size as this.
+	 * @param purpose		The purpose for the derived key. Must be either the
+	 * 						string "encryption" or "authenticity". Use "encryption" for
+     *                      creating a derived key to use for confidentiality, and "authenticity"
+     *                      for a derived key to use with a MAC to ensure message authenticity.
+	 * @return				The derived {@code SecretKey} to be used according
+	 * 						to the specified purpose. Note that this serves the same purpose
+	 * 						as "label" in section 5.1 of NIST SP 800-108.
+	 * @throws NoSuchAlgorithmException		The {@code keyDerivationKey} has an unsupported
+	 * 						encryption algorithm or no current JCE provider supports
+	 * 						"HmacSHA1".
+	 * @throws EncryptionException		If "UTF-8" is not supported as an encoding, then
+	 * 						this is thrown with the original {@code UnsupportedEncodingException}
+	 * 						as the cause. (NOTE: This should never happen as "UTF-8" is supposed to
+	 * 						be a common encoding supported by all Java implementations. Support
+	 * 					    for it is usually in rt.jar.)
+	 * @throws InvalidKeyException 	Likely indicates a coding error. Should not happen.
+	 * @throws EncryptionException  Throw for some precondition violations.
+	 * @deprecated Use{@code KeyDerivationFunction} instead. This method will be removed as of
+	 * 			   ESAPI release 2.1 so if you are using this, please change your code.
+	 */
+	public static SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, String purpose)
+			throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException
+	{
+        // These really should be turned into actual runtime checks and an
+        // IllegalArgumentException should be thrown if they are violated.
+		assert keyDerivationKey != null : "Key derivation key cannot be null.";
+			// We would choose a larger minimum key size, but we want to be
+			// able to accept DES for legacy encryption needs.
+		assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
+		assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
+		assert purpose != null;
+		assert purpose.equals("encryption") || purpose.equals("authenticity") :
+			"Purpose must be \"encryption\" or \"authenticity\".";
+
+		// DISCUSS: Should we use HmacSHA1 (what we were using) or the HMAC defined by
+		//			Encryptor.KDF.PRF instead? Either way, this is not compatible with
+		//			previous ESAPI versions. JavaEncryptor doesn't use this any longer.
+		KeyDerivationFunction kdf = new KeyDerivationFunction(
+											KeyDerivationFunction.PRF_ALGORITHMS.HmacSHA1);
+		return kdf.computeDerivedKey(keyDerivationKey, keySize, purpose);
+	}
+
+	/**
+	 * Return true if specified cipher mode is one of those specified in the
+	 * {@code ESAPI.properties} file that supports both confidentiality
+	 * <b>and</b> authenticity (i.e., a "combined cipher mode" as NIST refers
+	 * to it).
+	 * @param cipherMode The specified cipher mode to be used for the encryption
+	 *                   or decryption operation.
+	 * @return true if the specified cipher mode is in the comma-separated list
+	 *         of cipher modes supporting both confidentiality and authenticity;
+	 *         otherwise false.
+	 * @see org.owasp.esapi.SecurityConfiguration#getCombinedCipherModes()
+	 */
+	public static boolean isCombinedCipherMode(String cipherMode)
+	{
+	    assert cipherMode != null : "Cipher mode may not be null";
+	    assert ! cipherMode.equals("") : "Cipher mode may not be empty string";
+	    List<String> combinedCipherModes =
+	        ESAPI.securityConfiguration().getCombinedCipherModes();
+	    return combinedCipherModes.contains( cipherMode );
+	}
+
+	/**
+     * Return true if specified cipher mode is one that may be used for
+     * encryption / decryption operations via {@link org.owasp.esapi.Encryptor}.
+     * @param cipherMode The specified cipher mode to be used for the encryption
+     *                   or decryption operation.
+     * @return true if the specified cipher mode is in the comma-separated list
+     *         of cipher modes supporting both confidentiality and authenticity;
+     *         otherwise false.
+     * @see #isCombinedCipherMode(String)
+     * @see org.owasp.esapi.SecurityConfiguration#getCombinedCipherModes()
+     * @see org.owasp.esapi.SecurityConfiguration#getAdditionalAllowedCipherModes()
+     */
+	public static boolean isAllowedCipherMode(String cipherMode)
+	{
+	    if ( isCombinedCipherMode(cipherMode) ) {
+	        return true;
+	    }
+	    List<String> extraCipherModes =
+	        ESAPI.securityConfiguration().getAdditionalAllowedCipherModes();
+	    return extraCipherModes.contains( cipherMode );
+	}
+	
+    /**
+     * Check to see if a Message Authentication Code (MAC) is required
+     * for a given {@code CipherText} object and the current ESAPI.property
+     * settings. A MAC is considered "required" if the specified
+     * {@code CipherText} was not encrypted by one of the preferred
+     * "combined" cipher modes (e.g., CCM or GCM) and the setting of the
+     * current ESAPI properties for the property
+     * {@code Encryptor.CipherText.useMAC} is set to {@code true}. (Normally,
+     * the setting for {@code Encryptor.CipherText.useMAC} should be set to
+     * {@code true} unless FIPS 140-2 compliance is required. See
+     * <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html">
+     * User Guide for Symmetric Encryption in ESAPI 2.0</a> and the section
+     * on using ESAPI with FIPS for further details.
+     *
+     * @param ct    The specified {@code CipherText} object to check to see if
+     *              it requires a MAC.
+     * @return      True if a MAC is required, false if it is not required.
+     */
+    public static boolean isMACRequired(CipherText ct) {
+        boolean preferredCipherMode =
+            CryptoHelper.isCombinedCipherMode( ct.getCipherMode() );
+        boolean wantsMAC = ESAPI.securityConfiguration().useMACforCipherText();
+
+        // The preferred "combined" cipher modes such as CCM, GCM, etc. do
+        // not require a MAC as a MAC would be superfluous and just require
+        // additional computing time.
+        return ( !preferredCipherMode && wantsMAC );
+    }
+	
+    /**
+     * If a Message Authentication Code (MAC) is required for the specified
+     * {@code CipherText} object, then attempt to validate the MAC that
+     * should be embedded within the {@code CipherText} object by using a
+     * derived key based on the specified {@code SecretKey}.
+     *
+     * @param sk    The {@code SecretKey} used to derived a key to check
+     *              the authenticity via the MAC.
+     * @param ct    The {@code CipherText} that we are checking for a
+     *              valid MAC. 
+     *
+     * @return  True is returned if a MAC is required and it is valid as
+     *          verified using a key derived from the specified
+     *          {@code SecretKey} or a MAC is not required. False is returned
+     *          otherwise.
+     */
+    public static boolean isCipherTextMACvalid(SecretKey sk, CipherText ct)
+    {
+        if ( CryptoHelper.isMACRequired( ct ) ) {
+            try {
+                SecretKey authKey = CryptoHelper.computeDerivedKey( sk, ct.getKeySize(), "authenticity");
+                boolean validMAC = ct.validateMAC( authKey );
+                return validMAC;
+            } catch (Exception ex) {
+                // Error on side of security. If this fails and can't verify MAC
+                // assume it is invalid. Note that CipherText.toString() does not
+                // print the actual ciphertext.
+                logger.warning(Logger.SECURITY_FAILURE, "Unable to validate MAC for ciphertext " + ct, ex);
+                return false;
+            }
+        }
+        return true;
+    }
+    
+	/**
+	 * Overwrite a byte array with a specified byte. This is frequently done
+	 * to a plaintext byte array so the sensitive data is not lying around
+	 * exposed in memory.
+	 * @param bytes	The byte array to be overwritten.
+	 * @param x The byte array {@code bytes} is overwritten with this byte.
+	 */
+	public static void overwrite(byte[] bytes, byte x)
+	{
+		Arrays.fill(bytes, x);
+	}
+	
+	/**
+	 * Overwrite a byte array with the byte containing '*'. That is, call
+	 * <pre>
+	 * 		overwrite(bytes, (byte)'*');
+	 * </pre>
+	 * @param bytes The byte array to be overwritten.
+	 */
+	public static void overwrite(byte[] bytes)
+	{
+		overwrite(bytes, (byte)'*');
+	}
+	
+	// These provide for a bit more type safety when copying bytes around.
+	/**
+	 * Same as {@code System.arraycopy(src, 0, dest, 0, length)}.
+	 * 
+     * @param      src      the source array.
+     * @param      dest     the destination array.
+     * @param      length   the number of array elements to be copied.
+     * @exception  IndexOutOfBoundsException  if copying would cause
+     *               access of data outside array bounds.
+     * @exception  NullPointerException if either <code>src</code> or
+     *               <code>dest</code> is <code>null</code>.
+	 */
+	public static void copyByteArray(final byte[] src, byte[] dest, int length)
+	{
+		System.arraycopy(src, 0, dest, 0, length);
+	}
+	
+	/**
+	 * Same as {@code copyByteArray(src, dest, src.length)}.
+     * @param      src      the source array.
+     * @param      dest     the destination array.
+     * @exception  IndexOutOfBoundsException  if copying would cause
+     *               access of data outside array bounds.
+     * @exception  NullPointerException if either <code>src</code> or
+     *               <code>dest</code> is <code>null</code>.
+	 */
+	public static void copyByteArray(final byte[] src, byte[] dest)
+	{
+		copyByteArray(src, dest, src.length);
+	}
+	
+	/**
+	 * A "safe" array comparison that is not vulnerable to side-channel
+	 * "timing attacks". All comparisons of non-null, equal length bytes should
+	 * take same amount of time. We use this for cryptographic comparisons.
+	 * 
+	 * @param b1   A byte array to compare.
+	 * @param b2   A second byte array to compare.
+	 * @return     {@code true} if both byte arrays are null or if both byte
+	 *             arrays are identical or have the same value; otherwise
+	 *             {@code false} is returned.
+	 */
+	public static boolean arrayCompare(byte[] b1, byte[] b2) {
+	    if ( b1 == b2 ) {
+	        return true;
+	    }
+	    if ( b1 == null || b2 == null ) {
+	        return (b1 == b2);
+	    }
+	    if ( b1.length != b2.length ) {
+	        return false;
+	    }
+	    
+	    int result = 0;
+	    // Make sure to go through ALL the bytes. We use the fact that if
+	    // you XOR any bit stream with itself the result will be all 0 bits,
+	    // which in turn yields 0 for the result.
+	    for(int i = 0; i < b1.length; i++) {
+	        // XOR the 2 current bytes and then OR with the outstanding result.
+	        result |= (b1[i] ^ b2[i]);
+	    }
+	    return (result == 0) ? true : false;
+	}
+  
+	/**
+	 * Is this particular KDF version number one that is sane? For that, we
+	 * just make sure it is inbounds of the valid range which is:
+	 * <pre>
+	 *     [20110203, 99991231]
+	 * </pre>
+	 * @param kdfVers	KDF version # that we are checking. Generally this is
+	 * 				extracted from the serialized {@code CipherText}.
+	 * @param restrictToCurrent	If this is set, we do an additional check
+	 *				to see if the KDF version is a later version than the
+	 *				one that this current ESAPI version supports.
+	 * @param throwIfError	Instead of returning {@code false} in the case of
+	 * 				an error, throw an {@code IllegalArgumentException}
+	 * @return	True if in range, false otherwise (except if {@code throwIfError}
+	 * 			is true.}
+	 */
+	public static boolean isValidKDFVersion(int kdfVers, boolean restrictToCurrent,
+											boolean throwIfError)
+				throws IllegalArgumentException
+	{
+		boolean ret = true;
+		
+		if ( kdfVers < KeyDerivationFunction.originalVersion || kdfVers > 99991231 ) {
+			ret = false;
+		} else if ( restrictToCurrent ) {
+			ret = ( kdfVers <= KeyDerivationFunction.kdfVersion );
+		}
+		if ( ret ) {
+			return ret;				// True
+		} else {					// False, so throw or not.
+			logger.warning(Logger.SECURITY_FAILURE, "Possible data tampering. Encountered invalid KDF version #. " +
+						   ( throwIfError ? "Throwing IllegalArgumentException" : "" ));
+			if ( throwIfError ) {	
+				throw new IllegalArgumentException("Version (" + kdfVers + ") invalid. " +
+					"Must be date in format of YYYYMMDD between " + KeyDerivationFunction.originalVersion + "and 99991231.");
+			}
+		}
+		return false;
+	}
+	
+    /**
+     * Prevent public, no-argument CTOR from being auto-generated. Public CTOR
+     * is not needed as all methods are static.
+     */
+    private CryptoHelper() {
+    	;	// Prevent default CTOR from being auto-created.
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/CryptoToken.java b/src/main/java/org/owasp/esapi/crypto/CryptoToken.java
new file mode 100644
index 0000000..4276079
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/CryptoToken.java
@@ -0,0 +1,722 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2010 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and
+ * accept the LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @created 2010
+ */
+package org.owasp.esapi.crypto;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.ValidationException;
+
+///// IMPORTANT NOTE: Never print / log attribute *values* as they
+/////                 may be sensitive. Also, do not log the CryptoToken
+/////				  itself as it generally is used as an authentication token.
+
+// OPEN ISSUE: Assertions vs. IllegalArgumentException must be resolved. I prefer
+//             assertions for preconditions, which is more in line with Design-by-Contract
+//             and Eiffel. There are a few places where assertions are not appropriate
+//             because if they are not disabled (they are not by default), they could cause
+//             incorrect behavior (e.g., setting the expiration time to something in the
+//             past, etc.)
+
+/**
+ * Compute a cryptographically secure, encrypted token containing
+ * optional name/value pairs. The cryptographic token is computed
+ * like this:
+ * <pre>
+ *     username;expiration_time;[<attr1>;<attr2>;...;<attrN>;]
+ * </pre>
+ * where
+ * <i>username</i> is a user account name. Defaults to <anonymous> if
+ * not set and it is always converted to lower case as per the rules of the
+ * default locale. (Note this lower case conversion is consistent with the
+ * default reference implementation of ESAPI's {@code User} interface.)
+ * <br>
+ * <i>expiration_time</i> is time (in milliseconds) after which the encrypted
+ * token is considered invalid (i.e., expired). The time is stored as
+ * milliseconds since midnight, January 1, 1970 UTC, and optional attributes
+ * <br>
+ *   <i><attr1></i>;<i><attr2></i>;...<i><attrN></i>;
+ * <br>
+ * are optional semicolon (';') separated name/value pairs, where each
+ * name/value pair has the form:
+ * <pre>
+ *         name=[value]        (value may be empty, but not null)
+ * </pre>
+ * The attribute value may contain any value. However, values containing
+ * either '=' or ';' will be quoted using '\'. Likewise, values containing '\'
+ * will also be quoted using '\'. Hence if original name/value pair were
+ *             name=ab=xy\;
+ *         this would be represented as
+ *             name=ab\=xy\\\;
+ * To ensure things are "safe" (from a security perspective), attribute
+ * <i>names</i> must conform the the Java regular expression
+ * <pre>
+ *          [A-Za-z0-9_\.-]+
+ * </pre>
+ * The attribute <i>value</i> on the other hand, may be any valid string. (That
+ * is, the value is not checked, so beware!)
+ * <p>
+ * This entire semicolon-separated string is then encrypted via one of the 
+ * {@code Encryptor.encrypt()} methods and then base64-encoded, serialized
+ * IV + ciphertext + MAC representation as determined by
+ * {@code CipherTextasPortableSerializedByteArray()} is used as the
+ * resulting cryptographic token.
+ * <p>
+ * The attributes are sorted by attribute name and the attribute names
+ * must be unique. There are some restrictions on the attribute names.
+ * (See the {@link #setAttribute(String, String)} method for details.)
+ *
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class CryptoToken {
+    /** Represents an anonymous user. */
+    public static final String ANONYMOUS_USER = "<anonymous>";
+    
+    // Default expiration time
+    private static final long DEFAULT_EXP_TIME = 5 * 60 * 1000;  // 5 min == 300 milliseconds
+    private static final String DELIM = ";";                     // field delimiter
+    private static final char DELIM_CHAR = ';';                  // field delim as a char
+    private static final char QUOTE_CHAR = '\\';                 // char used to quote delimiters, '=' and itself.
+    
+        // OPEN ISSUE: Should we make these 2 regex's properties in ESAPI.properties???
+    private static final String ATTR_NAME_REGEX = "[A-Za-z0-9_.-]+"; // One or more alphanumeric, underscore, periods, or hyphens.
+    private static final String USERNAME_REGEX = "[a-z][a-z0-9_. at -]*";
+    
+    private static Logger logger = ESAPI.getLogger("CryptoToken");
+
+    private String username = ANONYMOUS_USER;        // Default user name if not set. Always lower case.
+    private long expirationTime = 0L;
+        // This probably needed be sorted. A HashMap would do as well.
+        // But this might make debugging a bit easier, so why not?
+    private TreeMap<String, String> attributes = new TreeMap<String,String>();
+    private transient SecretKey secretKey = null;
+    private Pattern attrNameRegex = Pattern.compile(ATTR_NAME_REGEX);
+    private Pattern userNameRegex = Pattern.compile(USERNAME_REGEX);
+    
+    /**
+     * Create a cryptographic token using default secret key from the
+     * <b>ESAPI.properties</b> property <b>Encryptor.MasterKey</b>. 
+     */
+    public CryptoToken() {
+        secretKey = getDefaultSecretKey(
+                            ESAPI.securityConfiguration().getEncryptionAlgorithm()
+                        );
+        long now = System.currentTimeMillis();
+        expirationTime = now + DEFAULT_EXP_TIME;
+    }
+
+    // Create using specified SecretKey
+    /**
+     * Create a cryptographic token using specified {@code SecretKey}.
+     * 
+     * @param skey  The specified {@code SecretKey} to use to encrypt the token.
+     */
+    public CryptoToken(SecretKey skey) {
+        assert skey != null : "SecretKey may not be null.";
+        secretKey = skey;
+        long now = System.currentTimeMillis();
+        expirationTime = now + DEFAULT_EXP_TIME;
+    }
+
+    /** 
+     * Create using previously encrypted token encrypted with default secret
+     * key from <b>ESAPI.properties</b>.
+     * @param token A previously encrypted token returned by one of the
+     *              {@code getToken()} or {@code updateToken()} methods. The
+     *              token <i>must</i> have been previously encrypted using the
+     *              using default secret key from the <b>ESAPI.properties</b>
+     *              property <b>Encryptor.MasterKey</b>.
+     * @throws EncryptionException  Thrown if they are any problems while decrypting
+     *                              the token using the default secret key from
+     *                              <b>ESAPI.properties</b> or if the decrypted
+     *                              token is not properly formatted.
+     */
+    public CryptoToken(String token) throws EncryptionException {
+        secretKey = getDefaultSecretKey(
+                ESAPI.securityConfiguration().getEncryptionAlgorithm()
+            );
+        try {
+            decryptToken(secretKey, token);
+        } catch (EncodingException e) {
+            throw new EncryptionException("Decryption of token failed. Token improperly encoded or encrypted with different key.",
+                                          "Can't decrypt token because not correctly encoded or encrypted with different key.", e);
+        }
+        assert username != null : "Programming error: Decrypted token found username null.";
+        assert expirationTime > 0 : "Programming error: Decrypted token found expirationTime <= 0.";
+    }
+
+    /** 
+     * Create cryptographic token using previously encrypted token that was
+     * encrypted with specified secret key.
+     * 
+     * @param token A previously encrypted token returned by one of the
+     *              {@code getToken()} or {@code updateToken()} methods.
+     * @throws EncryptionException  Thrown if they are any problems while decrypting
+     *                              the token using the default secret key from
+     *                              <b>ESAPI.properties</b> or if the decrypted
+     *                              token is not properly formatted.
+     */
+    // token is a previously encrypted token (i.e., CryptoToken.getToken())
+    // with different SecretKey other than the one in ESAPI.properties
+    public CryptoToken(SecretKey skey, String token) throws EncryptionException {
+        assert skey != null : "SecretKey may not be null.";
+        assert token != null : "Token may not be null";
+        secretKey = skey;
+        try {
+            decryptToken(secretKey, token);
+        } catch (EncodingException e) {
+            throw new EncryptionException("Decryption of token failed. Token improperly encoded.",
+                                          "Can't decrypt token because not correctly encoded.", e);
+        }
+        assert username != null : "Programming error: Decrypted token found username null.";
+        assert expirationTime > 0 : "Programming error: Decrypted token found expirationTime <= 0.";
+    }
+
+    /**
+     * Retrieve the user account name associated with this {@code CryptoToken}
+     * object.
+     * @return  The user account name. The string represented by
+     *          {@link #ANONYMOUS_USER} is returned if
+     *          {@link #setUserAccountName(String)} was never called.
+     */
+    public String getUserAccountName() {
+        return ( (username != null) ? username : ANONYMOUS_USER );
+    }
+    
+    /**
+     * Set the user account name associated with this cryptographic token
+     * object. The user account name is converted to lower case.
+     * @param userAccountName   The user account name.
+     * @throws ValidationException  Thrown if user account name is not valid, i.e.,
+     *                              if it doesn't conform to the regular expression
+     *                              given by "[a-z][a-z0-9_. at -]*". (Note that the
+     *                              parameter {@code userAccountName} is first converted
+     *                              to lower case before checked against the regular
+     *                              expression.)
+     */
+    public void setUserAccountName(String userAccountName) throws ValidationException {
+        assert userAccountName != null : "User account name may not be null.";
+        
+        // Converting to lower case first allows a simpler regex.
+        String userAcct = userAccountName.toLowerCase();
+        
+        // Check to make sure that attribute name is valid as per our regex.
+        Matcher userNameChecker = userNameRegex.matcher(userAcct);
+        if ( userNameChecker.matches() ) {
+            username = userAcct;
+        } else {
+            throw new ValidationException("Invalid user account name encountered.",
+                                          "User account name " + userAccountName +
+                                              " does not match regex " +
+                                              USERNAME_REGEX + " after conversion to lowercase.");
+        }
+    }
+
+    /** Check if token has expired yet.
+     * @return  True if token has expired; false otherwise.
+     */
+    public boolean isExpired() {
+        return System.currentTimeMillis() > expirationTime;
+    }
+    
+    /**
+     * Set expiration time to expire in 'interval' seconds (NOT milliseconds).
+     * @param intervalSecs  Number of seconds in the future from current date/time
+     *                  	to set expiration. Must be positive.
+     */
+    public void setExpiration(int intervalSecs) throws IllegalArgumentException
+    {
+        int intervalMillis = intervalSecs * 1000;   // Need to convert secs to millisec.
+        
+        // Don't want to use assertion here, because if they are disabled,
+        // this would result in setting the expiration time prior to the
+        // current time, hence it would already be expired.
+        if ( intervalMillis <= 0) {
+            throw new IllegalArgumentException("intervalSecs argument, converted to millisecs, must be > 0.");
+        }
+        // Check for arithmetic overflow here. In reality, this condition
+        // should never happen, but we want to avoid it--even theoretically--
+        // since otherwise, it could have security implications.
+        long now = System.currentTimeMillis();
+        preAdd(now, intervalMillis);
+        expirationTime = now + intervalMillis;
+    }
+    
+    /**
+     * Set expiration time for a specific date/time.
+     * @param expirationDate    The date/time at which the token will fail. Must
+     *                          be after the current date/time.
+     * @throws IllegalArgumentException Thrown if the parameter is null.
+     */
+    public void setExpiration(Date expirationDate) throws IllegalArgumentException
+    {
+        if ( expirationDate == null ) {
+            throw new IllegalArgumentException("expirationDate may not be null.");
+        }
+        long curTime = System.currentTimeMillis();
+        long expTime = expirationDate.getTime();
+        if ( expTime <= curTime ) {
+            throw new IllegalArgumentException("Expiration date must be after current date/time.");
+        }        
+        expirationTime = expTime;
+    }
+    
+    /**
+     * Return the expiration time in milliseconds since epoch time (midnight,
+     * January 1, 1970 UTC).
+     * @return  The current expiration time.
+     */
+    public long getExpiration() {
+        assert expirationTime > 0L : "Programming error: Expiration time <= 0";
+        return expirationTime;
+    }
+    
+    /**
+     * Return the expiration time as a {@code Date}.
+     * @return The {@code Date} object representing the expiration time.
+     */
+    public Date getExpirationDate() {
+        return new Date( getExpiration() );
+    }
+
+    /**
+     * Set a name/value pair as an attribute.
+     * @param name  The attribute name
+     * @param value The attribute value
+     * @throws ValidationException  Thrown if the attribute name is not properly
+     *                              formed. That is, the attribute name does not
+     *                              match the regular expression "[A-Za-z0-9_.-]+".
+     */
+    public void setAttribute(String name, String value) throws ValidationException {
+        if ( name == null || name.length() == 0 ) {
+            // CHECKME: Should this be an IllegalArgumentException instead? I
+            // would prefer an assertion here and state this as a precondition
+            // in the Javadoc.
+            throw new ValidationException("Null or empty attribute NAME encountered",
+                                          "Attribute NAMES may not be null or empty string.");
+        }
+        if ( value == null ) {
+            // CHECKME: Should this be an IllegalArgumentException instead? I
+            // would prefer an assertion here and state this as a precondition
+            // in the Javadoc.
+            throw new ValidationException("Null attribute VALUE encountered for attr name " + name,
+                                          "Attribute VALUE may not be null; attr name: " + name);            
+        }
+        // NOTE: OTOH, it *is* VALID if the _value_ is empty! Null values cause too much trouble
+        // to make it worth the effort of getting it to work consistently.
+
+        // Check to make sure that attribute name is valid as per our regex.
+        Matcher attrNameChecker = attrNameRegex.matcher(name);
+        if ( attrNameChecker.matches() ) {
+            attributes.put(name, value);
+        } else {
+            throw new ValidationException("Invalid attribute name encountered.",
+                                          "Attribute name " + name + " does not match regex " +
+                                          ATTR_NAME_REGEX);
+        }
+    }
+    
+    /**
+     * Add the specified collection of attributes to the current attributes.
+     * If there are duplicate attributes specified, they will replace any
+     * existing ones.
+     * 
+     * @param attrs Name/value pairs of attributes to add or replace the existing
+     *              attributes. Map must be non-null, but may be empty.
+     * @throws ValidationException Thrown if one of the keys in the specified
+     *                             parameter {@code attrs} is not a valid name.
+     *                             That is, all attribute names must match the regular
+     *                             expression ""[A-Za-z0-9_.-]+".
+     * @see #setAttribute(String, String)
+     */
+    public void addAttributes(final Map<String, String> attrs) throws ValidationException {
+        // CHECKME: Assertion vs. IllegalArgumentException
+        assert attrs != null : "Attribute map may not be null.";
+        Set< Entry<String,String> > keyValueSet = attrs.entrySet();
+        Iterator<Entry<String, String>> it = keyValueSet.iterator();
+        while( it.hasNext() ) {
+            Map.Entry<String,String> entry = it.next();
+            String key = entry.getKey();
+            String value = entry.getValue();
+            setAttribute(key, value);
+        }
+        return;
+    }
+    
+    /**
+     * Retrieve the attribute with the specified name.
+     * @param name  The attribute name.
+     * @return  The value associated with the attribute name. If attribute is not
+     *          set, then {@code null} is returned.
+     */
+    public String getAttribute(String name) {
+        return attributes.get(name);
+    }
+    
+    /**
+     * Retrieve a {@code Map} that is a clone of all the attributes. A <i>copy</i>
+     * is returned so that the attributes in {@code CrytpToken} are unaffected
+     * by alterations made the returned {@code Map}. (Otherwise, multi-threaded code
+     * could get trick.
+     * 
+     * @return  A {@code Map} of all the attributes.
+     * @see #getAttribute(String)
+     */
+    @SuppressWarnings("unchecked")
+    public Map<String, String> getAttributes() {
+        // Unfortunately, this requires a cast, which requires us to supress warnings.
+        return (Map<String, String>) attributes.clone();
+    }
+    
+    /**
+     * Removes all the attributes (if any) associated with this token. Note
+     * that this does not clear / reset the user account name or expiration time.
+     */
+    public void clearAttributes() {
+        attributes.clear();
+    }
+
+    /**
+     * Return the new encrypted token as a base64-encoded string, encrypted with
+     * the specified {@code SecretKey} which may be a different key than what the
+     * token was originally encrypted with. E.g.,
+     * <pre>
+     *   Alice:
+     *      SecretKey aliceSecretKey = ...; // Shared with Bob
+     *      CryptoToken cryptoToken = new CryptoToken(skey1);
+     *      cryptoToken.setUserAccountName("kwwall");
+     *      cryptoToken.setAttribute("role", "admin");
+     *      cryptoToken.setAttribute("state", "Ohio");
+     *      String token = cryptoToken.getToken(); // Encrypted with skey1
+     *      // send token to Bob ...
+     *  --------------------------------------------------------------------
+     *  Bob:
+     *      ...
+     *      SecretKey aliceSecretKey = ...  // Shared with Alice
+     *      SecretKey bobSecretKey = ...;   // Shared with Carol
+     *      CryptoToken cryptoToken = new CryptoToken(aliceSecretKey, tokenFromAlice);
+     *      
+     *      // Re-encrypt for Carol using my (Bob's) key...
+     *      String tokenForCarol = cryptoToken.getToken(bobSecretKey);
+     *      // send tokenForCarol to Carol ...
+     *      // use token ourselves
+     *  --------------------------------------------------------------------
+     *  Carol:
+     *      ...
+     *      SecretKey bobSecretKey = ...;   // Shared with Bob.
+     *      CryptoToken cryptoToken = new CryptoToken(bobSecretKey, tokenFromBob);
+     *      if ( ! cryptoToken.isExpired() ) {
+     *          String userName = cryptoToken.getUserAccountName();
+     *          String roleName = cryptoToken.getAttribute("role");
+     *          if ( roleName != null && roleName.equalsIgnoreCase("admin") ) {
+     *              // grant admin access...
+     *              ...
+     *          }
+     *      }
+     *      ...
+     * </pre>
+     * @param skey  The specified key to (re)encrypt the token.
+     * @return The newly encrypted token.
+     */
+    public String getToken(SecretKey skey) throws EncryptionException {
+        return createEncryptedToken(skey);
+    }
+    
+    /**
+     * Update the (current) expiration time by adding the specified number of
+     * seconds to it and then re-encrypting with the current {@code SecretKey}
+     * that was used to construct this object.
+     * 
+     * @param additionalSecs    The additional number of seconds to add to the
+     *                          current expiration time. This number must be
+     *                          >= 0 or otherwise an {@code IllegalArgumentException}
+     *                          is thrown.
+     * @return  The re-encrypted token with the updated expiration time is returned.
+     * @throws  IllegalArgumentException    Thrown if parameter {@code additionalSecs}
+     *                                      is less than 0.
+     * @throws  EncryptionException         Thrown if the encryption fails.
+     * @throws ValidationException          Thrown if the token will have already expired
+     *                                      even after adding the specified number of
+     *                                      additional seconds.
+     * @throws  ArithmeticException         If additional seconds is large enough such
+     *                                      that it would cause an arithmetic overflow
+     *                                      with a long (the current expiration time)
+     *                                      when added to the {@code additionalSecs}
+     *                                      parameter.
+     */
+    public String updateToken(int additionalSecs) throws EncryptionException, ValidationException {
+        if ( additionalSecs < 0) {
+            throw new IllegalArgumentException("additionalSecs argument must be >= 0.");
+        }
+        
+        // Avoid integer overflow. This could happen if one first calls
+        // setExpiration(Date) with a date far into the future. We want
+        // to avoid overflows as they might lead to security vulnerabilities.
+        long curExpTime = getExpiration();
+        preAdd(curExpTime, additionalSecs * 1000);
+            // Note: Can't use setExpiration(int) here was this needs a
+            //       'long'. Could convert to Date first, and use
+            //       setExpiration(Date) but that hardly seems worth the trouble.
+        expirationTime = curExpTime + (additionalSecs * 1000);
+        
+        if ( isExpired() ) {
+            // Too bad there is no ProcrastinationException ;-)
+            expirationTime = curExpTime;    // Restore the original value (which still may
+                                            // be expired.
+            throw new ValidationException("Token timed out.",
+                    "Cryptographic token not increased to sufficient value to prevent timeout.");
+            
+        }
+            // Don't change anything else (user acct name, attributes, skey, etc.)
+        return getToken();
+    }
+
+    /**
+     * Return the new encrypted token as a base64-encoded string, encrypted with
+     * the specified {@code SecretKey} with which this object was constructed.
+     * 
+     * @return The newly encrypted token.
+     * @see #getToken(SecretKey)
+     */
+    public String getToken() throws EncryptionException {
+        return createEncryptedToken(secretKey);
+    }
+   
+    // Create the actual encrypted token based on the specified SecretKey.
+    // This method will ensure that the decrypted token always ends with an
+    // unquoted delimiter.
+    private String createEncryptedToken(SecretKey skey) throws EncryptionException {
+        StringBuilder sb = new StringBuilder( getUserAccountName() + DELIM);
+        // CHECKME: Should we check here to see if token has already expired
+        //          and refuse to encrypt it (by throwing exception) if it has???
+        //          If so, then updateToken() should also be revisited.
+        sb.append( getExpiration() ).append( DELIM );
+        sb.append( getQuotedAttributes() );
+        
+        Encryptor encryptor = ESAPI.encryptor();
+        CipherText ct = encryptor.encrypt(skey, new PlainText( sb.toString() ) );
+        String b64 =
+            ESAPI.encoder().encodeForBase64(ct.asPortableSerializedByteArray(),
+                                            false);
+        return b64;
+    }
+    
+    // Return a string of all the attributes, properly quoted. This is used in
+    // creating the encrypted token. Note that this method ensures that the
+    // quoted attribute string always ends with an (quoted) delimiter.
+    private String getQuotedAttributes() {
+        StringBuilder sb = new StringBuilder();
+        Set< Entry<String,String> > keyValueSet = attributes.entrySet();
+        Iterator<Entry<String, String>> it = keyValueSet.iterator();
+        while( it.hasNext() ) {
+            Map.Entry<String,String> entry = it.next();
+            String key = entry.getKey();
+            String value = entry.getValue();
+            // Because attribute values may be confidential, we don't want to log them!
+            logger.debug(Logger.EVENT_UNSPECIFIED, "   " + key + " -> <not shown>");
+            sb.append(key + "=" + quoteAttributeValue( value ) + DELIM);
+        }
+        return sb.toString();
+    }
+    
+    // Do NOT define a toString() method as there may be sensitive
+    // information contained in the attribute names. If we absolutely
+    // need this, then only return the username and expiration time, and
+    // _maybe_ the attribute names, but not there values. And obviously,
+    // we NEVER want to include the SecretKey should we decide to do this.
+    /*
+     * public String toString() { return null; }
+     */
+    
+    
+    // Quote any special characters in value.
+    private static String quoteAttributeValue(String value) {
+        assert value != null : "Program error: Value should not be null."; // Empty is OK.
+        StringBuilder sb = new StringBuilder();
+        char[] charArray = value.toCharArray();
+        for( int i = 0; i < charArray.length; i++ ) {
+            char c = charArray[i];
+            if ( c == QUOTE_CHAR || c == '=' || c == DELIM_CHAR ) {
+                sb.append(QUOTE_CHAR).append(c);
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+    
+    // Parse the possibly quoted value and return the unquoted value.
+    private static String parseQuotedValue(String quotedValue) {
+        StringBuilder sb = new StringBuilder();
+        char[] charArray = quotedValue.toCharArray();
+        for( int i = 0; i < charArray.length; i++ ) {
+            char c = charArray[i];
+            if ( c == QUOTE_CHAR ) {
+                i++;    // Skip past quote character.
+                sb.append( charArray[i] );
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+    
+    /*
+     * Decrypt the encrypted token and parse it into the individual components.
+     * The string should always end with a semicolon (;) even when there are
+     * no attributes set.
+     * <p>
+     * Example of how quoted string might look:
+     * <pre>
+     *                            v              v  v            v     v
+     *  kwwall;1291183520293;abc=x\=yx;xyz=;efg=a\;a\;;bbb=quotes\\tuff\;;
+              |             |         |    |          |                  |
+     *
+     * </pre>
+     */
+    private void decryptToken(SecretKey skey, String b64token) throws EncryptionException, EncodingException {
+        byte[] token = null;
+        try {
+            token = ESAPI.encoder().decodeFromBase64(b64token);
+        } catch (IOException e) {
+            // CHECKME: Not clear if we should log the actual token itself. It's encrypted,
+            //          but could be arbitrarily long, especially since it is not valid
+            //          encoding. OTOH, it may help debugging as sometimes it may be a simple
+            //          case like someone failing to apply some other type of encoding
+            //          consistently (e.g., URL encoding), in which case logging this should
+            //          make this pretty obvious once a few of these are logged.
+            throw new EncodingException("Invalid base64 encoding.",
+                                          "Invalid base64 encoding. Encrypted token was: " + b64token);
+        }
+        CipherText ct = CipherText.fromPortableSerializedBytes(token);
+        Encryptor encryptor = ESAPI.encryptor();
+        PlainText pt = encryptor.decrypt(skey, ct);
+        String str = pt.toString();
+        assert str.endsWith(DELIM) : "Programming error: Expecting decrypted token to end with delim char, " + DELIM_CHAR;
+        char[] charArray = str.toCharArray();
+        int prevPos = -1;                // Position of previous unquoted delimiter.
+        int fieldNo = 0;
+        ArrayList<Object> fields = new ArrayList<Object>();
+        int lastPos = charArray.length;
+        for ( int curPos = 0; curPos < lastPos; curPos++ ) {
+            boolean quoted = false;
+            char curChar = charArray[curPos];
+            if ( curChar == QUOTE_CHAR ) {
+                // Found a case where we have quoted character. We need to skip
+                // over this and set the current character to the next character.
+                curPos++;
+                if ( curChar != lastPos ) {
+                    curChar = charArray[ curPos + 1 ];
+                    quoted = true;
+                } else {
+                    // Last position will always be a delimiter character that
+                    // should be treated as unquoted.
+                    curChar = DELIM_CHAR;
+                }
+            }
+            if ( curChar == DELIM_CHAR && !quoted ) {
+                // We found an actual (unquoted) field delimiter.
+                String record = str.substring(prevPos + 1, curPos);
+                fields.add( record );
+                fieldNo++;
+                prevPos = curPos;
+            } 
+        }
+        
+        Object[] objArray = fields.toArray();
+        assert fieldNo == objArray.length : "Program error: Mismatch of delimited field count.";
+        logger.debug(Logger.EVENT_UNSPECIFIED, "Found " + objArray.length + " fields.");
+        assert objArray.length >= 2 : "Missing mandatory fields from decrypted token (username &/or expiration time).";
+        username = ((String)(objArray[0])).toLowerCase();
+        String expTime = (String)objArray[1];
+        expirationTime = Long.parseLong(expTime);
+        
+        for( int i = 2; i < objArray.length; i++ ) {
+            String nvpair = (String)objArray[i];
+            int equalsAt = nvpair.indexOf("=");
+            if ( equalsAt == -1 ) {
+                throw new EncryptionException("Invalid attribute encountered in decrypted token.",
+                        "Malformed attribute name/value pair (" + nvpair + ") found in decrypted token.");
+            }
+            String name = nvpair.substring(0, equalsAt);
+            String quotedValue = nvpair.substring(equalsAt + 1);
+            String value = parseQuotedValue( quotedValue );
+            // Because attribute values may be confidential, we don't want to log them!
+            logger.debug(Logger.EVENT_UNSPECIFIED, "Attribute[" + i + "]: name=" + name + ", value=<not shown>");
+
+            // Check to make sure that attribute name is valid as per our regex.
+            Matcher attrNameChecker = attrNameRegex.matcher(name);
+            if ( attrNameChecker.matches() ) {
+                attributes.put(name, value);
+            } else {
+                throw new EncryptionException("Invalid attribute name encountered in decrypted token.",
+                                              "Invalid attribute name encountered in decrypted token; " +
+                                              "attribute name " + name + " does not match regex " +
+                                              ATTR_NAME_REGEX);
+            }
+            attributes.put(name, value);
+        }
+        return;
+    }
+    
+    private SecretKey getDefaultSecretKey(String encryptAlgorithm) {
+        assert encryptAlgorithm != null : "Encryption algorithm cannot be null";
+        byte[] skey = ESAPI.securityConfiguration().getMasterKey();
+        assert skey != null : "Can't obtain master key, Encryptor.MasterKey";
+        assert skey.length >= 7 :
+                        "Encryptor.MasterKey must be at least 7 bytes. " +
+                        "Length is: " + skey.length + " bytes.";
+        // Set up secretKeySpec for use for symmetric encryption and decryption,
+        // and set up the public/private keys for asymmetric encryption /
+        // decryption.
+        return new SecretKeySpec(skey, encryptAlgorithm );
+    }
+    
+    // Check precondition to see if addition of two operands will result in
+    // arithmetic overflow. Note that the operands are of two different
+    // integral types. I.e., check to see if:
+    //      long result = leftLongValue + rightIntValue
+    // would cause arithmetic overflow.
+    // Note: We know that as we use it here, leftLongValue will always be > 0,
+    //       so arithmetic underflow should never be possible, but we check for
+    //       it anyhow.
+    // Package level access to allow this to be used by other classes in this package.
+    static final void preAdd(long leftLongValue, int rightIntValue) throws ArithmeticException {
+        if ( rightIntValue > 0 && ( (leftLongValue + rightIntValue) < leftLongValue) ) {
+            throw new ArithmeticException("Arithmetic overflow for addition.");
+        }
+        if ( rightIntValue < 0 && ( (leftLongValue + rightIntValue) > leftLongValue) ) {
+            throw new ArithmeticException("Arithmetic underflow for addition.");
+        }
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java b/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java
new file mode 100644
index 0000000..17635ce
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java
@@ -0,0 +1,479 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2011 - The OWASP Foundation
+ */
+package org.owasp.esapi.crypto;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.ConfigurationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.DefaultSecurityConfiguration;
+import org.owasp.esapi.util.ByteConversionUtil;
+
+/**
+ * This class implements a Key Derivation Function (KDF) and supporting methods.
+ * A KDF is a function with which an input key (called the Key Derivation Key,
+ * or KDK) and other input data are used to securely generate (i.e., derive)
+ * keying material that can be employed by cryptographic algorithms.
+ * <p>
+ * <b>Acknowledgments</b>:
+ * ESAPI's KDF is patterned after suggestions first made by cryptographer
+ * Dr. David A. Wagner and later extended to follow KDF in counter mode
+ * as specified by section 5.1 of NIST SP 800-108. Jeffrey Walton and the NSA
+ * also made valuable suggestions regarding the modeling of the method,
+ * {@link #computeDerivedKey(SecretKey, int, String)}.
+ *
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class KeyDerivationFunction {
+	/**
+	 * Used to support backward compatibility. {@code kdfVersion} is used as the
+	 * version for the serialized encrypted ciphertext on all the "encrypt"
+	 * operations. This static field should be the same as
+	 * {@link CipherText#cipherTextVersion} and
+	 * {@link CipherTextSerializer#cipherTextSerializerVersion} to make sure
+	 * that these classes are all kept in-sync in order to support backward
+	 * compatibility of previously encrypted data.
+	 * <pre>
+	 * Previous versions:	20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
+	 *					    20130830 - Fix to issue #306 (release 2.1.0)
+	 * </pre>
+	 * @see CipherTextSerializer#asSerializedByteArray()
+	 * @see CipherText#asPortableSerializedByteArray()
+	 * @see CipherText#fromPortableSerializedBytes(byte[])
+	 */
+	public  static final int  originalVersion  = 20110203;	 // First version. Do not change. EVER!
+	public  static final int  kdfVersion       = 20130830;   // Current version. Format: YYYYMMDD, max is 99991231.
+	private static final long serialVersionUID = kdfVersion; // Format: YYYYMMDD
+	
+    // Pseudo-random function algorithms suitable for NIST KDF in counter mode.
+	// Note that HmacMD5 is intentionally omitted here!!!
+    public enum PRF_ALGORITHMS {
+    		// SHA-1, 160-bits
+        HmacSHA1(0, 160, "HmacSHA1"),
+        	// SHA-2 candidates, 256-, 384-, and 512-bits
+        HmacSHA256(1, 256, "HmacSHA256"),
+        HmacSHA384(2, 384, "HmacSHA384"),
+        HmacSHA512(3, 512, "HmacSHA512");
+        	// Reserved for SHA-3 winner, 224-, 256-, 384-, and 512-bits
+        	// Names not yet known. Will use standard JCE alg names here.
+        	//
+        	// E.g., might be something like
+        	//		HmacSHA3_224(4, 224, "HmacSHA3-224"),
+        	//		HmacSHA3_256(5, 256, "HmacSHA3-256"),
+        	//		HmacSHA3_384(6, 384, "HmacSHA3-385"),
+        	//		HmacSHA3_512(7, 512, "HmacSHA3-512");
+        // Reserved for future use -- values 8 through 15
+        //  Most likely these might be some other strong contenders that
+        //  were are based on HMACs from the NIST SHA-3 finalists.
+        
+        private final byte value;	// Value stored in serialized encrypted data to represent PRF
+        private final short bits;
+        private final String algName;
+        
+        PRF_ALGORITHMS(int value, int bits, String algName) {
+        	this.value = (byte) value;
+        	this.bits  = (short) bits;
+        	this.algName = algName;
+        }
+        
+        public byte getValue() { return value; }
+        public short getBits() { return bits; }
+        public String getAlgName() { return algName; }
+    }
+
+	private static final Logger logger = ESAPI.getLogger("KeyDerivationFunction");
+
+	private String prfAlg_ = null;
+	private int version_ = kdfVersion;
+	private String context_ = "";
+
+    // Check if versions of KeyDerivationFunction, CipherText, and
+    // CipherTextSerializer are all the same.
+    {
+    	// Ignore error about comparing identical versions and dead code.
+    	// We expect them to be, but the point is to catch us if they aren't.
+    	assert CipherTextSerializer.cipherTextSerializerVersion == CipherText.cipherTextVersion :
+            "Versions of CipherTextSerializer and CipherText are not compatible.";
+    	assert CipherTextSerializer.cipherTextSerializerVersion == KeyDerivationFunction.kdfVersion :
+    		"Versions of CipherTextSerializer and KeyDerivationFunction are not compatible.";
+    }
+    
+	/**
+	 * Construct a {@code KeyDerivationFunction}.
+	 * @param prfAlg	Specifies a supported algorithm.
+	 */
+	public KeyDerivationFunction(KeyDerivationFunction.PRF_ALGORITHMS prfAlg) {
+		this.prfAlg_ = prfAlg.getAlgName();
+	}
+
+	/**
+	 * Construct a {@code KeyDerivationFunction} based on the
+	 * <b>ESAPI.property</b> property, {@code Encryptor.KDF.PRF}.
+	 */
+	public KeyDerivationFunction() {			
+		String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		if ( ! KeyDerivationFunction.isValidPRF(prfName) ) {
+    		throw new ConfigurationException("Algorithm name " + prfName +
+    							" not a valid algorithm name for property " +
+    							DefaultSecurityConfiguration.KDF_PRF_ALG);
+		}
+		prfAlg_ = prfName;
+	}
+
+	/**
+	 * Return the name of the algorithm for the Pseudo Random Function (PRF)
+	 * that is being used.
+	 * @return	The PRF algorithm name.
+	 */
+	public String getPRFAlgName() {
+		return prfAlg_;		
+	}
+	
+	/**
+	 * Package level method for use by {@code CipherText} class to get default
+	 * 
+	 */
+	static int getDefaultPRFSelection() {
+		String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getAlgName().equals(prfName) ) {
+				return prf.getValue();
+			}
+		}
+		throw new ConfigurationException("Algorithm name " + prfName +
+				" not a valid algorithm name for property " +
+				DefaultSecurityConfiguration.KDF_PRF_ALG);
+	}
+	
+	/**
+	 * Set version so backward compatibility can be supported. Used to set the
+	 * version to some previous version so that previously encrypted data can
+	 * be decrypted.
+	 * @param version	Date as a integer, in format of YYYYMMDD. Maximum
+	 * 					version date is 99991231 (December 31, 9999).
+	 * @throws	IllegalArgumentException	If {@code version} is not within
+	 * 					the valid range of [20110203, 99991231].
+	 */
+	public void setVersion(int version) throws IllegalArgumentException {
+		CryptoHelper.isValidKDFVersion(version, false, true);
+		this.version_ = version;
+	}
+
+	/**
+	 * Return the version used for backward compatibility.
+	 * @return	The KDF version #, in format YYYYMMDD, used for supporting
+	 * 			backward compatibility.
+	 */
+	public int getVersion() {
+		return version_;
+	}
+	
+	
+	// TODO: IMPORTANT NOTE: In a future release (hopefully starting in 2.1.1),
+	// we will be using the 'context' to mix in some additional things. At a
+	// minimum, we will be using the KDF version (version_) so that downgrade version
+	// attacks are not possible. Other candidates are the cipher xform and
+	// the timestamp.
+	/**
+	 * Set the 'context' as specified by NIST Special Publication 800-108. NIST
+	 * defines 'context' as "A binary string containing the information related
+	 * to the derived keying material. It may include identities of parties who
+	 * are deriving and/or using the derived keying material and, optionally, a 
+	 * once known by the parties who derive the keys." NIST SP 800-108 seems to
+	 * imply that while 'context' is recommended, that it is optional. In section
+	 * 7.6 of NIST 800-108, NIST uses "SHOULD" rather than "MUST":
+	 * <blockquote>
+	 * "Derived keying material should be bound to all relying
+	 * entities and other information to identify the derived
+	 * keying material. This is called context binding.
+	 * In particular, the identity (or identifier, as the term
+	 * is defined in [NIST SP 800-56A , sic] and [NIST SP
+	 * 800-56B , sic]) of each entity that will access (meaning
+	 * derive, hold, use, and/or distribute) any segment of
+	 * the keying material should be included in the Context
+	 * string input to the KDF, provided that this information
+	 * is known by each entity who derives the keying material."
+	 * </blockquote>
+	 * The ISO/IEC's KDF2 uses a similar construction for their KDF and there
+	 * 'context' data is not specified at all. Therefore, ESAPI 2.0's
+	 * reference implementation, {@code JavaEncryptor}, chooses not to use
+	 * 'context' at all.
+	 * 
+	 * @param context	Optional binary string containing information related to
+	 * 					the derived keying material. By default (if this method
+	 * 					is never called), the empty string is used. May have any
+	 * 					value but {@code null}.
+	 */
+	public void setContext(String context) {
+		if ( context == null ) {
+			throw new IllegalArgumentException("Context may not be null.");
+		}
+		context_ = context;
+	}
+	
+	/**
+	 * Return the optional 'context' that typically contains information
+	 * related to the keying material, such as the identities of the message
+	 * sender and recipient.
+	 * @see #setContext(String)
+	 * @return The 'context' is returned.
+	 */
+	public String getContext() {
+		return context_;
+	}
+
+	/**
+	 * The method is ESAPI's Key Derivation Function (KDF) that computes a
+	 * derived key from the {@code keyDerivationKey} for either
+	 * encryption / decryption or for authentication.
+	 * <p>
+	 * <b>CAUTION:</b> If this algorithm for computing derived keys from the
+	 * key derivation key is <i>ever</i> changed, we risk breaking backward compatibility of being
+	 * able to decrypt data previously encrypted with earlier / different versions
+	 * of this method. Therefore, do not change this unless you are 100% certain that
+	 * what you are doing will NOT change either of the derived keys for
+	 * ANY "key derivation key" AT ALL!!!
+	 * <p>
+	 * <b>NOTE:</b> This method is generally not intended to be called separately.
+	 * It is used by ESAPI's reference crypto implementation class {@code JavaEncryptor}
+	 * and might be useful for someone implementing their own replacement class, but
+	 * generally it is not something that is useful to application client code.
+	 * 
+	 * @param keyDerivationKey  A key used as an input to a key derivation function
+	 *                          to derive other keys. This is the key that generally
+	 *                          is created using some key generation mechanism such as
+	 *                          {@link CryptoHelper#generateSecretKey(String, int)}. The
+	 *                          "input" key from which the other keys are derived.
+	 * 							The derived key will have the same algorithm type
+	 * 							as this key. This KDK cannot be null.
+	 * @param keySize		The cipher's key size (in bits) for the {@code keyDerivationKey}.
+	 * 						Must have a minimum size of 56 bits and be an integral multiple of 8-bits.
+	 * 						<b>Note:</b> The derived key will have the same size as this.
+	 * @param purpose		The purpose for the derived key. For the ESAPI reference implementation,
+	 * 						{@code JavaEncryptor}, this <i>must</i> be either the string "encryption" or
+	 * 						"authenticity", where "encryption" is used for creating a derived key to use
+	 * 						for confidentiality, and "authenticity" is used for creating a derived key to
+	 * 						use with a MAC to ensure message authenticity. However, since parameter serves
+	 * 						the same purpose as the "Label" in section 5.1 of NIST SP 800-108, it really can
+	 * 						be set to anything other than {@code null} or an empty string when called outside
+	 * 						of {@code JavaEncryptor}.
+	 * @return				The derived {@code SecretKey} to be used according
+	 * 						to the specified purpose. 
+	 * @throws NoSuchAlgorithmException		The {@code keyDerivationKey} has an unsupported
+	 * 						encryption algorithm or no current JCE provider supports
+	 * 						"HmacSHA1".
+	 * @throws EncryptionException		If "UTF-8" is not supported as an encoding, then
+	 * 						this is thrown with the original {@code UnsupportedEncodingException}
+	 * 						as the cause. (NOTE: This should never happen as "UTF-8" is supposed to
+	 * 						be a common encoding supported by all Java implementations. Support
+	 * 					    for it is usually in rt.jar.)
+	 * @throws InvalidKeyException 	Likely indicates a coding error. Should not happen.
+	 * @throws EncryptionException  Throw for some precondition violations.
+	 */
+	public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, String purpose)
+			throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException
+	{
+		// Acknowledgments: David Wagner first suggested this approach, I (Kevin Wall)
+		//				    stumbled upon NIST SP 800-108 and used it as a basis to
+		//				    extend it. Later it was changed that conforms more closely
+		//					to section 5.1 of NIST SP 800-108 based on feedback from
+		//					Jeffrey Walton.
+		//
+        // These probably should be turned into actual runtime checks and an
+        // IllegalArgumentException should be thrown if they are violated.
+		assert keyDerivationKey != null : "Key derivation key cannot be null.";
+			// We would choose a larger minimum key size, but we want to be
+			// able to accept DES for legacy encryption needs.
+		assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
+		assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
+		assert purpose != null && !purpose.equals("") : "Purpose may not be null or empty.";
+
+		keySize = calcKeySize( keySize );	// Safely convert to whole # of bytes.
+		byte[] derivedKey = new byte[ keySize ];
+		byte[] label;				    	// Same purpose as NIST SP 800-108's "label" in section 5.1.
+		byte[] context;						// See setContext() for details.
+		try {
+			label = purpose.getBytes("UTF-8");
+			context = context_.getBytes("UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			throw new EncryptionException("Encryption failure (internal encoding error: UTF-8)",
+					 "UTF-8 encoding is NOT supported as a standard byte encoding: " + e.getMessage(), e);
+		}
+		
+			// Note that keyDerivationKey is going to be some SecretKey like an AES or
+			// DESede key, but not an HmacSHA1 key. That means it is not likely
+			// going to be 20 bytes but something different. Experiments show
+			// that doesn't really matter though as the SecretKeySpec CTOR on
+			// the following line still returns the appropriate sized key for
+			// HmacSHA1. So, if keyDerivationKey was originally (say) a 56-bit
+            // DES key, then there is apparently some key-stretching going on here
+            // under the hood to create 'sk' so that it is 20 bytes. I cannot vouch
+            // for how secure this key-stretching is. Worse, it might not be specified
+            // as to *how* it is done and left to each JCE provider.
+		SecretKey sk = new SecretKeySpec(keyDerivationKey.getEncoded(), "HmacSHA1");
+		Mac mac = null;
+
+		try {
+			mac = Mac.getInstance("HmacSHA1");
+			mac.init(sk);
+		} catch( InvalidKeyException ex ) {
+			logger.error(Logger.SECURITY_FAILURE,
+					"Created HmacSHA1 Mac but SecretKey sk has alg " +
+					sk.getAlgorithm(), ex);
+			throw ex;
+		}
+		
+		// Repeatedly call of HmacSHA1 hash until we've collected enough bits
+		// for the derived key. The first time through, we calculate the HmacSHA1
+		// on the "purpose" string, but subsequent calculations are performed
+		// on the previous result.
+		int ctr = 1;		// Iteration counter for NIST 800-108
+		int totalCopied = 0;
+		int destPos = 0;
+		int len = 0;
+		byte[] tmpKey = null;	// Do not declare inside do-while loop!!!
+		do {
+			//
+			// This is to make our KDF more along the line of NIST's.
+			// NIST's Special Publication 800-108 performs the following in
+            // the iterative loop of Section 5.1:
+            //       n := number of blocks required to fulfill request
+            //       for i = 1 to n, do
+            //           K(i) := PRF(KDK, [i]2 || Label || 0x00 || Context || [L]2)
+            //           result(i) := result(i-1) || K(i)
+            //       end
+            // where '||' is represents bit string concatenation, and PRF is
+            // an NIST approved pseudo-random function (such as an HMAC),
+            // KDK is the key derivation key, [i]2 is the big-endian binary
+            // representation of the iteration, and [L]2 is the bits
+            // requested by the caller, and 0x00 represents a null byte
+            // used as a separation indicator.  However, other sections of this
+            // document (Section 7.6) implies that Context is to be an
+            // optional field (based on NIST's use of the word SHOULD
+            // rather than MUST)
+            // 
+			mac.update( ByteConversionUtil.fromInt( ctr++ ) );
+			mac.update(label);
+			mac.update((byte) '\0');
+			mac.update(context); // This is problematic for us. See Jeff Walton's
+								  // analysis of ESAPI 2.0's KDF for details.
+								  // Maybe for 2.1, we'll see; 2.0 too close to GA.
+			
+	            // According to the Javadoc for Mac.doFinal(byte[]),
+	            // "A call to this method resets this Mac object to the state it was
+	            // in when previously initialized via a call to init(Key) or
+	            // init(Key, AlgorithmParameterSpec). That is, the object is reset
+	            // and available to generate another MAC from the same key, if
+	            // desired, via new calls to update and doFinal." Therefore, we do
+	            // not do an explicit reset().
+			tmpKey = mac.doFinal( ByteConversionUtil.fromInt( keySize ) );
+			
+			if ( tmpKey.length >= keySize ) {
+				len = keySize;
+			} else {
+				len = Math.min(tmpKey.length, keySize - totalCopied);
+			}
+			System.arraycopy(tmpKey, 0, derivedKey, destPos, len);
+			label = tmpKey;
+			totalCopied += tmpKey.length;
+			destPos += len;
+		} while( totalCopied < keySize );
+		
+		// Don't leave remnants of the partial key in memory. (Note: we could
+		// not do this if tmpKey were declared in the do-while loop.
+		for ( int i = 0; i < tmpKey.length; i++ ) {
+			tmpKey[i] = '\0';
+		}
+		tmpKey = null;	// Make it immediately eligible for GC.
+		
+        // Convert it back into a SecretKey of the appropriate type.
+		return new SecretKeySpec(derivedKey, keyDerivationKey.getAlgorithm());
+	}
+
+	/**
+	 * Check if specified algorithm name is a valid PRF that can be used.
+	 * @param prfAlgName	Name of the PRF algorithm; e.g., "HmacSHA1", "HmacSHA384", etc.
+	 * @return	True if {@code prfAlgName} is supported, otherwise false.
+	 */
+	public static boolean isValidPRF(String prfAlgName) {
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getAlgName().equals(prfAlgName) ) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public static PRF_ALGORITHMS convertNameToPRF(String prfAlgName) {
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getAlgName().equals(prfAlgName) ) {
+				return prf;
+			}
+		}
+		throw new IllegalArgumentException("Algorithm name " + prfAlgName +
+				" not a valid PRF algorithm name for the ESAPI KDF.");
+	}
+	
+	public static PRF_ALGORITHMS convertIntToPRF(int selection) {
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+			if ( prf.getValue() == selection ) {
+				return prf;
+			}
+		}
+		throw new IllegalArgumentException("No KDF PRF algorithm found for value name " + selection);		
+	}
+	
+    /**
+     * Calculate the size of a key. The key size is given in bits, but we
+     * can only allocate them by octets (i.e., bytes), so make sure we
+     * round up to the next whole number of octets to have room for all
+     * the bits. For example, a key size of 9 bits would require 2 octets
+     * to store it.
+     *
+     * @param ks    The key size, in bits.
+     * @return      The key size, in octets, large enough to accommodate
+     *              {@code ks} bits.
+     */
+    private static int calcKeySize(int ks) {
+        assert ks > 0 : "Key size must be > 0 bits.";
+        int numBytes = 0;
+        int n = ks/8;
+        int rem = ks % 8;
+        if ( rem == 0 ) {
+            numBytes = n;
+        } else {
+            numBytes = n + 1;
+        }
+        return numBytes;
+    }
+
+    /**
+     * Print list of ESAPI supported pseudo-random functions for KDF and
+     * KDF version information.
+     *
+     * @param args  Required, but not used.
+     */
+	public static final void main(String args[]) {
+		System.out.println("Supported pseudo-random functions for KDF (version: " + kdfVersion + ")");
+		System.out.println("Enum Name\tAlgorithm\t# bits");
+		for (PRF_ALGORITHMS prf : PRF_ALGORITHMS.values()) {
+		    System.out.println(prf + "\t" + prf.getAlgName() + "\t" + prf.getBits());
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/PlainText.java b/src/main/java/org/owasp/esapi/crypto/PlainText.java
new file mode 100644
index 0000000..4491404
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/PlainText.java
@@ -0,0 +1,159 @@
+package org.owasp.esapi.crypto;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/**
+ * A class representing plaintext (versus ciphertext) as related to
+ * cryptographic systems.  This class embodies UTF-8 byte-encoding to
+ * translate between byte arrays and {@code String}s. Once constructed, this
+ * object is immutable.
+ * <p>
+ * Note: Conversion to/from UTF-8 byte-encoding can, in theory, throw
+ * an {@code UnsupportedEncodingException}. However, UTF-8 encoding
+ * should be a standard encoding for all Java installations, so an
+ * {@code UnsupportedEncodingException} never actually be thrown. Therefore,
+ * in order to to keep client code uncluttered, any possible
+ * {@code UnsupportedEncodingException}s will be first logged, and then
+ * re-thrown as a {@code RuntimeException} with the original
+ * {@code UnsupportedEncodingException} as the cause.
+ * <p>
+ * Copyright © 2009 - The OWASP Foundation
+ * </p>
+ * @author kevin.w.wall at gmail.com
+ * @see CipherText
+ * @since 2.0
+ */
+public final class PlainText implements Serializable {
+
+	private static final long serialVersionUID = 20090921;
+	private static Logger logger = ESAPI.getLogger("PlainText");
+	
+	private byte[] rawBytes = null;
+	
+	/**
+	 * Construct a {@code PlainText} object from a {@code String}.
+	 * @param str	The {@code String} that is converted to a UTF-8 encoded
+	 * 				byte array to create the {@code PlainText} object.
+	 * @throws IllegalArgumentException	If {@code str} argument is null.
+	 */
+	public PlainText(String str) {
+		try {
+		    assert str != null : "String for plaintext cannot be null.";
+		    if ( str == null ) {
+		    	throw new IllegalArgumentException("String for plaintext may not be null!");
+		    }
+			rawBytes = str.getBytes("UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			// Should never happen.
+			logger.error(Logger.EVENT_FAILURE, "PlainText(String) CTOR failed: Can't find UTF-8 byte-encoding!", e);
+			throw new RuntimeException("Can't find UTF-8 byte-encoding!", e);
+		}
+	}
+
+	/**
+	 * Construct a {@code PlainText} object from a {@code byte} array.
+	 * @param b	The {@code byte} array used to create the {@code PlainText}
+	 * 			object.
+	 */
+	public PlainText(byte[] b) {
+		// Must allow 0 length arrays though, to represent empty strings.
+		assert b != null : "Byte array representing plaintext cannot be null.";
+		    // Make copy so mutable byte array b can't change PlainText.
+		rawBytes = new byte[ b.length ];
+		System.arraycopy(b, 0, rawBytes, 0, b.length);
+	}
+	
+	/**
+	 * Convert the {@code PlainText} object to a UTF-8 encoded {@code String}.
+	 * @return	A {@code String} representing the {@code PlainText} object.
+	 */
+	public String toString() {
+		try {
+			return new String(rawBytes, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			// Should never happen.
+			logger.error(Logger.EVENT_FAILURE, "PlainText.toString() failed: Can't find UTF-8 byte-encoding!", e);
+			throw new RuntimeException("Can't find UTF-8 byte-encoding!", e);
+		}
+	}
+	
+	/**
+	 * Convert the {@code PlainText} object to a byte array.
+	 * @return	A byte array representing the {@code PlainText} object.
+	 */
+	public byte[] asBytes() {
+	    byte[] bytes = new byte[ rawBytes.length ];
+	    System.arraycopy(rawBytes, 0, bytes, 0, rawBytes.length);
+		return bytes;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override public boolean equals(Object anObject) {
+
+        if ( this == anObject ) return true;
+        if ( anObject == null ) return false;
+        boolean result = false;
+        if ( anObject instanceof PlainText ) {
+            PlainText that = (PlainText)anObject;
+            result = ( that.canEqual(this) &&
+                    ( this.toString().equals( that.toString() ) )
+                  );
+        }
+        return result;
+	}
+	
+	/**
+	 * Same as {@code this.toString().hashCode()}.
+	 * @return	{@code this.toString().hashCode()}.
+	 */
+	@Override public int hashCode() {
+		return this.toString().hashCode();
+	}
+	
+	/**
+	 * Return the length of the UTF-8 encoded byte array representing this
+	 * object. Note that if this object was constructed with the constructor
+	 * {@code PlainText(String str)}, then this length might not necessarily
+	 * agree with {@code str.length()}.
+	 * 
+	 * @return	The length of the UTF-8 encoded byte array representing this
+	 * 			object.
+	 */
+	public int length() {
+		return rawBytes.length;
+	}
+	
+	// DISCUSS: Should we set 'rawBytes' to null??? Won't make it eligible for
+	//			GC unless this PlainText object is set to null which can't do
+	//			from here. If we set it to null, most methods will cause
+	//			NullPointerException to be thrown. Also will have to change a
+	//			lot of JUnit tests.
+	/**
+	 * First overwrite the bytes of plaintext with the character '*'.
+	 */
+	public void overwrite() {
+		CryptoHelper.overwrite( rawBytes );
+		// rawBytes = null;					// See above comment re: discussion.
+	}
+	
+    /**
+     * Needed for correct definition of equals for general classes.
+     * (Technically not needed for 'final' classes though like this class
+     * though; this will just allow it to work in the future should we
+     * decide to allow * sub-classing of this class.)
+     * </p><p>
+     * See <a href="http://www.artima.com/lejava/articles/equality.html">
+     * How to write an Equality Method in Java</a>
+     * for full explanation.
+     * </p>
+     */
+    protected boolean canEqual(Object other) {
+        return (other instanceof PlainText);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java b/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java
new file mode 100644
index 0000000..cacc9c9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java
@@ -0,0 +1,284 @@
+package org.owasp.esapi.crypto;
+
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Hashtable;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/** 
+ * This class provides a generic static method that loads a
+ * {@code java.security.Provider} either by some generic name
+ * (i.e., {@code Provider.getName()}) or by a fully-qualified class name.
+ * It is intended to be called dynamically by an application to add a
+ * specific JCE provider at runtime.
+ * </p><p>
+ * If the {@code ESAPI.properties} file has a the property
+ * {@code ESAPI.PreferredJCEProvider} defined to either a recognized
+ * JCE provider (see below for list) or a fully qualified path name of
+ * that JCE provider's {@code Provider} class, then the reference implementation
+ * of ESAPI cryptography ({@code org.owasp.esapi.reference.crypto.JavaEncryptor})
+ * tries to load this specified JCE provider via
+ * {@link SecurityProviderLoader#insertProviderAt(String,int)}.
+ * </p>
+ */
+public class SecurityProviderLoader  {
+    private static Logger logger = ESAPI.getLogger("SecurityProviderLoader");
+    private static Hashtable<String, String> jceProviders;
+
+    static {
+        //
+        // Load the table with known providers. We load the (short) JCE name
+        // and the corresponding provider class. We don't 'new' the actual
+        // class name here because that would mean we would have to have all
+        // these jars. Instead we use reflection and do it dynamically only
+        // when SecurityProviderLoader.insertProviderAt() is called because
+        // presumably they will have the jar in their classpath for the
+        // provider they wish to use.
+        //
+        jceProviders = new Hashtable<String,String>();
+            // SunJCE is installed by default and should always be available
+            // with Sun-based JREs. As of JDK 1.3 and later, it is part of the
+            // standard JRE install.
+        jceProviders.put("SunJCE", "com.sun.crypto.provider.SunJCE");
+        
+            // IBMJCE is default for WebSphere and is used by IBM JDKs. They
+            // also have IBMJCEFIPS, but not sure if this is *always* provided
+            // with WebSphere or just an add-on, hence not including it. IBMJCEFIPS
+        	// is a FIPS 140-2 compliant JCE provider from IBM.
+        jceProviders.put("IBMJCE", "com.ibm.crypto.provider.IBMJCE");
+        // jceProviders.put("IBMJCEFIPS", "com.ibm.crypto.fips.provider.IBMJCEFIPS");
+        
+            // GnuCrypto is JCE provider for GNU Compiler for Java (GCJ)
+        jceProviders.put("GnuCrypto", "gnu.crypto.jce.GnuCrypto");
+
+            // Bouncy Castle -- http://www.bouncycastle.org/ - FOSS, maintained.
+        jceProviders.put("BC",
+                         "org.bouncycastle.jce.provider.BouncyCastleProvider");
+
+            // IAIK -- http://jce.iaik.tugraz.at/ -- No longer free.
+        jceProviders.put("IAIK", "iaik.security.provider.IAIK");
+
+            // IBM FIPS 140-2 compliant provider -- Commercial
+            // See above comments.
+        // jceProviders.put("IBMJCEFIPS", "com.ibm.crypto.fips.provider.IBMJCEFIPS");
+
+            // RSA FIPS 142-2 compliant provider -- Commercial
+        // jceProviders.put("RSA", "com.rsa.jsafe.crypto.CryptoJ");
+
+            // Cryptix -- http://www.cryptix.org/ - FOSS, not maintained.
+            // Cryptix JCE code signing cert expired 2009/08/29 and was not
+            // renewed.
+        jceProviders.put("CryptixCrypto", "cryptix.jce.provider.CryptixCrypto");
+        jceProviders.put("Cryptix", "cryptix.jce.provider.CryptixCrypto");
+
+            // ABA - FOSS, not maintained - Google for it, or maybe search for
+            // old copy at http://www.archive.org/
+        jceProviders.put("ABA", "au.net.aba.crypto.provider.ABAProvider");
+    }
+
+    /** 
+     * This methods adds a provider to the {@code SecurityManager}
+     * either by some generic name or by the class name. 
+     * </p><p>
+     * The following generic JCE provider names are built-in:
+     * <ul>
+     * <li>SunJCE</li>
+     * <li>IBMJCE [for WebSphere]</li>
+     * <li>GnuCrypto [for use with GNU Compiler for Java, i.e., gcj]</li>
+     * <li>BC [i.e., Bouncy Castle]</li>
+     * <li>IAIK</li>
+     * <li>CryptixCrypto (or Cryptix)
+     * <li>ABA
+     * </ul>
+     * Note that neither Cryptix or ABA are actively maintained so
+     * it is recommended that you do not start using them for ESAPI
+     * unless your application already has a dependency on them. Furthermore,
+     * the Cryptix JCE jars likely will not work as the Cryptix code signing
+     * certificate has expired as of August 28, 2009. (This likely is true
+     * for ABA, but I can't even find a copy to download!). Lastly, the IAIK
+     * provider is no longer offered as free, open source. It is not a
+     * commercial product. See {@link "http://jce.iaik.tugraz.at/"} for
+     * details. While some older versions were offered free, it is not clear
+     * whether the accompanying license still allows you to use it, and if
+     * it does, whether or not the code signing certificate used to sign
+     * their JCE jar(s) has expired are not.  Therefore, if you are looking
+     * for a FOSS alternative to SunJCE, Bouncy Castle
+     * ({@link "http://www.bouncycastle.org/"} is probably your best bet. The
+     * BC provider does support many the "combined cipher modes" that provide
+     * both confidentiality and authenticity. (See the {@code ESAPI.properties}
+     * property {@code Encryptor.cipher_modes.combined_modes} for details.)
+     * </p><p>
+     * For those working in the U.S. federal government, it should be noted
+     * that <i>none</i> of the providers listed here are considered validated
+     * by NIST's Cryptographic Module Validation Program and are therefore
+     * <b>not</b> considered FIPS 140-2 compliant. There are a few approved
+     * JCE compatible Java libraries that are on NIST's CMVP list, but this
+     * list changes constantly so they are not listed here. For further details
+     * on NIST's CMVP, see
+     * {@link "http://csrc.nist.gov/groups/STM/cmvp/index.html"}.
+     * </p><p>
+     * Finally, if you wish to use some other JCE provider not recognized above,
+     * you must specify the provider's fully-qualified class name (which in
+     * turn must have a public, no argument constructor).
+     * </p><p>
+     * The application must be given the {@code SecurityPermission} with a
+     * value of {@code insertProvider.<provider_name>} (where
+     * <provider_name> is the name of the algorithm provider if
+     * a security manager is installed.
+     * </p>
+     *
+     * @param algProvider Name of the JCE algorithm provider. If the name
+     *                    contains a ".", this is interpreted as the name
+     *                    of a {@code java.security.Provider} class name.
+     * @param pos         The preference position (starting at 1) that the
+     *                    caller would like for this provider. If you wish
+     *                    for it to be installed as the <i>last</i> provider
+     *                    (as of the time of this call), set {@code pos} to -1.
+     * @return The actual preference position at which the provider was added,
+     *         or -1 if the provider was not added because it is already
+     *         installed.
+     * @exception NoSuchProviderException - thrown if the provider class
+     *         could not be loaded or added to the {@code SecurityManager} or
+     *         any other reason for failure.
+     */
+    public static int insertProviderAt(String algProvider, int pos)
+        throws NoSuchProviderException
+    {
+        // We assume that if the algorithm provider contains a ".", then
+        // we interpret this as a crypto provider class name and dynamically
+        // add the provider. If it's one of the special ones we know about,
+        // we also dynamically create it. Otherwise, we assume the provider
+        // is in the "java.security" file.
+        Class<?> providerClass = null;
+        String clzName = null;
+        Provider cryptoProvider = null;
+        assert (pos == -1 || pos >= 1) : "Position pos must be -1 or integer >= 1";
+        try {
+            // Does algProvider look like a class name?
+            if (algProvider.indexOf('.') != -1) {
+                clzName = algProvider;
+            } else if ( jceProviders.containsKey(algProvider) ) {
+                // One of the special cases we know about.
+                clzName = jceProviders.get(algProvider);
+            } else {
+                throw new NoSuchProviderException("Unable to locate Provider class for " +
+                             "provider " + algProvider + ". Try using fully qualified class name " +
+                             "or check provider name for typos. Builtin provider names are: " +
+                             jceProviders.toString());
+            }
+
+            providerClass = Class.forName(clzName);
+            cryptoProvider = (Provider)providerClass.newInstance();
+
+            // Found from above. Note that Security.insertProviderAt() can
+            // throw a SecurityException if a Java SecurityManager is
+            // installed and application doesn't have appropriate
+            // permissions in policy file.
+            //
+            // However, since SecurityException is a RuntimeException it
+            // doesn't need to be explicitly declared on the throws clause.
+            // The application must be given the SecurityPermission with
+            // a value of "insertProvider.<provider_name>" (where
+            // <provider_name> is the name of the algorithm provider) if
+            // a SecurityManager is installed.
+            int ret;
+            if ( pos == -1 ) {      // Special case: Means place _last_.
+                ret = Security.addProvider(cryptoProvider);
+            } else {
+                ret = Security.insertProviderAt(cryptoProvider, pos);
+            }
+            if ( ret == -1 ) {
+                // log INFO that provider was already loaded.
+                String msg = "JCE provider '" + algProvider + "' already loaded";
+                if (pos == -1) {
+                    // The just wanted it available (loaded last) and it is, so
+                    // this is not critical.
+                    logger.always(Logger.SECURITY_SUCCESS, msg);
+                } else {
+                    // In this case, it's a warning because it may have already
+                    // been loaded, but *after* the position they requested.
+                    // For example, if they were trying to load a FIPS 140-2
+                    // compliant JCE provider at the first position and it was
+                    // already loaded at position 3, then this is not FIPS 140-2
+                    // compliant. Therefore, we make it a warning and a failure.
+                	// Also log separately using 'always' in case warnings suppressed
+                	// as per NSA suggestion.
+                    logger.warning(Logger.SECURITY_FAILURE, msg);
+                    logger.always(Logger.SECURITY_FAILURE, "(audit) " + msg);
+                }
+            } else {
+            	// As per NSA suggestion.
+                logger.always(Logger.SECURITY_AUDIT,
+                        "Successfully loaded preferred JCE provider " +
+                        algProvider + " at position " + pos);
+            }
+            return ret;
+        } catch(SecurityException ex) {
+            // CHECKME: Log security event here too? This is a RuntimeException.
+        	// It would only be thrown if a SecurityManager is installed that
+        	// prohibits Security.addProvider() or Security.insertProviderAt()
+        	// by the current user of this thread. Will log it here. Can always
+        	// be ignored.
+        	logger.always(Logger.SECURITY_FAILURE, "Failed to load preferred JCE provider " +
+        				  algProvider + " at position " + pos, ex);
+            throw ex;
+        } catch(Exception ex) {
+            // Possibilities include: ClassNotFoundException,
+            //                        InstantiationException, and others???
+            //
+            // Log an error & re-throw original message as NoSuchProviderException,
+            // since that what it probably really implied here. This probably a configuration
+        	// error (e.g., classpath problem, etc.) so we use EVENT_FAILURE rather than
+        	// SECURITY_FAILURE here.
+            logger.error(Logger.EVENT_FAILURE, "Failed to insert failed crypto " +
+                    " provider " + algProvider + " at position " + pos, ex);
+            throw new NoSuchProviderException("Failed to insert crypto " +
+                                       " provider for " + algProvider +
+                                       "; exception msg: " + ex.toString());
+        }
+    }
+    
+    /**
+     * Load the preferred JCE provider for ESAPI based on the <b>ESAPI.properties</b>
+     * property {@code Encryptor.PreferredJCEProvider}. If this property is null
+     * (i.e., unset) or set to an empty string, then no JCE provider is inserted
+     * at the "preferred" position and thus the Java VM continues to use whatever
+     * the default it was using for this (generally specified in the file
+     * {@code $JAVA_HOME/jre/security/java.security}).
+     * @return The actual preference position at which the provider was added,
+     *         (which is expected to be 1) or -1 if the provider was not added
+     *         because it is already installed at some other position. -1 is also
+     *         returned if the {@code Encryptor.PreferredJCEProvider} was not set
+     *         or set to an empty string, i.e., if the application <i>has</i> no
+     *         preferred JCE provider.
+     * @exception NoSuchProviderException - thrown if the provider class
+     *         could not be loaded or added to the {@code SecurityManager} or
+     *         any other reason for failure.
+     * @see <a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.htm">
+     *      ESAPI 2.0 Symmetric Encryption User Guide</a>
+     */
+    public static int loadESAPIPreferredJCEProvider() throws NoSuchProviderException
+    {
+        String prefJCEProvider =
+            ESAPI.securityConfiguration().getPreferredJCEProvider();
+        try {
+            // If unset or set to empty string, then don't try to change it.
+            if ( prefJCEProvider == null || prefJCEProvider.trim().length() == 0) {
+            		// Always log, per NSA suggestion.
+                logger.always(Logger.SECURITY_AUDIT, "No Encryptor.PreferredJCEProvider specified.");
+                return -1;  // Unchanged; it is, whatever it is.
+            } else {
+                return insertProviderAt(prefJCEProvider, 1);
+            }
+        } catch (NoSuchProviderException ex) {
+            // Will already have logged with exception msg.
+        	String msg = "failed to load *preferred* " + "JCE crypto provider, " + prefJCEProvider;
+        	logger.always(Logger.SECURITY_AUDIT, msg);	// Per NSA suggestion.
+            logger.error(Logger.SECURITY_FAILURE, msg);
+            throw ex;
+        }
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/crypto/package.html b/src/main/java/org/owasp/esapi/crypto/package.html
new file mode 100644
index 0000000..fcca55e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/crypto/package.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body bgcolor="white">
+This package contains ESAPI cryptography-related classes used throughout
+ESAPI.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/all-wcprops b/src/main/java/org/owasp/esapi/doc-files/.svn/all-wcprops
new file mode 100644
index 0000000..688efcb
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/all-wcprops
@@ -0,0 +1,59 @@
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files
+END
+Authenticator.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/Authenticator.jpg
+END
+HTTPUtilities.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/HTTPUtilities.jpg
+END
+AccessReferenceMap.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/AccessReferenceMap.jpg
+END
+Architecture.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/Architecture.jpg
+END
+OWASPTopTen.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/OWASPTopTen.jpg
+END
+IntrusionDetector.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/IntrusionDetector.jpg
+END
+Validator.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/Validator.jpg
+END
+AccessController.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/AccessController.jpg
+END
+Encryptor.jpg
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files/Encryptor.jpg
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/entries b/src/main/java/org/owasp/esapi/doc-files/.svn/entries
new file mode 100644
index 0000000..6a1692f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/entries
@@ -0,0 +1,334 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/doc-files
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-11-13T13:26:45.456114Z
+816
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+Architecture.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+f2b6a20ddbd34ba0e2563b5fe2f78711
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+77232
+

+OWASPTopTen.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+c3e58cb9de3157d43638d542f2f1fd2c
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+96552
+

+IntrusionDetector.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+4f5b4ec76c731fd5e12d8907413ac2e5
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+39551
+

+Validator.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+40aebfb1784019a6f4486ad212d33ba8
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+45676
+

+AccessController.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+5b08684c812eebe59a99dc0aead91121
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+52532
+

+Encryptor.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+6814bfd0d2c95094def162034b916988
+2009-11-13T13:26:45.456114Z
+816
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+51236
+

+Authenticator.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+243268296394d84a9e9c5b265b8c211c
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+49988
+

+HTTPUtilities.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+9806dc207281ca3baf439977035b0ad9
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+36727
+

+AccessReferenceMap.jpg
+file
+
+
+
+
+2014-02-18T16:19:53.429975Z
+7b350f2b031e7d4e688b97e0a70fac47
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+48956
+

diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/AccessController.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/AccessController.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/AccessController.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/AccessReferenceMap.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/AccessReferenceMap.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/AccessReferenceMap.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Architecture.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Architecture.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Architecture.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Authenticator.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Authenticator.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Authenticator.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Encryptor.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Encryptor.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Encryptor.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/HTTPUtilities.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/HTTPUtilities.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/HTTPUtilities.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/IntrusionDetector.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/IntrusionDetector.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/IntrusionDetector.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/OWASPTopTen.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/OWASPTopTen.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/OWASPTopTen.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Validator.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Validator.jpg.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/doc-files/.svn/prop-base/Validator.jpg.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/AccessController.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/AccessController.jpg.svn-base
new file mode 100644
index 0000000..414d6a1
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/AccessController.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/AccessReferenceMap.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/AccessReferenceMap.jpg.svn-base
new file mode 100644
index 0000000..c9611c3
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/AccessReferenceMap.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Architecture.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Architecture.jpg.svn-base
new file mode 100644
index 0000000..913e24a
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Architecture.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Authenticator.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Authenticator.jpg.svn-base
new file mode 100644
index 0000000..af40202
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Authenticator.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Encryptor.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Encryptor.jpg.svn-base
new file mode 100644
index 0000000..a52da89
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Encryptor.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/HTTPUtilities.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/HTTPUtilities.jpg.svn-base
new file mode 100644
index 0000000..f114d52
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/HTTPUtilities.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/IntrusionDetector.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/IntrusionDetector.jpg.svn-base
new file mode 100644
index 0000000..78a3345
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/IntrusionDetector.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/OWASPTopTen.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/OWASPTopTen.jpg.svn-base
new file mode 100644
index 0000000..3c9dcf5
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/OWASPTopTen.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Validator.jpg.svn-base b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Validator.jpg.svn-base
new file mode 100644
index 0000000..72fd9c9
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/.svn/text-base/Validator.jpg.svn-base differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/AccessController.jpg b/src/main/java/org/owasp/esapi/doc-files/AccessController.jpg
new file mode 100644
index 0000000..414d6a1
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/AccessController.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/AccessReferenceMap.jpg b/src/main/java/org/owasp/esapi/doc-files/AccessReferenceMap.jpg
new file mode 100644
index 0000000..c9611c3
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/AccessReferenceMap.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/Architecture.jpg b/src/main/java/org/owasp/esapi/doc-files/Architecture.jpg
new file mode 100644
index 0000000..913e24a
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/Architecture.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/Authenticator.jpg b/src/main/java/org/owasp/esapi/doc-files/Authenticator.jpg
new file mode 100644
index 0000000..af40202
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/Authenticator.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/Encryptor.jpg b/src/main/java/org/owasp/esapi/doc-files/Encryptor.jpg
new file mode 100644
index 0000000..a52da89
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/Encryptor.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/HTTPUtilities.jpg b/src/main/java/org/owasp/esapi/doc-files/HTTPUtilities.jpg
new file mode 100644
index 0000000..f114d52
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/HTTPUtilities.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/IntrusionDetector.jpg b/src/main/java/org/owasp/esapi/doc-files/IntrusionDetector.jpg
new file mode 100644
index 0000000..78a3345
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/IntrusionDetector.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/OWASPTopTen.jpg b/src/main/java/org/owasp/esapi/doc-files/OWASPTopTen.jpg
new file mode 100644
index 0000000..3c9dcf5
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/OWASPTopTen.jpg differ
diff --git a/src/main/java/org/owasp/esapi/doc-files/Validator.jpg b/src/main/java/org/owasp/esapi/doc-files/Validator.jpg
new file mode 100644
index 0000000..72fd9c9
Binary files /dev/null and b/src/main/java/org/owasp/esapi/doc-files/Validator.jpg differ
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/all-wcprops b/src/main/java/org/owasp/esapi/errors/.svn/all-wcprops
new file mode 100644
index 0000000..811f5d9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/all-wcprops
@@ -0,0 +1,131 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors
+END
+ValidationAvailabilityException.java
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java
+END
+EncodingException.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/EncodingException.java
+END
+EncryptionException.java
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/EncryptionException.java
+END
+ValidationException.java
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/ValidationException.java
+END
+IntrusionException.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/IntrusionException.java
+END
+AuthenticationCredentialsException.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java
+END
+ExecutorException.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/ExecutorException.java
+END
+EncryptionRuntimeException.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/package.html
+END
+ValidationUploadException.java
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java
+END
+AccessControlException.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/AccessControlException.java
+END
+ConfigurationException.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/ConfigurationException.java
+END
+AuthenticationLoginException.java
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java
+END
+EnterpriseSecurityException.java
+K 25
+svn:wc:ra_dav:version-url
+V 105
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java
+END
+AvailabilityException.java
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/AvailabilityException.java
+END
+EnterpriseSecurityRuntimeException.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java
+END
+AuthenticationHostException.java
+K 25
+svn:wc:ra_dav:version-url
+V 105
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java
+END
+IntegrityException.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/IntegrityException.java
+END
+AuthenticationException.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/AuthenticationException.java
+END
+AuthenticationAccountsException.java
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java
+END
+CertificateException.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors/CertificateException.java
+END
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/entries b/src/main/java/org/owasp/esapi/errors/.svn/entries
new file mode 100644
index 0000000..a80df63
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/entries
@@ -0,0 +1,742 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/errors
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-12-01T06:10:09.321108Z
+1663
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+CertificateException.java
+file
+
+
+
+
+2014-02-18T16:19:53.469976Z
+b6d8631f9897e5e82f9121c4d6cb4352
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1807
+

+ValidationAvailabilityException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+1afd39f1b1de91f2fe1c8e0b3100213e
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1727
+

+EncodingException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+b13cd3628f09c166fd58d1540ef6d17b
+2010-12-01T06:10:09.321108Z
+1663
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1854
+

+EncryptionException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+0c996a980fc491f54b7294e2fdb628ac
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1877
+

+ValidationException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+0a784bc4c3c52f8b9a028881be46f2e5
+2009-07-10T05:22:23.827015Z
+565
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3471
+

+IntrusionException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+0964257796a28142f8f5bcc1302da5df
+2010-09-28T06:09:00.718012Z
+1545
+jtmelton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3190
+

+AuthenticationCredentialsException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+2eabced0a30f31cdb7d469b78134bb51
+2010-12-01T06:10:09.321108Z
+1663
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1893
+

+ExecutorException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+9261230c7f3188db49ac66c17f485b90
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1860
+

+EncryptionRuntimeException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+1cbc61f5ec6b00a34f4caff9b70a1490
+2010-10-13T22:20:17.790391Z
+1556
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1945
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+b5e1431fd586fef88c2f164528aaa431
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+529
+

+ValidationUploadException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+1bf173fbb8350cae382b4135b0225087
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1716
+

+AccessControlException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+8828a2203fda8e141c9d2387a1f1055e
+2010-12-01T06:10:09.321108Z
+1663
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1742
+

+ConfigurationException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+1d86e540714e3ee35e8a1e13bc42b79f
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1048
+

+AuthenticationLoginException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+a78e8a65110534856d148feae21dfbc7
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1849
+

+EnterpriseSecurityException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+87a0beb2146d94e28ac7c9df763e37cf
+2010-09-28T06:09:00.718012Z
+1545
+jtmelton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5113
+

+AvailabilityException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+e4e64771c3da5146636cfd9ab2cd6da8
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1921
+

+EnterpriseSecurityRuntimeException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+37154548e005a60f6f60ba795536f733
+2010-10-13T22:20:17.790391Z
+1556
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5116
+

+AuthenticationHostException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+b970bb90f5ecbac17617d8dbf9e21638
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1836
+

+IntegrityException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+a901eb2003a6a183178598623ac1bb58
+2009-06-30T21:03:49.197070Z
+546
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2000
+

+AuthenticationException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+82047180d21fc8e0c919f857a2d1a8b7
+2010-12-01T06:10:09.321108Z
+1663
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1905
+

+AuthenticationAccountsException.java
+file
+
+
+
+
+2014-02-18T16:19:53.473976Z
+e64b8e84191c7c0964265f42ac120540
+2010-12-01T06:10:09.321108Z
+1663
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1895
+

diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/AccessControlException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AccessControlException.java.svn-base
new file mode 100644
index 0000000..2c54417
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AccessControlException.java.svn-base
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AccessControlException should be thrown when a user attempts to access a
+ * resource that they are not authorized for.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AccessControlException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new access control exception.
+	 */
+	protected AccessControlException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of {@code AccessControlException}.
+     *
+     * @param userMessage
+     * @param logMessage
+     */
+	public AccessControlException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new access control exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the root cause
+	 */
+	public AccessControlException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationAccountsException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationAccountsException.java.svn-base
new file mode 100644
index 0000000..98954db
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationAccountsException.java.svn-base
@@ -0,0 +1,59 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationAccountsException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationAccountsException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of {@code AuthenticationAccountsException}.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged     
+	 *       
+	 */
+	public AuthenticationAccountsException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the root cause
+	 */
+	public AuthenticationAccountsException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationCredentialsException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationCredentialsException.java.svn-base
new file mode 100644
index 0000000..0bcd0a7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationCredentialsException.java.svn-base
@@ -0,0 +1,58 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationCredentialsException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationCredentialsException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of {@code AuthenticationCredentialsException}.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 */
+	public AuthenticationCredentialsException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the root cause
+	 */
+	public AuthenticationCredentialsException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationException.java.svn-base
new file mode 100644
index 0000000..12ef63e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationException.java.svn-base
@@ -0,0 +1,58 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of {@code AuthenticationException}.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     */
+    public AuthenticationException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new authentication exception.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     * @param cause the root cause
+     */
+    public AuthenticationException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationHostException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationHostException.java.svn-base
new file mode 100644
index 0000000..159a60b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationHostException.java.svn-base
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationHostException should be thrown when there is a problem with
+ * the host involved with authentication, particularly if the host changes unexpectedly.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationHostException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationHostException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of AuthenticationHostException.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 */
+	public AuthenticationHostException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the cause
+	 */
+	public AuthenticationHostException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationLoginException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationLoginException.java.svn-base
new file mode 100644
index 0000000..6effd2a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AuthenticationLoginException.java.svn-base
@@ -0,0 +1,58 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationLoginException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationLoginException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of EnterpriseSecurityException.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 */
+	public AuthenticationLoginException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the cause
+	 */
+	public AuthenticationLoginException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/AvailabilityException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AvailabilityException.java.svn-base
new file mode 100644
index 0000000..3530cc3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/AvailabilityException.java.svn-base
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AvailabilityException should be thrown when the availability of a limited
+ * resource is in jeopardy. For example, if a database connection pool runs out
+ * of connections, an availability exception should be thrown.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AvailabilityException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new availability exception.
+	 */
+	protected AvailabilityException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of AvailabilityException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     */
+    public AvailabilityException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new AvailabilityException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     * @param cause the cause
+     */
+    public AvailabilityException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/CertificateException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/CertificateException.java.svn-base
new file mode 100644
index 0000000..957d32a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/CertificateException.java.svn-base
@@ -0,0 +1,56 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * A CertificateException should be thrown for any problems that arise during
+ * processing of digital certificates.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class CertificateException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new certificate exception.
+	 */
+	protected CertificateException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of CertificateException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     */
+    public CertificateException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new CertificateException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     * @param cause the cause
+     */
+    public CertificateException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/ConfigurationException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ConfigurationException.java.svn-base
new file mode 100644
index 0000000..f34c25a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ConfigurationException.java.svn-base
@@ -0,0 +1,34 @@
+package org.owasp.esapi.errors;
+
+/**
+ * A {@code ConfigurationException} should be thrown when a problem arises because of
+ * a problem in one of ESAPI's configuration files, such as a missing required
+ * property or invalid setting of a property, or missing or unreadable
+ * configuration file, etc.
+ * <p>
+ * A {@code ConfigurationException} is a {@code RuntimeException}
+ * because 1) configuration properties can, for the most part, only be checked
+ * at run-time, and 2) we want this to be an unchecked exception to make ESAPI
+ * easy to use and not cluttered with catching a bunch of try/catch blocks.
+ * </p>
+ */
+public class ConfigurationException extends RuntimeException {
+
+	protected static final long serialVersionUID = 1L;
+
+	public ConfigurationException(Exception e) {
+		super(e);
+	}
+
+	public ConfigurationException(String s) {
+		super(s);
+	}
+	
+	public ConfigurationException(String s, Throwable cause) {
+		super(s, cause);
+	}
+	
+	public ConfigurationException(Throwable cause) {
+		super(cause);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncodingException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncodingException.java.svn-base
new file mode 100644
index 0000000..9335004
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncodingException.java.svn-base
@@ -0,0 +1,62 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An EncodingException should be thrown for any problems that occur when
+ * encoding or decoding data.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncodingException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new service exception.
+	 */
+	protected EncodingException() {
+		// hidden
+	}
+
+       /**
+     * Creates a new instance of EncodingException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EncodingException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new EncodingException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public EncodingException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncryptionException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncryptionException.java.svn-base
new file mode 100644
index 0000000..965a52e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncryptionException.java.svn-base
@@ -0,0 +1,61 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An EncryptionException should be thrown for any problems related to
+ * encryption, hashing, or digital signatures.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncryptionException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new EncryptionException.
+	 */
+	protected EncryptionException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EncryptionException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public EncryptionException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncryptionRuntimeException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncryptionRuntimeException.java.svn-base
new file mode 100644
index 0000000..466ef5c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EncryptionRuntimeException.java.svn-base
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2010 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An EncryptionRuntimeException should be thrown for any problems related to
+ * encryption, hashing, or digital signatures.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class EncryptionRuntimeException extends EnterpriseSecurityRuntimeException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new EncryptionException.
+	 */
+	protected EncryptionRuntimeException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EncryptionRuntimeException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public EncryptionRuntimeException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityException.java.svn-base
new file mode 100644
index 0000000..a09df2c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityException.java.svn-base
@@ -0,0 +1,124 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+
+/**
+ * EnterpriseSecurityException is the base class for all security related exceptions. You should pass in the root cause
+ * exception where possible. Constructors for classes extending EnterpriseSecurityException should be sure to call the
+ * appropriate super() method in order to ensure that logging and intrusion detection occur properly.
+ * <P>
+ * All EnterpriseSecurityExceptions have two messages, one for the user and one for the log file. This way, a message
+ * can be shown to the user that doesn't contain sensitive information or unnecessary implementation details. Meanwhile,
+ * all the critical information can be included in the exception so that it gets logged.
+ * <P>
+ * Note that the "logMessage" for ALL EnterpriseSecurityExceptions is logged in the log file. This feature should be
+ * used extensively throughout ESAPI implementations and the result is a fairly complete set of security log records.
+ * ALL EnterpriseSecurityExceptions are also sent to the IntrusionDetector for use in detecting anomalous patterns of
+ * application usage.
+ * <P>
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EnterpriseSecurityException extends Exception {
+
+    protected static final long serialVersionUID = 1L;
+
+    /** The logger. */
+    protected final transient Logger logger = ESAPI.getLogger("EnterpriseSecurityException");
+
+    /**
+     *
+     */
+    protected String logMessage = null;
+
+    /**
+     * Instantiates a new security exception.
+     */
+    protected EnterpriseSecurityException() {
+        // hidden
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by
+     * using this API, applications will generate an extensive security log. In addition, this exception is
+     * automatically registered with the IntrusionDetector, so that quotas can be checked.
+     *
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     * 
+     * @param userMessage 
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EnterpriseSecurityException(String userMessage, String logMessage) {
+    	super(userMessage);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException that includes a root cause Throwable.
+     * 
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     *
+     * @param userMessage
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause the cause
+     */
+    public EnterpriseSecurityException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, cause);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+    
+    /**
+     * Returns message meant for display to users
+     *
+     * Note that if you are unsure of what set this message, it would probably
+     * be a good idea to encode this message before displaying it to the end user.
+     * 
+     * @return a String containing a message that is safe to display to users
+     */
+    public String getUserMessage() {
+        return getMessage();
+    }
+
+    /**
+     * Returns a message that is safe to display in logs, but may contain
+     * sensitive information and therefore probably should not be displayed to
+     * users.
+     * 
+     * @return a String containing a message that is safe to display in logs,
+     * but probably not to users as it may contain sensitive information.
+     */
+    public String getLogMessage() {
+        return logMessage;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityRuntimeException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityRuntimeException.java.svn-base
new file mode 100644
index 0000000..5f746f4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityRuntimeException.java.svn-base
@@ -0,0 +1,126 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2010 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+
+/**
+ * EnterpriseSecurityRuntimeException is the base class for all security related runtime exceptions. You should pass in the root cause
+ * exception wherever possible. Constructors for classes extending this class should be sure to call the
+ * appropriate super() method in order to ensure that logging and intrusion detection occur properly.
+ * <P>
+ * All EnterpriseSecurityRuntimeExceptions have two messages, one for the user and one for the log file. This way, a message
+ * can be shown to the user that doesn't contain sensitive information or unnecessary implementation details. Meanwhile,
+ * all the critical information can be included in the exception so that it gets logged.
+ * <P>
+ * Note that the "logMessage" for ALL EnterpriseSecurityRuntimeExceptions is logged in the log file. This feature should be
+ * used extensively throughout ESAPI implementations and the result is a fairly complete set of security log records.
+ * ALL EnterpriseSecurityRuntimeExceptions are also sent to the IntrusionDetector for use in detecting anomalous patterns of
+ * application usage.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class EnterpriseSecurityRuntimeException extends java.lang.RuntimeException {
+
+    protected static final long serialVersionUID = 1L;
+
+    /** The logger. */
+    protected final Logger logger = ESAPI.getLogger(this.getClass());
+
+    /**
+     *
+     */
+    protected String logMessage = null;
+
+    /**
+     * Instantiates a new security exception.
+     */
+    protected EnterpriseSecurityRuntimeException() {
+        // hidden
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by
+     * using this API, applications will generate an extensive security log. In addition, this exception is
+     * automatically registered with the IntrusionDetector, so that quotas can be checked.
+     *
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     * 
+     * @param userMessage 
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EnterpriseSecurityRuntimeException(String userMessage, String logMessage) {
+    	super(userMessage);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException that includes a root cause Throwable.
+     * 
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     *
+     * @param userMessage
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause the cause
+     */
+    public EnterpriseSecurityRuntimeException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, cause);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+    
+    /**
+     * Returns message meant for display to users
+     *
+     * Note that if you are unsure of what set this message, it would probably
+     * be a good idea to encode this message before displaying it to the end user.
+     * 
+     * @return a String containing a message that is safe to display to users
+     */
+    public String getUserMessage() {
+        return getMessage();
+    }
+
+    /**
+     * Returns a message that is safe to display in logs, but may contain
+     * sensitive information and therefore probably should not be displayed to
+     * users.
+     * 
+     * @return a String containing a message that is safe to display in logs,
+     * but probably not to users as it may contain sensitive information.
+     */
+    public String getLogMessage() {
+        return logMessage;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/ExecutorException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ExecutorException.java.svn-base
new file mode 100644
index 0000000..d453129
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ExecutorException.java.svn-base
@@ -0,0 +1,62 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An ExecutorException should be thrown for any problems that arise during the
+ * execution of a system executable.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ExecutorException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new ExecutorException.
+	 */
+	protected ExecutorException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of ExecutorException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public ExecutorException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new ExecutorException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public ExecutorException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/IntegrityException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/IntegrityException.java.svn-base
new file mode 100644
index 0000000..02e3797
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/IntegrityException.java.svn-base
@@ -0,0 +1,62 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An IntegrityException should be thrown when a problem with the integrity of data
+ * has been detected. For example, if a financial account cannot be reconciled after
+ * a transaction has been performed, an integrity exception should be thrown.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntegrityException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new availability exception.
+	 */
+	protected IntegrityException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of IntegrityException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public IntegrityException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new IntegrityException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public IntegrityException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/IntrusionException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/IntrusionException.java.svn-base
new file mode 100644
index 0000000..4af7f41
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/IntrusionException.java.svn-base
@@ -0,0 +1,92 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/**
+ * An IntrusionException should be thrown anytime an error condition arises that is likely to be the result of an attack
+ * in progress. IntrusionExceptions are handled specially by the IntrusionDetector, which is equipped to respond by
+ * either specially logging the event, logging out the current user, or invalidating the current user's account.
+ * <P>
+ * Unlike other exceptions in the ESAPI, the IntrusionException is a RuntimeException so that it can be thrown from
+ * anywhere and will not require a lot of special exception handling.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntrusionException extends RuntimeException {
+
+    /** The Constant serialVersionUID. */
+    private static final long serialVersionUID = 1L;
+
+    /** The logger. */
+    protected final transient Logger logger = ESAPI.getLogger("IntrusionException");
+
+    /**
+     *
+     */
+    protected String logMessage = null;
+
+    /**
+     * Creates a new instance of IntrusionException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public IntrusionException(String userMessage, String logMessage) {
+        super(userMessage);
+        this.logMessage = logMessage;
+        logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage);
+    }
+
+    /**
+     * Instantiates a new intrusion exception.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause 
+     *			  the cause
+     */
+    public IntrusionException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, cause);
+        this.logMessage = logMessage;
+        logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage, cause);
+    }
+
+    /**
+     * Returns a String containing a message that is safe to display to users
+     * 
+     * @return a String containing a message that is safe to display to users
+     */
+    public String getUserMessage() {
+        return getMessage();
+    }
+
+    /**
+     * Returns a String that is safe to display in logs, but probably not to users
+     * 
+     * @return a String containing a message that is safe to display in logs, but probably not to users
+     */
+    public String getLogMessage() {
+        return logMessage;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationAvailabilityException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationAvailabilityException.java.svn-base
new file mode 100644
index 0000000..d749e4f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationAvailabilityException.java.svn-base
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationAvailabilityException extends ValidationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new validation exception.
+	 */
+	protected ValidationAvailabilityException() {
+		// hidden
+	}
+
+    /**
+     * Create a new ValidationException
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+	public ValidationAvailabilityException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+    /**
+     * Create a new ValidationException
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     * 			  the cause
+     */			  
+	public ValidationAvailabilityException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationException.java.svn-base
new file mode 100644
index 0000000..b6ebcb9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationException.java.svn-base
@@ -0,0 +1,115 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * A ValidationException should be thrown to indicate that the data provided by
+ * the user or from some other external source does not match the validation
+ * rules that have been specified for that data.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationException extends EnterpriseSecurityException {
+
+    protected static final long serialVersionUID = 1L;
+
+	/** The UI reference that caused this ValidationException */
+	private String context;
+
+	/**
+	 * Instantiates a new validation exception.
+	 */
+	protected ValidationException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public ValidationException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public ValidationException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+    
+    /**
+     * Creates a new instance of ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param context
+     *            the source that caused this exception
+     */
+    public ValidationException(String userMessage, String logMessage, String context) {
+        super(userMessage, logMessage);
+        setContext(context);
+    }
+    
+    /**
+     * Instantiates a new ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     * @param context
+     *            the source that caused this exception
+     */
+    public ValidationException(String userMessage, String logMessage, Throwable cause, String context) {
+        super(userMessage, logMessage, cause);
+    	setContext(context);
+    }
+    
+	/**
+	 * Returns the UI reference that caused this ValidationException
+	 *  
+	 * @return context, the source that caused the exception, stored as a string
+	 */
+	public String getContext() {
+		return context;
+	}
+
+	/**
+	 * Set's the UI reference that caused this ValidationException
+	 *  
+	 * @param context
+	 * 			the context to set, passed as a String
+	 */
+	public void setContext(String context) {
+		this.context = context;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationUploadException.java.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationUploadException.java.svn-base
new file mode 100644
index 0000000..0541e42
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/ValidationUploadException.java.svn-base
@@ -0,0 +1,59 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationUploadException extends ValidationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new validation exception.
+	 */
+	protected ValidationUploadException() {
+		// hidden
+	}
+
+    /**
+     * Create a new ValidationException
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+	public ValidationUploadException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+    /**
+     * Create a new ValidationException
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     * 			  the cause
+     */
+	public ValidationUploadException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/errors/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..cb35f9c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/.svn/text-base/package.html.svn-base
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+A set of exception classes designed to model the error conditions that
+frequently arise in enterprise web applications and web services. The
+root class is the EnterpriseSecurityException and all the other
+exception classes extend this basic class. The
+EnterpriseSecurityException automatically logs a message in the
+constructor so that a full set of security events are captured in the
+logs.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/errors/AccessControlException.java b/src/main/java/org/owasp/esapi/errors/AccessControlException.java
new file mode 100644
index 0000000..2c54417
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/AccessControlException.java
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AccessControlException should be thrown when a user attempts to access a
+ * resource that they are not authorized for.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AccessControlException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new access control exception.
+	 */
+	protected AccessControlException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of {@code AccessControlException}.
+     *
+     * @param userMessage
+     * @param logMessage
+     */
+	public AccessControlException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new access control exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the root cause
+	 */
+	public AccessControlException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java
new file mode 100644
index 0000000..98954db
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java
@@ -0,0 +1,59 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationAccountsException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationAccountsException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of {@code AuthenticationAccountsException}.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged     
+	 *       
+	 */
+	public AuthenticationAccountsException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the root cause
+	 */
+	public AuthenticationAccountsException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java
new file mode 100644
index 0000000..0bcd0a7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java
@@ -0,0 +1,58 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationCredentialsException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationCredentialsException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of {@code AuthenticationCredentialsException}.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 */
+	public AuthenticationCredentialsException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the root cause
+	 */
+	public AuthenticationCredentialsException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationException.java
new file mode 100644
index 0000000..12ef63e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/AuthenticationException.java
@@ -0,0 +1,58 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of {@code AuthenticationException}.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     */
+    public AuthenticationException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new authentication exception.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     * @param cause the root cause
+     */
+    public AuthenticationException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java
new file mode 100644
index 0000000..159a60b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationHostException should be thrown when there is a problem with
+ * the host involved with authentication, particularly if the host changes unexpectedly.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationHostException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationHostException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of AuthenticationHostException.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 */
+	public AuthenticationHostException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the cause
+	 */
+	public AuthenticationHostException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java
new file mode 100644
index 0000000..6effd2a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java
@@ -0,0 +1,58 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AuthenticationException should be thrown when anything goes wrong during
+ * login or logout. They are also appropriate for any problems related to
+ * identity management.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticationLoginException extends AuthenticationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new authentication exception.
+	 */
+	protected AuthenticationLoginException() {
+		// hidden
+	}
+
+	/**
+	 * Creates a new instance of EnterpriseSecurityException.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 */
+	public AuthenticationLoginException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+	/**
+	 * Instantiates a new authentication exception.
+	 * 
+	 * @param userMessage the message displayed to the user
+	 * @param logMessage the message logged
+	 * @param cause the cause
+	 */
+	public AuthenticationLoginException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/AvailabilityException.java b/src/main/java/org/owasp/esapi/errors/AvailabilityException.java
new file mode 100644
index 0000000..3530cc3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/AvailabilityException.java
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An AvailabilityException should be thrown when the availability of a limited
+ * resource is in jeopardy. For example, if a database connection pool runs out
+ * of connections, an availability exception should be thrown.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AvailabilityException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new availability exception.
+	 */
+	protected AvailabilityException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of AvailabilityException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     */
+    public AvailabilityException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new AvailabilityException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     * @param cause the cause
+     */
+    public AvailabilityException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/CertificateException.java b/src/main/java/org/owasp/esapi/errors/CertificateException.java
new file mode 100644
index 0000000..957d32a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/CertificateException.java
@@ -0,0 +1,56 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * A CertificateException should be thrown for any problems that arise during
+ * processing of digital certificates.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class CertificateException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new certificate exception.
+	 */
+	protected CertificateException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of CertificateException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     */
+    public CertificateException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new CertificateException.
+     * 
+     * @param userMessage the message displayed to the user
+     * @param logMessage the message logged
+     * @param cause the cause
+     */
+    public CertificateException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/ConfigurationException.java b/src/main/java/org/owasp/esapi/errors/ConfigurationException.java
new file mode 100644
index 0000000..f34c25a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/ConfigurationException.java
@@ -0,0 +1,34 @@
+package org.owasp.esapi.errors;
+
+/**
+ * A {@code ConfigurationException} should be thrown when a problem arises because of
+ * a problem in one of ESAPI's configuration files, such as a missing required
+ * property or invalid setting of a property, or missing or unreadable
+ * configuration file, etc.
+ * <p>
+ * A {@code ConfigurationException} is a {@code RuntimeException}
+ * because 1) configuration properties can, for the most part, only be checked
+ * at run-time, and 2) we want this to be an unchecked exception to make ESAPI
+ * easy to use and not cluttered with catching a bunch of try/catch blocks.
+ * </p>
+ */
+public class ConfigurationException extends RuntimeException {
+
+	protected static final long serialVersionUID = 1L;
+
+	public ConfigurationException(Exception e) {
+		super(e);
+	}
+
+	public ConfigurationException(String s) {
+		super(s);
+	}
+	
+	public ConfigurationException(String s, Throwable cause) {
+		super(s, cause);
+	}
+	
+	public ConfigurationException(Throwable cause) {
+		super(cause);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/errors/EncodingException.java b/src/main/java/org/owasp/esapi/errors/EncodingException.java
new file mode 100644
index 0000000..9335004
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/EncodingException.java
@@ -0,0 +1,62 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An EncodingException should be thrown for any problems that occur when
+ * encoding or decoding data.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncodingException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new service exception.
+	 */
+	protected EncodingException() {
+		// hidden
+	}
+
+       /**
+     * Creates a new instance of EncodingException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EncodingException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new EncodingException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public EncodingException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/EncryptionException.java b/src/main/java/org/owasp/esapi/errors/EncryptionException.java
new file mode 100644
index 0000000..965a52e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/EncryptionException.java
@@ -0,0 +1,61 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An EncryptionException should be thrown for any problems related to
+ * encryption, hashing, or digital signatures.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncryptionException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new EncryptionException.
+	 */
+	protected EncryptionException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EncryptionException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public EncryptionException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java b/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java
new file mode 100644
index 0000000..466ef5c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2010 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An EncryptionRuntimeException should be thrown for any problems related to
+ * encryption, hashing, or digital signatures.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class EncryptionRuntimeException extends EnterpriseSecurityRuntimeException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new EncryptionException.
+	 */
+	protected EncryptionRuntimeException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EncryptionRuntimeException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new EncryptionException.
+     * 
+     * @param userMessage
+     *            the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public EncryptionRuntimeException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java
new file mode 100644
index 0000000..a09df2c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java
@@ -0,0 +1,124 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+
+/**
+ * EnterpriseSecurityException is the base class for all security related exceptions. You should pass in the root cause
+ * exception where possible. Constructors for classes extending EnterpriseSecurityException should be sure to call the
+ * appropriate super() method in order to ensure that logging and intrusion detection occur properly.
+ * <P>
+ * All EnterpriseSecurityExceptions have two messages, one for the user and one for the log file. This way, a message
+ * can be shown to the user that doesn't contain sensitive information or unnecessary implementation details. Meanwhile,
+ * all the critical information can be included in the exception so that it gets logged.
+ * <P>
+ * Note that the "logMessage" for ALL EnterpriseSecurityExceptions is logged in the log file. This feature should be
+ * used extensively throughout ESAPI implementations and the result is a fairly complete set of security log records.
+ * ALL EnterpriseSecurityExceptions are also sent to the IntrusionDetector for use in detecting anomalous patterns of
+ * application usage.
+ * <P>
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EnterpriseSecurityException extends Exception {
+
+    protected static final long serialVersionUID = 1L;
+
+    /** The logger. */
+    protected final transient Logger logger = ESAPI.getLogger("EnterpriseSecurityException");
+
+    /**
+     *
+     */
+    protected String logMessage = null;
+
+    /**
+     * Instantiates a new security exception.
+     */
+    protected EnterpriseSecurityException() {
+        // hidden
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by
+     * using this API, applications will generate an extensive security log. In addition, this exception is
+     * automatically registered with the IntrusionDetector, so that quotas can be checked.
+     *
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     * 
+     * @param userMessage 
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EnterpriseSecurityException(String userMessage, String logMessage) {
+    	super(userMessage);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException that includes a root cause Throwable.
+     * 
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     *
+     * @param userMessage
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause the cause
+     */
+    public EnterpriseSecurityException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, cause);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+    
+    /**
+     * Returns message meant for display to users
+     *
+     * Note that if you are unsure of what set this message, it would probably
+     * be a good idea to encode this message before displaying it to the end user.
+     * 
+     * @return a String containing a message that is safe to display to users
+     */
+    public String getUserMessage() {
+        return getMessage();
+    }
+
+    /**
+     * Returns a message that is safe to display in logs, but may contain
+     * sensitive information and therefore probably should not be displayed to
+     * users.
+     * 
+     * @return a String containing a message that is safe to display in logs,
+     * but probably not to users as it may contain sensitive information.
+     */
+    public String getLogMessage() {
+        return logMessage;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java
new file mode 100644
index 0000000..5f746f4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java
@@ -0,0 +1,126 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2010 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+
+/**
+ * EnterpriseSecurityRuntimeException is the base class for all security related runtime exceptions. You should pass in the root cause
+ * exception wherever possible. Constructors for classes extending this class should be sure to call the
+ * appropriate super() method in order to ensure that logging and intrusion detection occur properly.
+ * <P>
+ * All EnterpriseSecurityRuntimeExceptions have two messages, one for the user and one for the log file. This way, a message
+ * can be shown to the user that doesn't contain sensitive information or unnecessary implementation details. Meanwhile,
+ * all the critical information can be included in the exception so that it gets logged.
+ * <P>
+ * Note that the "logMessage" for ALL EnterpriseSecurityRuntimeExceptions is logged in the log file. This feature should be
+ * used extensively throughout ESAPI implementations and the result is a fairly complete set of security log records.
+ * ALL EnterpriseSecurityRuntimeExceptions are also sent to the IntrusionDetector for use in detecting anomalous patterns of
+ * application usage.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class EnterpriseSecurityRuntimeException extends java.lang.RuntimeException {
+
+    protected static final long serialVersionUID = 1L;
+
+    /** The logger. */
+    protected final Logger logger = ESAPI.getLogger(this.getClass());
+
+    /**
+     *
+     */
+    protected String logMessage = null;
+
+    /**
+     * Instantiates a new security exception.
+     */
+    protected EnterpriseSecurityRuntimeException() {
+        // hidden
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by
+     * using this API, applications will generate an extensive security log. In addition, this exception is
+     * automatically registered with the IntrusionDetector, so that quotas can be checked.
+     *
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     * 
+     * @param userMessage 
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public EnterpriseSecurityRuntimeException(String userMessage, String logMessage) {
+    	super(userMessage);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+
+    /**
+     * Creates a new instance of EnterpriseSecurityException that includes a root cause Throwable.
+     * 
+     * It should be noted that messages that are intended to be displayed to the user should be safe for display. In
+     * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the
+     * context of the exception.
+     *
+     * @param userMessage
+     * 			  the message displayed to the user
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause the cause
+     */
+    public EnterpriseSecurityRuntimeException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, cause);
+        this.logMessage = logMessage;
+        if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) {
+        	ESAPI.intrusionDetector().addException(this);
+        }
+    }
+    
+    /**
+     * Returns message meant for display to users
+     *
+     * Note that if you are unsure of what set this message, it would probably
+     * be a good idea to encode this message before displaying it to the end user.
+     * 
+     * @return a String containing a message that is safe to display to users
+     */
+    public String getUserMessage() {
+        return getMessage();
+    }
+
+    /**
+     * Returns a message that is safe to display in logs, but may contain
+     * sensitive information and therefore probably should not be displayed to
+     * users.
+     * 
+     * @return a String containing a message that is safe to display in logs,
+     * but probably not to users as it may contain sensitive information.
+     */
+    public String getLogMessage() {
+        return logMessage;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/ExecutorException.java b/src/main/java/org/owasp/esapi/errors/ExecutorException.java
new file mode 100644
index 0000000..d453129
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/ExecutorException.java
@@ -0,0 +1,62 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An ExecutorException should be thrown for any problems that arise during the
+ * execution of a system executable.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ExecutorException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new ExecutorException.
+	 */
+	protected ExecutorException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of ExecutorException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public ExecutorException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new ExecutorException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public ExecutorException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/IntegrityException.java b/src/main/java/org/owasp/esapi/errors/IntegrityException.java
new file mode 100644
index 0000000..02e3797
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/IntegrityException.java
@@ -0,0 +1,62 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * An IntegrityException should be thrown when a problem with the integrity of data
+ * has been detected. For example, if a financial account cannot be reconciled after
+ * a transaction has been performed, an integrity exception should be thrown.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntegrityException extends EnterpriseSecurityException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new availability exception.
+	 */
+	protected IntegrityException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of IntegrityException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public IntegrityException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new IntegrityException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public IntegrityException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/errors/IntrusionException.java b/src/main/java/org/owasp/esapi/errors/IntrusionException.java
new file mode 100644
index 0000000..4af7f41
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/IntrusionException.java
@@ -0,0 +1,92 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/**
+ * An IntrusionException should be thrown anytime an error condition arises that is likely to be the result of an attack
+ * in progress. IntrusionExceptions are handled specially by the IntrusionDetector, which is equipped to respond by
+ * either specially logging the event, logging out the current user, or invalidating the current user's account.
+ * <P>
+ * Unlike other exceptions in the ESAPI, the IntrusionException is a RuntimeException so that it can be thrown from
+ * anywhere and will not require a lot of special exception handling.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntrusionException extends RuntimeException {
+
+    /** The Constant serialVersionUID. */
+    private static final long serialVersionUID = 1L;
+
+    /** The logger. */
+    protected final transient Logger logger = ESAPI.getLogger("IntrusionException");
+
+    /**
+     *
+     */
+    protected String logMessage = null;
+
+    /**
+     * Creates a new instance of IntrusionException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public IntrusionException(String userMessage, String logMessage) {
+        super(userMessage);
+        this.logMessage = logMessage;
+        logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage);
+    }
+
+    /**
+     * Instantiates a new intrusion exception.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause 
+     *			  the cause
+     */
+    public IntrusionException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, cause);
+        this.logMessage = logMessage;
+        logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage, cause);
+    }
+
+    /**
+     * Returns a String containing a message that is safe to display to users
+     * 
+     * @return a String containing a message that is safe to display to users
+     */
+    public String getUserMessage() {
+        return getMessage();
+    }
+
+    /**
+     * Returns a String that is safe to display in logs, but probably not to users
+     * 
+     * @return a String containing a message that is safe to display in logs, but probably not to users
+     */
+    public String getLogMessage() {
+        return logMessage;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java b/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java
new file mode 100644
index 0000000..d749e4f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationAvailabilityException extends ValidationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new validation exception.
+	 */
+	protected ValidationAvailabilityException() {
+		// hidden
+	}
+
+    /**
+     * Create a new ValidationException
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+	public ValidationAvailabilityException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+    /**
+     * Create a new ValidationException
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     * 			  the cause
+     */			  
+	public ValidationAvailabilityException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/ValidationException.java b/src/main/java/org/owasp/esapi/errors/ValidationException.java
new file mode 100644
index 0000000..b6ebcb9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/ValidationException.java
@@ -0,0 +1,115 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * A ValidationException should be thrown to indicate that the data provided by
+ * the user or from some other external source does not match the validation
+ * rules that have been specified for that data.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationException extends EnterpriseSecurityException {
+
+    protected static final long serialVersionUID = 1L;
+
+	/** The UI reference that caused this ValidationException */
+	private String context;
+
+	/**
+	 * Instantiates a new validation exception.
+	 */
+	protected ValidationException() {
+		// hidden
+	}
+
+    /**
+     * Creates a new instance of ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+    public ValidationException(String userMessage, String logMessage) {
+        super(userMessage, logMessage);
+    }
+
+    /**
+     * Instantiates a new ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     */
+    public ValidationException(String userMessage, String logMessage, Throwable cause) {
+        super(userMessage, logMessage, cause);
+    }
+    
+    /**
+     * Creates a new instance of ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param context
+     *            the source that caused this exception
+     */
+    public ValidationException(String userMessage, String logMessage, String context) {
+        super(userMessage, logMessage);
+        setContext(context);
+    }
+    
+    /**
+     * Instantiates a new ValidationException.
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     *            the cause
+     * @param context
+     *            the source that caused this exception
+     */
+    public ValidationException(String userMessage, String logMessage, Throwable cause, String context) {
+        super(userMessage, logMessage, cause);
+    	setContext(context);
+    }
+    
+	/**
+	 * Returns the UI reference that caused this ValidationException
+	 *  
+	 * @return context, the source that caused the exception, stored as a string
+	 */
+	public String getContext() {
+		return context;
+	}
+
+	/**
+	 * Set's the UI reference that caused this ValidationException
+	 *  
+	 * @param context
+	 * 			the context to set, passed as a String
+	 */
+	public void setContext(String context) {
+		this.context = context;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java b/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java
new file mode 100644
index 0000000..0541e42
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java
@@ -0,0 +1,59 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationUploadException extends ValidationException {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Instantiates a new validation exception.
+	 */
+	protected ValidationUploadException() {
+		// hidden
+	}
+
+    /**
+     * Create a new ValidationException
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     */
+	public ValidationUploadException(String userMessage, String logMessage) {
+		super(userMessage, logMessage);
+	}
+
+    /**
+     * Create a new ValidationException
+     * 
+     * @param userMessage
+     *            the message to display to users
+     * @param logMessage
+	 * 			  the message logged
+     * @param cause
+     * 			  the cause
+     */
+	public ValidationUploadException(String userMessage, String logMessage, Throwable cause) {
+		super(userMessage, logMessage, cause);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/errors/package.html b/src/main/java/org/owasp/esapi/errors/package.html
new file mode 100644
index 0000000..cb35f9c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/errors/package.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+A set of exception classes designed to model the error conditions that
+frequently arise in enterprise web applications and web services. The
+root class is the EnterpriseSecurityException and all the other
+exception classes extend this basic class. The
+EnterpriseSecurityException automatically logs a message in the
+constructor so that a full set of security events are captured in the
+logs.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/all-wcprops b/src/main/java/org/owasp/esapi/filters/.svn/all-wcprops
new file mode 100644
index 0000000..0aadca4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters/package.html
+END
+RequestRateThrottleFilter.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java
+END
+SecurityWrapperRequest.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java
+END
+ClickjackFilter.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java
+END
+SecurityWrapperResponse.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java
+END
+ESAPIFilter.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters/ESAPIFilter.java
+END
+SecurityWrapper.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters/SecurityWrapper.java
+END
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/entries b/src/main/java/org/owasp/esapi/filters/.svn/entries
new file mode 100644
index 0000000..4b5d8e8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/entries
@@ -0,0 +1,266 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/filters
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-11-09T22:06:16.875552Z
+1653
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.585959Z
+0ba17c5bbb7d09acde7ac5d5ea103920
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+353
+

+RequestRateThrottleFilter.java
+file
+
+
+
+
+2014-02-18T16:19:52.585959Z
+ff2f0738c42a9dfc2bb87cac5d2d158c
+2009-10-24T22:27:35.714449Z
+721
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4257
+

+SecurityWrapperRequest.java
+file
+
+
+
+
+2014-02-18T16:19:52.585959Z
+178e5d28b096fcc57fe8b51a8f9293aa
+2010-11-09T22:06:16.875552Z
+1653
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+32342
+

+ClickjackFilter.java
+file
+
+
+
+
+2014-02-18T16:19:52.585959Z
+3a783d784712166c7b17ff57c751baf9
+2010-01-16T20:49:06.836699Z
+943
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3994
+

+SecurityWrapperResponse.java
+file
+
+
+
+
+2014-02-18T16:19:52.585959Z
+b3ab868739485362c0cc46e4f1e7a186
+2010-11-05T02:58:19.425543Z
+1645
+jtmelton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+18982
+

+ESAPIFilter.java
+file
+
+
+
+
+2014-02-18T16:19:52.585959Z
+d13043815f987ef99202f0da7aa7d321
+2009-12-09T05:15:28.334517Z
+907
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5101
+

+SecurityWrapper.java
+file
+
+
+
+
+2014-02-18T16:19:52.585959Z
+beb5f0ca172551e0142bbbcaad2fa616
+2009-12-05T03:08:44.297728Z
+854
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5552
+

diff --git a/src/main/java/org/owasp/esapi/filters/.svn/text-base/ClickjackFilter.java.svn-base b/src/main/java/org/owasp/esapi/filters/.svn/text-base/ClickjackFilter.java.svn-base
new file mode 100644
index 0000000..735d21e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/text-base/ClickjackFilter.java.svn-base
@@ -0,0 +1,108 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author     Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created    February 6, 2009
+ */
+
+package org.owasp.esapi.filters;
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * The {@code ClickjackFilter} is discussed at
+ * {@link http://www.owasp.org/index.php/ClickjackFilter_for_Java_EE}.
+ * <pre>
+ *     <filter>
+ *            <filter-name>ClickjackFilterDeny</filter-name>
+ *            <filter-class>org.owasp.filters.ClickjackFilter</filter-class>
+ *            <init-param>
+ *                <param-name>mode</param-name>
+ *                 <param-value>DENY</param-value>
+ *             </init-param>
+ *         </filter>
+ *         
+ *         <filter>
+ *             <filter-name>ClickjackFilterSameOrigin</filter-name>
+ *             <filter-class>org.owasp.filters.ClickjackFilter</filter-class>
+ *             <init-param>
+ *                 <param-name>mode</param-name>
+ *                 <param-value>SAMEORIGIN</param-value>
+ *             </init-param>
+ *         </filter>
+ *        
+ *        <!--  use the Deny version to prevent anyone, including yourself, from framing the page -->
+ *        <filter-mapping> 
+ *            <filter-name>ClickjackFilterDeny</filter-name>
+ *            <url-pattern>/*</url-pattern>
+ *        </filter-mapping>
+ *         
+ *         <!-- use the SameOrigin version to allow your application to frame, but nobody else
+ *         <filter-mapping> 
+ *            <filter-name>ClickjackFilterSameOrigin</filter-name>
+ *             <url-pattern>/*</url-pattern>
+ *         </filter-mapping>
+ * </pre>
+ */
+public class ClickjackFilter implements Filter 
+{
+
+	private String mode = "DENY";
+
+	/**
+	 * Initialize "mode" parameter from web.xml. Valid values are "DENY" and "SAMEORIGIN". 
+	 * If you leave this parameter out, the default is to use the DENY mode.
+	 * 
+	 * @param filterConfig A filter configuration object used by a servlet container
+	 *                     to pass information to a filter during initialization. 
+	 */
+	public void init(FilterConfig filterConfig) {
+		String configMode = filterConfig.getInitParameter("mode");
+		if ( configMode != null && ( configMode.equals( "DENY" ) || configMode.equals( "SAMEORIGIN" ) ) ) {
+			mode = configMode;
+		}
+	}
+	
+	/**
+	 * Add X-FRAME-OPTIONS response header to tell IE8 (and any other browsers who
+	 * decide to implement) not to display this content in a frame. For details, please
+	 * refer to
+	 * {@link http://blogs.msdn.com/sdl/archive/2009/02/05/clickjacking-defense-in-ie8.aspx}.
+	 * 
+	 * @param request The request object.
+	 * @param response The response object.
+	 * @param chain Refers to the {@code FilterChain} object to pass control to the
+	 *              next {@code Filter}.
+	 * @throws IOException
+	 * @throws ServletException
+	 */
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+	{
+        HttpServletResponse res = (HttpServletResponse)response;
+        chain.doFilter(request, response);
+        res.addHeader("X-FRAME-OPTIONS", mode );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void destroy() {
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/text-base/ESAPIFilter.java.svn-base b/src/main/java/org/owasp/esapi/filters/.svn/text-base/ESAPIFilter.java.svn-base
new file mode 100644
index 0000000..840d17b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/text-base/ESAPIFilter.java.svn-base
@@ -0,0 +1,141 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AuthenticationException;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class ESAPIFilter implements Filter {
+
+	private final Logger logger = ESAPI.getLogger("ESAPIFilter");
+
+	private static final String[] obfuscate = { "password" };
+
+	/**
+	 * Called by the web container to indicate to a filter that it is being
+	 * placed into service. The servlet container calls the init method exactly
+	 * once after instantiating the filter. The init method must complete
+	 * successfully before the filter is asked to do any filtering work.
+	 * 
+	 * @param filterConfig
+	 *            configuration object
+	 */
+	public void init(FilterConfig filterConfig) {
+		String path = filterConfig.getInitParameter("resourceDirectory");
+		if ( path != null ) {
+			ESAPI.securityConfiguration().setResourceDirectory( path );
+		}
+	}
+
+	/**
+	 * The doFilter method of the Filter is called by the container each time a
+	 * request/response pair is passed through the chain due to a client request
+	 * for a resource at the end of the chain. The FilterChain passed in to this
+	 * method allows the Filter to pass on the request and response to the next
+	 * entity in the chain.
+	 * 
+	 * @param req
+	 *            Request object to be processed
+	 * @param resp
+	 *            Response object
+	 * @param chain
+	 *            current FilterChain
+	 * @exception IOException
+	 *                if any occurs
+	 */
+	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException {
+		HttpServletRequest request = (HttpServletRequest) req;
+		HttpServletResponse response = (HttpServletResponse) resp;
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		
+		try {
+			// figure out who the current user is
+			try {
+				ESAPI.authenticator().login(request, response);
+			} catch( AuthenticationException e ) {
+				ESAPI.authenticator().logout();
+				request.setAttribute("message", "Authentication failed");
+				RequestDispatcher dispatcher = request.getRequestDispatcher("WEB-INF/login.jsp");
+				dispatcher.forward(request, response);
+				return;
+			}
+
+			// log this request, obfuscating any parameter named password
+			ESAPI.httpUtilities().logHTTPRequest(request, logger, Arrays.asList(obfuscate));
+
+			// check access to this URL
+			if ( !ESAPI.accessController().isAuthorizedForURL(request.getRequestURI()) ) {
+				request.setAttribute("message", "Unauthorized" );
+				RequestDispatcher dispatcher = request.getRequestDispatcher("WEB-INF/index.jsp");
+				dispatcher.forward(request, response);
+				return;
+			}
+
+			// check for CSRF attacks
+			// ESAPI.httpUtilities().checkCSRFToken();
+			
+			// forward this request on to the web application
+			chain.doFilter(request, response);
+
+			// set up response with content type
+			ESAPI.httpUtilities().setContentType( response );
+
+            // set no-cache headers on every response
+            // only do this if the entire site should not be cached
+            // otherwise you should do this strategically in your controller or actions
+			ESAPI.httpUtilities().setNoCacheHeaders( response );
+            
+		} catch (Exception e) {
+			logger.error( Logger.SECURITY_FAILURE, "Error in ESAPI security filter: " + e.getMessage(), e );
+			request.setAttribute("message", e.getMessage() );
+			
+		} finally {
+			// VERY IMPORTANT
+			// clear out the ThreadLocal variables in the authenticator
+			// some containers could possibly reuse this thread without clearing the User
+			ESAPI.clearCurrent();
+		}
+	}
+
+	/**
+	 * Called by the web container to indicate to a filter that it is being
+	 * taken out of service. This method is only called once all threads within
+	 * the filter's doFilter method have exited or after a timeout period has
+	 * passed. After the web container calls this method, it will not call the
+	 * doFilter method again on this instance of the filter.
+	 */
+	public void destroy() {
+		// finalize
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/text-base/RequestRateThrottleFilter.java.svn-base b/src/main/java/org/owasp/esapi/filters/.svn/text-base/RequestRateThrottleFilter.java.svn-base
new file mode 100644
index 0000000..0ca121e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/text-base/RequestRateThrottleFilter.java.svn-base
@@ -0,0 +1,114 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import org.owasp.esapi.ESAPI;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Stack;
+
+/**
+ * A simple servlet filter that limits the request rate to a certain threshold of requests per second.
+ * The default rate is 5 hits in 10 seconds. This can be overridden in the web.xml file by adding
+ * parameters named "hits" and "period" with the desired values. When the rate is exceeded, a short
+ * string is written to the response output stream and the chain method is not invoked. Otherwise,
+ * processing proceeds as normal.
+ */
+public class RequestRateThrottleFilter implements Filter
+{
+
+    private int hits = 5;
+
+    private int period = 10;
+
+    private static final String HITS = "hits";
+
+    private static final String PERIOD = "period";
+
+    /**
+     * Called by the web container to indicate to a filter that it is being
+     * placed into service. The servlet container calls the init method exactly
+     * once after instantiating the filter. The init method must complete
+     * successfully before the filter is asked to do any filtering work.
+     * 
+     * @param filterConfig
+     *            configuration object
+     */
+    public void init(FilterConfig filterConfig)
+    {
+        hits = Integer.parseInt(filterConfig.getInitParameter(HITS));
+        period = Integer.parseInt(filterConfig.getInitParameter(PERIOD));
+    }
+
+    /**
+     * Checks to see if the current session has exceeded the allowed number
+     * of requests in the specified time period. If the threshold has been
+     * exceeded, then a short error message is written to the output stream and
+     * no further processing is done on the request. Otherwise the request is
+     * processed as normal.
+     * @param request 
+     * @param response 
+     * @param chain 
+     * @throws IOException
+     * @throws ServletException
+     */
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+    {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpSession session = httpRequest.getSession(true);
+        
+        synchronized( session.getId().intern() ) {
+	        Stack<Date> times = ESAPI.httpUtilities().getSessionAttribute("times");
+	        if (times == null)
+	        {
+	            times = new Stack<Date>();
+	            times.push(new Date(0));
+	            session.setAttribute("times", times);
+	        }
+	        times.push(new Date());
+	        if (times.size() >= hits)
+	        {
+	            times.removeElementAt(0);
+	        }
+	        Date newest = times.get(times.size() - 1);
+	        Date oldest = times.get(0);
+	        long elapsed = newest.getTime() - oldest.getTime();        
+	        if (elapsed < period * 1000) // seconds
+	        {
+	            response.getWriter().println("Request rate too high");
+	            return;
+	        }
+        }        
+        chain.doFilter(request, response);
+    }
+
+    /**
+     * Called by the web container to indicate to a filter that it is being
+     * taken out of service. This method is only called once all threads within
+     * the filter's doFilter method have exited or after a timeout period has
+     * passed. After the web container calls this method, it will not call the
+     * doFilter method again on this instance of the filter.
+     */
+    public void destroy()
+    {
+        // finalize
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapper.java.svn-base b/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapper.java.svn-base
new file mode 100644
index 0000000..ba32981
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapper.java.svn-base
@@ -0,0 +1,136 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.StringUtilities;
+
+
+/**
+ * This filter wraps the incoming request and outgoing response and overrides
+ * many methods with safer versions. Many of the safer versions simply validate
+ * parts of the request or response for unwanted characters before allowing the
+ * call to complete. Some examples of attacks that use these
+ * vectors include request splitting, response splitting, and file download
+ * injection. Attackers use techniques like CRLF injection and null byte injection
+ * to confuse the parsing of requests and responses.
+ * <p/>
+ * <b>Example Configuration #1 (Default Configuration allows /WEB-INF):</b>
+ * <pre>
+ * <filter>
+ *    <filter-name>SecurityWrapperDefault</filter-name>
+ *    <filter-class>org.owasp.filters.SecurityWrapper</filter-class>
+ * </filter>
+ * </pre>
+ * <p/>
+ * <b>Example Configuration #2 (Allows /servlet)</b>
+ * <pre>
+ * <filter>
+ *    <filter-name>SecurityWrapperForServlet</filter-name>
+ *    <filter-class>org.owasp.filters.SecurityWrapper</filter-class>
+ *    <init-param>
+ *       <param-name>allowableResourceRoot</param-name>
+ *       <param-value>/servlet</param-value>
+ *    </init-param>
+ * </filter>
+ * </pre>
+ *
+ * @author  Chris Schmidt (chrisisbeef at gmail.com)
+ */
+public class SecurityWrapper implements Filter {
+
+    private final Logger logger = ESAPI.getLogger("SecurityWrapper");
+
+    /**
+     * This is the root path of what resources this filter will allow a RequestDispatcher to be dispatched to. This
+     * defaults to WEB-INF as best practice dictates that dispatched requests should be done to resources that are
+     * not browsable and everything behind WEB-INF is protected by the container. However, it is possible and sometimes
+     * required to dispatch requests to places outside of the WEB-INF path (such as to another servlet).
+     *
+     * See <a href="http://code.google.com/p/owasp-esapi-java/issues/detail?id=70">http://code.google.com/p/owasp-esapi-java/issues/detail?id=70</a>
+     * and <a href="https://lists.owasp.org/pipermail/owasp-esapi/2009-December/001672.html">https://lists.owasp.org/pipermail/owasp-esapi/2009-December/001672.html</a>
+     * for details.
+     */
+    private String allowableResourcesRoot = "WEB-INF";
+
+    /**
+     *
+     * @param request
+     * @param response
+     * @param chain
+     * @throws java.io.IOException
+     * @throws javax.servlet.ServletException
+     */
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        if (!(request instanceof HttpServletRequest)) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        try {
+            HttpServletRequest hrequest = (HttpServletRequest)request;
+            HttpServletResponse hresponse = (HttpServletResponse)response;
+
+            SecurityWrapperRequest secureRequest = new SecurityWrapperRequest(hrequest);
+            SecurityWrapperResponse secureResponse = new SecurityWrapperResponse(hresponse);
+
+            // Set the configuration on the wrapped request
+            secureRequest.setAllowableContentRoot(allowableResourcesRoot);
+
+            ESAPI.httpUtilities().setCurrentHTTP(secureRequest, secureResponse);
+
+            chain.doFilter(ESAPI.currentRequest(), ESAPI.currentResponse());
+        } catch (Exception e) {
+            logger.error( Logger.SECURITY_FAILURE, "Error in SecurityWrapper: " + e.getMessage(), e );
+            request.setAttribute("message", e.getMessage() );
+        } finally {
+            // VERY IMPORTANT
+            // clear out the ThreadLocal variables in the authenticator
+            // some containers could possibly reuse this thread without clearing the User
+            // Issue 70 - http://code.google.com/p/owasp-esapi-java/issues/detail?id=70
+            ESAPI.httpUtilities().clearCurrent();
+        }
+    }
+
+    /**
+     *
+     */
+    public void destroy() {
+		// no special action
+	}
+
+    /**
+     *
+     * @param filterConfig
+     * @throws javax.servlet.ServletException
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+		this.allowableResourcesRoot = StringUtilities.replaceNull( filterConfig.getInitParameter( "allowableResourcesRoot" ), allowableResourcesRoot );
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapperRequest.java.svn-base b/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapperRequest.java.svn-base
new file mode 100644
index 0000000..2777e42
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapperRequest.java.svn-base
@@ -0,0 +1,836 @@
+/**
+ * OWASP Enterprise Security API (ESAPI) This file is part of the Open Web
+ * Application Security Project (OWASP) Enterprise Security API (ESAPI) project.
+ * For details, please see <a
+ * href="http://www.owasp.org/index.php/ESAPI">http://
+ * www.owasp.org/index.php/ESAPI</a>. Copyright (c) 2007 - The OWASP Foundation
+ * The ESAPI is published by OWASP under the BSD license. You should read and
+ * accept the LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect
+ *         Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.errors.AccessControlException;
+
+/**
+ * This request wrapper simply overrides unsafe methods in the
+ * HttpServletRequest API with safe versions that return canonicalized data
+ * where possible. The wrapper returns a safe value when a validation error is
+ * detected, including stripped or empty strings.
+ */
+public class SecurityWrapperRequest extends HttpServletRequestWrapper implements HttpServletRequest {
+
+    private final Logger logger = ESAPI.getLogger("SecurityWrapperRequest");
+
+    private String allowableContentRoot = "WEB-INF";
+
+    /** 
+     * Construct a safe request that overrides the default request methods with
+     * safer versions.
+     * 
+     * @param request The {@code HttpServletRequest} we are wrapping.
+     */
+    public SecurityWrapperRequest(HttpServletRequest request) {
+    	super( request );
+    }
+
+    private HttpServletRequest getHttpServletRequest() {
+    	return (HttpServletRequest)super.getRequest();
+    }
+    
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The attribute name
+     * @return The attribute value
+     */
+    public Object getAttribute(String name) {
+        return getHttpServletRequest().getAttribute(name);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return An {@code Enumeration} of attribute names.
+     */
+    public Enumeration getAttributeNames() {
+        return getHttpServletRequest().getAttributeNames();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The authentication type
+     */
+    public String getAuthType() {
+        return getHttpServletRequest().getAuthType();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return  The character-encoding for this {@code HttpServletRequest}
+     */
+    public String getCharacterEncoding() {
+        return getHttpServletRequest().getCharacterEncoding();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The content-length for this {@code HttpServletRequest}
+     */
+    public int getContentLength() {
+        return getHttpServletRequest().getContentLength();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The content-type for this {@code HttpServletRequest}
+     */
+    public String getContentType() {
+        return getHttpServletRequest().getContentType();
+    }
+
+    /**
+     * Returns the context path from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return The context path for this {@code HttpServletRequest}
+     */
+    public String getContextPath() {
+        String path = getHttpServletRequest().getContextPath();
+
+		//Return empty String for the ROOT context
+		if (path == null || "".equals(path.trim())) return "";
+
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP context path: " + path, path, "HTTPContextPath", 150, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the array of Cookies from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return An array of {@code Cookie}s for this {@code HttpServletRequest}
+     */
+    public Cookie[] getCookies() {
+        Cookie[] cookies = getHttpServletRequest().getCookies();
+        if (cookies == null) return new Cookie[0];
+        
+        List<Cookie> newCookies = new ArrayList<Cookie>();
+        for (Cookie c : cookies) {
+            // build a new clean cookie
+            try {
+                // get data from original cookie
+                String name = ESAPI.validator().getValidInput("Cookie name: " + c.getName(), c.getName(), "HTTPCookieName", 150, true);
+                String value = ESAPI.validator().getValidInput("Cookie value: " + c.getValue(), c.getValue(), "HTTPCookieValue", 1000, true);
+                int maxAge = c.getMaxAge();
+                String domain = c.getDomain();
+                String path = c.getPath();
+				
+                Cookie n = new Cookie(name, value);
+                n.setMaxAge(maxAge);
+
+                if (domain != null) {
+                    n.setDomain(ESAPI.validator().getValidInput("Cookie domain: " + domain, domain, "HTTPHeaderValue", 200, false));
+                }
+                if (path != null) {
+                    n.setPath(ESAPI.validator().getValidInput("Cookie path: " + path, path, "HTTPHeaderValue", 200, false));
+                }
+                newCookies.add(n);
+            } catch (ValidationException e) {
+                logger.warning(Logger.SECURITY_FAILURE, "Skipping bad cookie: " + c.getName() + "=" + c.getValue(), e );
+            }
+        }
+        return newCookies.toArray(new Cookie[newCookies.size()]);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name Specifies the name of the HTTP request header; e.g.,
+     *             {@code If-Modified-Since}.
+     * @return a long value representing the date specified in the header
+     * expressed as the number of milliseconds since {@code January 1, 1970 GMT},
+     * or {@code -1} if the named header was not included with the request.
+     */
+    public long getDateHeader(String name) {
+        return getHttpServletRequest().getDateHeader(name);
+    }
+
+    /**
+     * Returns the named header from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @param name The name of an HTTP request header
+     * @return The specified header value is returned.
+     */
+    public String getHeader(String name) {
+        String value = getHttpServletRequest().getHeader(name);
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, true);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the enumeration of header names from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return An {@code Enumeration} of header names associated with this request.
+     */
+    public Enumeration getHeaderNames() {
+        Vector<String> v = new Vector<String>();
+        Enumeration en = getHttpServletRequest().getHeaderNames();
+        while (en.hasMoreElements()) {
+            try {
+                String name = (String) en.nextElement();
+                String clean = ESAPI.validator().getValidInput("HTTP header name: " + name, name, "HTTPHeaderName", 150, true);
+                v.add(clean);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return v.elements();
+    }
+
+    /**
+     * Returns the enumeration of headers from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The name of an HTTP request header.
+     * @return An {@code Enumeration} of headers from the request after
+     *         canonicalizing and filtering has been performed.
+     */
+    public Enumeration getHeaders(String name) {
+        Vector<String> v = new Vector<String>();
+        Enumeration en = getHttpServletRequest().getHeaders(name);
+        while (en.hasMoreElements()) {
+            try {
+                String value = (String) en.nextElement();
+                String clean = ESAPI.validator().getValidInput("HTTP header value (" + name + "): " + value, value, "HTTPHeaderValue", 150, true);
+                v.add(clean);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return v.elements();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required. Note that this
+     * input stream may contain attacks and the developer is responsible for
+     * canonicalizing, validating, and encoding any data from this stream.
+     * @return The {@code ServletInputStream} associated with this
+     *         {@code HttpServletRequest}.
+     * @throws IOException Thrown if an input exception is thrown, such as the
+     *         remote peer closing the connection.
+     */
+    public ServletInputStream getInputStream() throws IOException {
+        return getHttpServletRequest().getInputStream();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The name of an HTTP request header.
+     * @return Returns the value of the specified request header as an {@code int}.
+     */
+    public int getIntHeader(String name) {
+        return getHttpServletRequest().getIntHeader(name);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return A {@code String} containing the IP address on which the
+     *         request was received.
+     */
+    public String getLocalAddr() {
+        return getHttpServletRequest().getLocalAddr();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The preferred {@code Locale} for the client.
+     */
+    public Locale getLocale() {
+        return getHttpServletRequest().getLocale();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return An {@code Enumeration} of preferred {@code Locale}
+     *         objects for the client.
+     */
+    public Enumeration getLocales() {
+        return getHttpServletRequest().getLocales();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return A {@code String} containing the host name of the IP on which
+     *         the request was received.
+     */
+    public String getLocalName() {
+        return getHttpServletRequest().getLocalName();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the Internet Protocol (IP) port number of the interface
+     *         on which the request was received.
+     */
+    public int getLocalPort() {
+        return getHttpServletRequest().getLocalPort();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the name of the HTTP method with which this request was made.
+     */
+    public String getMethod() {
+        return getHttpServletRequest().getMethod();
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name) {
+        return getParameter(name, true);
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @param allowNull Whether null values are allowed
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name, boolean allowNull) {
+        return getParameter(name, allowNull, 2000, "HTTPParameterValue");
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @param allowNull Whether null values are allowed
+     * @param maxLength The maximum length allowed
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name, boolean allowNull, int maxLength) {
+        return getParameter(name,allowNull,maxLength,"HTTPParameterValue");
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @param allowNull Whether null values are allowed
+     * @param maxLength The maximum length allowed
+     * @param regexName The name of the regex mapped from ESAPI.properties
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name, boolean allowNull, int maxLength, String regexName) {
+        String orig = getHttpServletRequest().getParameter(name);
+        String clean = null;
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, orig, regexName, maxLength, allowNull);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the parameter map from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return A {@code Map} containing scrubbed parameter names / value pairs.
+     */
+    public Map getParameterMap() {
+        @SuppressWarnings({"unchecked"})
+        Map<String,String[]> map = getHttpServletRequest().getParameterMap();
+        Map<String,String[]> cleanMap = new HashMap<String,String[]>();
+        for (Object o : map.entrySet()) {
+            try {
+                Map.Entry e = (Map.Entry) o;
+                String name = (String) e.getKey();
+                String cleanName = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", 100, true);
+
+                String[] value = (String[]) e.getValue();
+                String[] cleanValues = new String[value.length];
+                for (int j = 0; j < value.length; j++) {
+                    String cleanValue = ESAPI.validator().getValidInput("HTTP parameter value: " + value[j], value[j], "HTTPParameterValue", 2000, true);
+                    cleanValues[j] = cleanValue;
+                }
+                cleanMap.put(cleanName, cleanValues);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return cleanMap;
+    }
+
+    /**
+     * Returns the enumeration of parameter names from the HttpServletRequest
+     * after canonicalizing and filtering out any dangerous characters.
+     * @return An {@code Enumeration} of properly "scrubbed" parameter names.
+     */
+    public Enumeration getParameterNames() {
+        Vector<String> v = new Vector<String>();
+        Enumeration en = getHttpServletRequest().getParameterNames();
+        while (en.hasMoreElements()) {
+            try {
+                String name = (String) en.nextElement();
+                String clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", 150, true);
+                v.add(clean);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return v.elements();
+    }
+
+    /**
+     * Returns the array of matching parameter values from the
+     * HttpServletRequest after canonicalizing and filtering out any dangerous
+     * characters.
+     * @param name The parameter name
+     * @return An array of matching "scrubbed" parameter values or
+     * <code>null</code> if the parameter does not exist.
+     */
+    public String[] getParameterValues(String name) {
+        String[] values = getHttpServletRequest().getParameterValues(name);
+        List<String> newValues;
+
+	if(values == null)
+		return null;
+        newValues = new ArrayList<String>();
+        for (String value : values) {
+            try {
+                String cleanValue = ESAPI.validator().getValidInput("HTTP parameter value: " + value, value, "HTTPParameterValue", 2000, true);
+                newValues.add(cleanValue);
+            } catch (ValidationException e) {
+                logger.warning(Logger.SECURITY_FAILURE, "Skipping bad parameter");
+            }
+        }
+        return newValues.toArray(new String[newValues.size()]);
+    }
+
+    /**
+     * Returns the path info from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return Returns any extra path information, appropriately scrubbed,
+     *         associated with the URL the client sent when it made this request.
+     */
+    public String getPathInfo() {
+        String path = getHttpServletRequest().getPathInfo();
+		if (path == null) return null;
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP path: " + path, path, "HTTPPath", 150, true);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns any extra path information, appropriate scrubbed,
+     *         after the servlet name but before the query string, and
+     *         translates it to a real path.
+     */
+    public String getPathTranslated() {
+        return getHttpServletRequest().getPathTranslated();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the name and version of the protocol the request uses in
+     *       the form protocol/majorVersion.minorVersion, for example, HTTP/1.1.
+     */
+    public String getProtocol() {
+        return getHttpServletRequest().getProtocol();
+    }
+
+    /**
+     * Returns the query string from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return The scrubbed query string is returned.
+     */
+    public String getQueryString() {
+        String query = getHttpServletRequest().getQueryString();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP query string: " + query, query, "HTTPQueryString", 2000, true);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required. Note that this
+     * reader may contain attacks and the developer is responsible for
+     * canonicalizing, validating, and encoding any data from this stream.
+     * @return aA {@code BufferedReader} containing the body of the request. 
+     * @throws IOException If an input error occurred while reading the request
+     *                     body (e.g., premature EOF).
+     */
+    public BufferedReader getReader() throws IOException {
+        return getHttpServletRequest().getReader();
+    }
+
+    // CHECKME: Should this be deprecated since ServletRequest.getRealPath(String)
+    //          is deprecated? Should use ServletContext.getRealPath(String) instead.
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param path A virtual path on a web or application server; e.g., "/index.htm".
+     * @return Returns a String containing the real path for a given virtual path.
+     * @deprecated in servlet spec 2.1. Use {@link javax.servlet.ServletContext#getRealPath(String)} instead.
+     */
+    @SuppressWarnings({"deprecation"})
+    @Deprecated
+    public String getRealPath(String path) {
+        return getHttpServletRequest().getRealPath(path);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the IP address of the client or last proxy that sent the request.
+     */
+    public String getRemoteAddr() {
+        return getHttpServletRequest().getRemoteAddr();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The remote host
+     */
+    public String getRemoteHost() {
+        return getHttpServletRequest().getRemoteHost();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The remote port
+     */
+    public int getRemotePort() {
+        return getHttpServletRequest().getRemotePort();
+    }
+
+    /**
+     * Returns the name of the ESAPI user associated with this getHttpServletRequest().
+     * @return Returns the fully qualified name of the client or the last proxy
+     *         that sent the request
+     */
+    public String getRemoteUser() {
+        return ESAPI.authenticator().getCurrentUser().getAccountName();
+    }
+
+    /**
+     * Checks to make sure the path to forward to is within the WEB-INF
+     * directory and then returns the dispatcher. Otherwise returns null.
+     * @param path The path to create a request dispatcher for
+     * @return A {@code RequestDispatcher} object that acts as a wrapper for the
+     *         resource at the specified path, or null if the servlet container
+     *         cannot return a {@code RequestDispatcher}.
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+        if (path.startsWith(allowableContentRoot)) {
+            return getHttpServletRequest().getRequestDispatcher(path);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the URI from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters. Code must be very careful not to
+     * depend on the value of a requested session id reported by the user.
+     * @return The requested Session ID
+     */
+    public String getRequestedSessionId() {
+        String id = getHttpServletRequest().getRequestedSessionId();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("Requested cookie: " + id, id, "HTTPJSESSIONID", 50, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the URI from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters.
+     * @return The current request URI
+     */
+    public String getRequestURI() {
+        String uri = getHttpServletRequest().getRequestURI();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP URI: " + uri, uri, "HTTPURI", 2000, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the URL from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters.
+     * @return The currect request URL
+     */
+    public StringBuffer getRequestURL() {
+        String url = getHttpServletRequest().getRequestURL().toString();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP URL: " + url, url, "HTTPURL", 2000, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return new StringBuffer(clean);
+    }
+
+    /**
+     * Returns the scheme from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters.
+     * @return The scheme of the current request
+     */
+    public String getScheme() {
+        String scheme = getHttpServletRequest().getScheme();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP scheme: " + scheme, scheme, "HTTPScheme", 10, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the server name (host header) from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return The local server name
+     */
+    public String getServerName() {
+        String name = getHttpServletRequest().getServerName();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP server name: " + name, name, "HTTPServerName", 100, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the server port (after the : in the host header) from the
+     * HttpServletRequest after parsing and checking the range 0-65536.
+     * @return The local server port
+     */
+	public int getServerPort() {
+		int port = getHttpServletRequest().getServerPort();
+		if ( port < 0 || port > 0xFFFF ) {
+			logger.warning( Logger.SECURITY_FAILURE, "HTTP server port out of range: " + port );
+			port = 0;
+		}
+		return port;
+	}
+ 	
+
+    /**
+     * Returns the server path from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return The servlet path
+     */
+    public String getServletPath() {
+        String path = getHttpServletRequest().getServletPath();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP servlet path: " + path, path, "HTTPServletPath", 100, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns a session, creating it if necessary, and sets the HttpOnly flag
+     * on the Session ID cookie.
+     * @return The current session
+     */
+    public HttpSession getSession() {
+		HttpSession session = getHttpServletRequest().getSession();
+
+		// send a new cookie header with HttpOnly on first and second responses
+	    if (ESAPI.securityConfiguration().getForceHttpOnlySession()) {
+	        if (session.getAttribute("HTTP_ONLY") == null) {
+				session.setAttribute("HTTP_ONLY", "set");
+				Cookie cookie = new Cookie(ESAPI.securityConfiguration().getHttpSessionIdName(), session.getId());
+				cookie.setPath( getHttpServletRequest().getContextPath() );
+				cookie.setMaxAge(-1); // session cookie
+	            HttpServletResponse response = ESAPI.currentResponse();
+	            if (response != null) {
+	                ESAPI.currentResponse().addCookie(cookie);
+	            }
+	        }
+	    }
+        return session;
+    }
+
+    /**
+     * Returns a session, creating it if necessary, and sets the HttpOnly flag
+     * on the Session ID cookie.
+     * @param create Create a new session if one doesn't exist
+     * @return The current session
+     */
+    public HttpSession getSession(boolean create) {
+        HttpSession session = getHttpServletRequest().getSession(create);
+        if (session == null) {
+            return null;
+        }
+
+        // send a new cookie header with HttpOnly on first and second responses
+        if (ESAPI.securityConfiguration().getForceHttpOnlySession()) {
+	        if (session.getAttribute("HTTP_ONLY") == null) {
+	            session.setAttribute("HTTP_ONLY", "set");
+	            Cookie cookie = new Cookie(ESAPI.securityConfiguration().getHttpSessionIdName(), session.getId());
+	            cookie.setMaxAge(-1); // session cookie
+	            cookie.setPath( getHttpServletRequest().getContextPath() );
+	            HttpServletResponse response = ESAPI.currentResponse();
+	            if (response != null) {
+	                ESAPI.currentResponse().addCookie(cookie);
+	            }
+	        }
+        }
+        return session;
+    }
+
+    /**
+     * Returns the ESAPI User associated with this getHttpServletRequest().
+     * @return The ESAPI User
+     */
+    public Principal getUserPrincipal() {
+        return ESAPI.authenticator().getCurrentUser();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return if requested session id is from a cookie
+     */
+    public boolean isRequestedSessionIdFromCookie() {
+        return getHttpServletRequest().isRequestedSessionIdFromCookie();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the requested session id is from the URL
+     * @deprecated in servlet spec 2.1. Use {@link #isRequestedSessionIdFromURL()} instead.
+     */
+    @SuppressWarnings({"deprecation"})
+    @Deprecated
+    public boolean isRequestedSessionIdFromUrl() {
+        return getHttpServletRequest().isRequestedSessionIdFromUrl();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the requested session id is from the URL
+     */
+    public boolean isRequestedSessionIdFromURL() {
+        return getHttpServletRequest().isRequestedSessionIdFromURL();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the requested session id is valid
+     */
+    public boolean isRequestedSessionIdValid() {
+        return getHttpServletRequest().isRequestedSessionIdValid();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the current request is secure
+     */
+    public boolean isSecure() {
+        try {
+            ESAPI.httpUtilities().assertSecureChannel();
+        } catch (AccessControlException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the ESAPI User associated with this request has the
+     * specified role.
+     * @param role The role to check
+     * @return Whether the current user is in the passed role
+     */
+    public boolean isUserInRole(String role) {
+        return ESAPI.authenticator().getCurrentUser().isInRole(role);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The attribute name
+     */
+    public void removeAttribute(String name) {
+        getHttpServletRequest().removeAttribute(name);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The attribute name
+     * @param o The attribute value
+     */
+    public void setAttribute(String name, Object o) {
+        getHttpServletRequest().setAttribute(name, o);
+    }
+
+    /**
+     * Sets the character encoding scheme to the ESAPI configured encoding scheme.
+     * @param enc The encoding scheme
+     * @throws UnsupportedEncodingException
+     */
+    public void setCharacterEncoding(String enc) throws UnsupportedEncodingException {
+        getHttpServletRequest().setCharacterEncoding(ESAPI.securityConfiguration().getCharacterEncoding());
+    }
+
+    public String getAllowableContentRoot() {
+        return allowableContentRoot;
+    }
+
+    public void setAllowableContentRoot(String allowableContentRoot) {
+        this.allowableContentRoot = allowableContentRoot.startsWith( "/" ) ? allowableContentRoot : "/" + allowableContentRoot;
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapperResponse.java.svn-base b/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapperResponse.java.svn-base
new file mode 100644
index 0000000..efc0013
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/text-base/SecurityWrapperResponse.java.svn-base
@@ -0,0 +1,513 @@
+/**
+ * OWASP Enterprise Security API (ESAPI) This file is part of the Open Web
+ * Application Security Project (OWASP) Enterprise Security API (ESAPI) project.
+ * For details, please see <a
+ * href="http://www.owasp.org/index.php/ESAPI">http://
+ * www.owasp.org/index.php/ESAPI</a>. Copyright (c) 2007 - The OWASP Foundation
+ * The ESAPI is published by OWASP under the BSD license. You should read and
+ * accept the LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect
+ *         Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * This response wrapper simply overrides unsafe methods in the
+ * HttpServletResponse API with safe versions.
+ */
+public class SecurityWrapperResponse extends HttpServletResponseWrapper implements HttpServletResponse {
+
+    private final Logger logger = ESAPI.getLogger("SecurityWrapperResponse");
+
+    // modes are "log", "skip", "sanitize", "throw"
+    // TODO: move this to SecurityConfiguration
+    private String mode = "log";
+
+    /**
+     * Construct a safe response that overrides the default response methods
+     * with safer versions. 
+     * 
+     * @param response
+     */
+    public SecurityWrapperResponse(HttpServletResponse response) {
+    	super( response );
+    }
+
+    /**
+     *
+     * @param response
+     * @param mode
+     */
+    public SecurityWrapperResponse(HttpServletResponse response, String mode) {
+    	super( response );
+        this.mode = mode;
+    }
+
+
+    private HttpServletResponse getHttpServletResponse() {
+    	return (HttpServletResponse)super.getResponse();
+    }
+
+    /**
+     * Add a cookie to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This method also sets
+     * the secure and HttpOnly flags on the cookie. This implementation uses a
+     * custom "set-cookie" header instead of using Java's cookie interface which
+     * doesn't allow the use of HttpOnly.
+     * @param cookie
+     */
+    public void addCookie(Cookie cookie) {
+        String name = cookie.getName();
+        String value = cookie.getValue();
+        int maxAge = cookie.getMaxAge();
+        String domain = cookie.getDomain();
+        String path = cookie.getPath();
+        boolean secure = cookie.getSecure();
+
+        // validate the name and value
+        ValidationErrorList errors = new ValidationErrorList();
+        String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors);
+        String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false, errors);
+
+        // if there are no errors, then just set a cookie header
+        if (errors.size() == 0) {
+            String header = createCookieHeader(name, value, maxAge, domain, path, secure);
+            this.addHeader("Set-Cookie", header);
+            return;
+        }
+
+        // if there was an error
+        if (mode.equals("skip")) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (skip mode). Skipping cookie and continuing.");
+            return;
+        }
+
+        // add the original cookie to the response and continue
+        if (mode.equals("log")) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (log mode). Adding unsafe cookie anyway and continuing.");
+            getHttpServletResponse().addCookie(cookie);
+            return;
+        }
+
+        // create a sanitized cookie header and continue
+        if (mode.equals("sanitize")) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (sanitize mode). Sanitizing cookie and continuing.");
+            String header = createCookieHeader(cookieName, cookieValue, maxAge, domain, path, secure);
+            this.addHeader("Set-Cookie", header);
+            return;
+        }
+
+        // throw an exception if necessary or add original cookie header
+        throw new IntrusionException("Security error", "Attempt to add unsafe data to cookie (throw mode)");
+    }
+
+    private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure) {
+        // create the special cookie header instead of creating a Java cookie
+        // Set-Cookie:<name>=<value>[; <name>=<value>][; expires=<date>][;
+        // domain=<domain_name>][; path=<some_path>][; secure][;HttpOnly
+        String header = name + "=" + value;
+        header += "; Max-Age=" + maxAge;
+        if (domain != null) {
+            header += "; Domain=" + domain;
+        }
+        if (path != null) {
+            header += "; Path=" + path;
+        }
+        if ( secure || ESAPI.securityConfiguration().getForceSecureCookies() ) {
+			header += "; Secure";
+        }
+        if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) {
+			header += "; HttpOnly";
+        }
+        return header;
+    }
+
+    /**
+     * Add a cookie to the response after ensuring that there are no encoded or
+     * illegal characters in the name.
+     * @param name 
+     * @param date
+     */
+    public void addDateHeader(String name, long date) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().addDateHeader(safeName, date);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e);
+        }
+    }
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This implementation
+     * follows the following recommendation: "A recipient MAY replace any linear
+     * white space with a single SP before interpreting the field value or
+     * forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     * @param name
+     * @param value
+     */
+    public void addHeader(String name, String value) {
+        try {
+            // TODO: make stripping a global config
+            String strippedName = StringUtilities.stripControls(name);
+            String strippedValue = StringUtilities.stripControls(value);
+            String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false);
+            getHttpServletResponse().setHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e);
+        }
+    }
+
+    /**
+     * Add an int header to the response after ensuring that there are no
+     * encoded or illegal characters in the name and name.
+     * @param name 
+     * @param value
+     */
+    public void addIntHeader(String name, int value) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().addIntHeader(safeName, value);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e);
+        }
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param name
+     * @return
+     */
+    public boolean containsHeader(String name) {
+        return getHttpServletResponse().containsHeader(name);
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID. The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     * @deprecated in servlet spec 2.1. Use
+     * {@link #encodeRedirectUrl(String)} instead.
+     */
+    @Deprecated
+    public String encodeRedirectUrl(String url) {
+        return url;
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     */
+    public String encodeRedirectURL(String url) {
+        return url;
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     * @deprecated in servlet spec 2.1. Use
+     * {@link #encodeURL(String)} instead.
+     */
+    @Deprecated
+    public String encodeUrl(String url) {
+        return url;
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     */
+    public String encodeURL(String url) {
+        return url;
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @throws IOException
+     */
+    public void flushBuffer() throws IOException {
+        getHttpServletResponse().flushBuffer();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public int getBufferSize() {
+        return getHttpServletResponse().getBufferSize();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public String getCharacterEncoding() {
+        return getHttpServletResponse().getCharacterEncoding();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public String getContentType() {
+        return getHttpServletResponse().getContentType();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public Locale getLocale() {
+        return getHttpServletResponse().getLocale();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return 
+     * @throws IOException
+     */
+    public ServletOutputStream getOutputStream() throws IOException {
+        return getHttpServletResponse().getOutputStream();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return 
+     * @throws IOException
+     */
+    public PrintWriter getWriter() throws IOException {
+        return getHttpServletResponse().getWriter();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public boolean isCommitted() {
+        return getHttpServletResponse().isCommitted();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     */
+    public void reset() {
+        getHttpServletResponse().reset();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     */
+    public void resetBuffer() {
+        getHttpServletResponse().resetBuffer();
+    }
+
+    /**
+     * Override the error code with a 200 in order to confound attackers using
+     * automated scanners.
+     * @param sc 
+     * @throws IOException
+     */
+    public void sendError(int sc) throws IOException {
+        getHttpServletResponse().sendError(HttpServletResponse.SC_OK, getHTTPMessage(sc));
+    }
+
+    /**
+     * Override the error code with a 200 in order to confound attackers using
+     * automated scanners. The message is canonicalized and filtered for
+     * dangerous characters.
+     * @param sc 
+     * @param msg
+     * @throws IOException
+     */
+    public void sendError(int sc, String msg) throws IOException {
+        getHttpServletResponse().sendError(HttpServletResponse.SC_OK, ESAPI.encoder().encodeForHTML(msg));
+    }
+
+    /**
+     * This method generates a redirect response that can only be used to
+     * redirect the browser to safe locations, as configured in the ESAPI
+     * security configuration. This method does not that redirect requests can
+     * be modified by attackers, so do not rely information contained within
+     * redirect requests, and do not include sensitive information in a
+     * redirect.
+     * @param location 
+     * @throws IOException
+     */
+    public void sendRedirect(String location) throws IOException {
+        if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location);
+            throw new IOException("Redirect failed");
+        }
+        getHttpServletResponse().sendRedirect(location);
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param size
+     */
+    public void setBufferSize(int size) {
+        getHttpServletResponse().setBufferSize(size);
+    }
+
+    /**
+     * Sets the character encoding to the ESAPI configured encoding.
+     * @param charset
+     */
+    public void setCharacterEncoding(String charset) {
+        getHttpServletResponse().setCharacterEncoding(ESAPI.securityConfiguration().getCharacterEncoding());
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param len
+     */
+    public void setContentLength(int len) {
+        getHttpServletResponse().setContentLength(len);
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param type
+     */
+    public void setContentType(String type) {
+        getHttpServletResponse().setContentType(type);
+    }
+
+    /**
+     * Add a date header to the response after ensuring that there are no
+     * encoded or illegal characters in the name.
+     * @param name 
+     * @param date
+     */
+    public void setDateHeader(String name, long date) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().setDateHeader(safeName, date);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e);
+        }
+    }
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and value. "A recipient MAY replace any
+     * linear white space with a single SP before interpreting the field value
+     * or forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     * @param name 
+     * @param value
+     */
+    public void setHeader(String name, String value) {
+        try {
+            String strippedName = StringUtilities.stripControls(name);
+            String strippedValue = StringUtilities.stripControls(value);
+            String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false);
+            getHttpServletResponse().setHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e);
+        }
+    }
+
+    /**
+     * Add an int header to the response after ensuring that there are no
+     * encoded or illegal characters in the name.
+     * @param name 
+     * @param value
+     */
+    public void setIntHeader(String name, int value) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().setIntHeader(safeName, value);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e);
+        }
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param loc
+     */
+    public void setLocale(Locale loc) {
+        // TODO investigate the character set issues here
+        getHttpServletResponse().setLocale(loc);
+    }
+
+    /**
+     * Override the status code with a 200 in order to confound attackers using
+     * automated scanners.
+     * @param sc
+     */
+    public void setStatus(int sc) {
+        getHttpServletResponse().setStatus(HttpServletResponse.SC_OK);
+    }
+
+    /**
+     * Override the status code with a 200 in order to confound attackers using
+     * automated scanners. The message is canonicalized and filtered for
+     * dangerous characters.
+     * @param sc 
+     * @param sm
+     * @deprecated In Servlet spec 2.1.
+     */
+    @Deprecated
+    public void setStatus(int sc, String sm) {
+        try {
+            // setStatus is deprecated so use sendError instead
+            sendError(HttpServletResponse.SC_OK, sm);
+        } catch (IOException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set response status failed", e);
+        }
+    }
+
+    /**
+     * returns a text message for the HTTP response code
+     */
+    private String getHTTPMessage(int sc) {
+        return "HTTP error code: " + sc;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/filters/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/filters/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..0efc5ab
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/.svn/text-base/package.html.svn-base
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains several filters that demonstrate ways of using the ESAPI security
+controls in front of your application. These filters are intended to be used as examples
+that you can customize for your particular application.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java
new file mode 100644
index 0000000..735d21e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java
@@ -0,0 +1,108 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author     Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created    February 6, 2009
+ */
+
+package org.owasp.esapi.filters;
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * The {@code ClickjackFilter} is discussed at
+ * {@link http://www.owasp.org/index.php/ClickjackFilter_for_Java_EE}.
+ * <pre>
+ *     <filter>
+ *            <filter-name>ClickjackFilterDeny</filter-name>
+ *            <filter-class>org.owasp.filters.ClickjackFilter</filter-class>
+ *            <init-param>
+ *                <param-name>mode</param-name>
+ *                 <param-value>DENY</param-value>
+ *             </init-param>
+ *         </filter>
+ *         
+ *         <filter>
+ *             <filter-name>ClickjackFilterSameOrigin</filter-name>
+ *             <filter-class>org.owasp.filters.ClickjackFilter</filter-class>
+ *             <init-param>
+ *                 <param-name>mode</param-name>
+ *                 <param-value>SAMEORIGIN</param-value>
+ *             </init-param>
+ *         </filter>
+ *        
+ *        <!--  use the Deny version to prevent anyone, including yourself, from framing the page -->
+ *        <filter-mapping> 
+ *            <filter-name>ClickjackFilterDeny</filter-name>
+ *            <url-pattern>/*</url-pattern>
+ *        </filter-mapping>
+ *         
+ *         <!-- use the SameOrigin version to allow your application to frame, but nobody else
+ *         <filter-mapping> 
+ *            <filter-name>ClickjackFilterSameOrigin</filter-name>
+ *             <url-pattern>/*</url-pattern>
+ *         </filter-mapping>
+ * </pre>
+ */
+public class ClickjackFilter implements Filter 
+{
+
+	private String mode = "DENY";
+
+	/**
+	 * Initialize "mode" parameter from web.xml. Valid values are "DENY" and "SAMEORIGIN". 
+	 * If you leave this parameter out, the default is to use the DENY mode.
+	 * 
+	 * @param filterConfig A filter configuration object used by a servlet container
+	 *                     to pass information to a filter during initialization. 
+	 */
+	public void init(FilterConfig filterConfig) {
+		String configMode = filterConfig.getInitParameter("mode");
+		if ( configMode != null && ( configMode.equals( "DENY" ) || configMode.equals( "SAMEORIGIN" ) ) ) {
+			mode = configMode;
+		}
+	}
+	
+	/**
+	 * Add X-FRAME-OPTIONS response header to tell IE8 (and any other browsers who
+	 * decide to implement) not to display this content in a frame. For details, please
+	 * refer to
+	 * {@link http://blogs.msdn.com/sdl/archive/2009/02/05/clickjacking-defense-in-ie8.aspx}.
+	 * 
+	 * @param request The request object.
+	 * @param response The response object.
+	 * @param chain Refers to the {@code FilterChain} object to pass control to the
+	 *              next {@code Filter}.
+	 * @throws IOException
+	 * @throws ServletException
+	 */
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+	{
+        HttpServletResponse res = (HttpServletResponse)response;
+        chain.doFilter(request, response);
+        res.addHeader("X-FRAME-OPTIONS", mode );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void destroy() {
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/filters/ESAPIFilter.java b/src/main/java/org/owasp/esapi/filters/ESAPIFilter.java
new file mode 100644
index 0000000..840d17b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/ESAPIFilter.java
@@ -0,0 +1,141 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AuthenticationException;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class ESAPIFilter implements Filter {
+
+	private final Logger logger = ESAPI.getLogger("ESAPIFilter");
+
+	private static final String[] obfuscate = { "password" };
+
+	/**
+	 * Called by the web container to indicate to a filter that it is being
+	 * placed into service. The servlet container calls the init method exactly
+	 * once after instantiating the filter. The init method must complete
+	 * successfully before the filter is asked to do any filtering work.
+	 * 
+	 * @param filterConfig
+	 *            configuration object
+	 */
+	public void init(FilterConfig filterConfig) {
+		String path = filterConfig.getInitParameter("resourceDirectory");
+		if ( path != null ) {
+			ESAPI.securityConfiguration().setResourceDirectory( path );
+		}
+	}
+
+	/**
+	 * The doFilter method of the Filter is called by the container each time a
+	 * request/response pair is passed through the chain due to a client request
+	 * for a resource at the end of the chain. The FilterChain passed in to this
+	 * method allows the Filter to pass on the request and response to the next
+	 * entity in the chain.
+	 * 
+	 * @param req
+	 *            Request object to be processed
+	 * @param resp
+	 *            Response object
+	 * @param chain
+	 *            current FilterChain
+	 * @exception IOException
+	 *                if any occurs
+	 */
+	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException {
+		HttpServletRequest request = (HttpServletRequest) req;
+		HttpServletResponse response = (HttpServletResponse) resp;
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		
+		try {
+			// figure out who the current user is
+			try {
+				ESAPI.authenticator().login(request, response);
+			} catch( AuthenticationException e ) {
+				ESAPI.authenticator().logout();
+				request.setAttribute("message", "Authentication failed");
+				RequestDispatcher dispatcher = request.getRequestDispatcher("WEB-INF/login.jsp");
+				dispatcher.forward(request, response);
+				return;
+			}
+
+			// log this request, obfuscating any parameter named password
+			ESAPI.httpUtilities().logHTTPRequest(request, logger, Arrays.asList(obfuscate));
+
+			// check access to this URL
+			if ( !ESAPI.accessController().isAuthorizedForURL(request.getRequestURI()) ) {
+				request.setAttribute("message", "Unauthorized" );
+				RequestDispatcher dispatcher = request.getRequestDispatcher("WEB-INF/index.jsp");
+				dispatcher.forward(request, response);
+				return;
+			}
+
+			// check for CSRF attacks
+			// ESAPI.httpUtilities().checkCSRFToken();
+			
+			// forward this request on to the web application
+			chain.doFilter(request, response);
+
+			// set up response with content type
+			ESAPI.httpUtilities().setContentType( response );
+
+            // set no-cache headers on every response
+            // only do this if the entire site should not be cached
+            // otherwise you should do this strategically in your controller or actions
+			ESAPI.httpUtilities().setNoCacheHeaders( response );
+            
+		} catch (Exception e) {
+			logger.error( Logger.SECURITY_FAILURE, "Error in ESAPI security filter: " + e.getMessage(), e );
+			request.setAttribute("message", e.getMessage() );
+			
+		} finally {
+			// VERY IMPORTANT
+			// clear out the ThreadLocal variables in the authenticator
+			// some containers could possibly reuse this thread without clearing the User
+			ESAPI.clearCurrent();
+		}
+	}
+
+	/**
+	 * Called by the web container to indicate to a filter that it is being
+	 * taken out of service. This method is only called once all threads within
+	 * the filter's doFilter method have exited or after a timeout period has
+	 * passed. After the web container calls this method, it will not call the
+	 * doFilter method again on this instance of the filter.
+	 */
+	public void destroy() {
+		// finalize
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java b/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java
new file mode 100644
index 0000000..0ca121e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java
@@ -0,0 +1,114 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import org.owasp.esapi.ESAPI;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Stack;
+
+/**
+ * A simple servlet filter that limits the request rate to a certain threshold of requests per second.
+ * The default rate is 5 hits in 10 seconds. This can be overridden in the web.xml file by adding
+ * parameters named "hits" and "period" with the desired values. When the rate is exceeded, a short
+ * string is written to the response output stream and the chain method is not invoked. Otherwise,
+ * processing proceeds as normal.
+ */
+public class RequestRateThrottleFilter implements Filter
+{
+
+    private int hits = 5;
+
+    private int period = 10;
+
+    private static final String HITS = "hits";
+
+    private static final String PERIOD = "period";
+
+    /**
+     * Called by the web container to indicate to a filter that it is being
+     * placed into service. The servlet container calls the init method exactly
+     * once after instantiating the filter. The init method must complete
+     * successfully before the filter is asked to do any filtering work.
+     * 
+     * @param filterConfig
+     *            configuration object
+     */
+    public void init(FilterConfig filterConfig)
+    {
+        hits = Integer.parseInt(filterConfig.getInitParameter(HITS));
+        period = Integer.parseInt(filterConfig.getInitParameter(PERIOD));
+    }
+
+    /**
+     * Checks to see if the current session has exceeded the allowed number
+     * of requests in the specified time period. If the threshold has been
+     * exceeded, then a short error message is written to the output stream and
+     * no further processing is done on the request. Otherwise the request is
+     * processed as normal.
+     * @param request 
+     * @param response 
+     * @param chain 
+     * @throws IOException
+     * @throws ServletException
+     */
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+    {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpSession session = httpRequest.getSession(true);
+        
+        synchronized( session.getId().intern() ) {
+	        Stack<Date> times = ESAPI.httpUtilities().getSessionAttribute("times");
+	        if (times == null)
+	        {
+	            times = new Stack<Date>();
+	            times.push(new Date(0));
+	            session.setAttribute("times", times);
+	        }
+	        times.push(new Date());
+	        if (times.size() >= hits)
+	        {
+	            times.removeElementAt(0);
+	        }
+	        Date newest = times.get(times.size() - 1);
+	        Date oldest = times.get(0);
+	        long elapsed = newest.getTime() - oldest.getTime();        
+	        if (elapsed < period * 1000) // seconds
+	        {
+	            response.getWriter().println("Request rate too high");
+	            return;
+	        }
+        }        
+        chain.doFilter(request, response);
+    }
+
+    /**
+     * Called by the web container to indicate to a filter that it is being
+     * taken out of service. This method is only called once all threads within
+     * the filter's doFilter method have exited or after a timeout period has
+     * passed. After the web container calls this method, it will not call the
+     * doFilter method again on this instance of the filter.
+     */
+    public void destroy()
+    {
+        // finalize
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapper.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapper.java
new file mode 100644
index 0000000..ba32981
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapper.java
@@ -0,0 +1,136 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.StringUtilities;
+
+
+/**
+ * This filter wraps the incoming request and outgoing response and overrides
+ * many methods with safer versions. Many of the safer versions simply validate
+ * parts of the request or response for unwanted characters before allowing the
+ * call to complete. Some examples of attacks that use these
+ * vectors include request splitting, response splitting, and file download
+ * injection. Attackers use techniques like CRLF injection and null byte injection
+ * to confuse the parsing of requests and responses.
+ * <p/>
+ * <b>Example Configuration #1 (Default Configuration allows /WEB-INF):</b>
+ * <pre>
+ * <filter>
+ *    <filter-name>SecurityWrapperDefault</filter-name>
+ *    <filter-class>org.owasp.filters.SecurityWrapper</filter-class>
+ * </filter>
+ * </pre>
+ * <p/>
+ * <b>Example Configuration #2 (Allows /servlet)</b>
+ * <pre>
+ * <filter>
+ *    <filter-name>SecurityWrapperForServlet</filter-name>
+ *    <filter-class>org.owasp.filters.SecurityWrapper</filter-class>
+ *    <init-param>
+ *       <param-name>allowableResourceRoot</param-name>
+ *       <param-value>/servlet</param-value>
+ *    </init-param>
+ * </filter>
+ * </pre>
+ *
+ * @author  Chris Schmidt (chrisisbeef at gmail.com)
+ */
+public class SecurityWrapper implements Filter {
+
+    private final Logger logger = ESAPI.getLogger("SecurityWrapper");
+
+    /**
+     * This is the root path of what resources this filter will allow a RequestDispatcher to be dispatched to. This
+     * defaults to WEB-INF as best practice dictates that dispatched requests should be done to resources that are
+     * not browsable and everything behind WEB-INF is protected by the container. However, it is possible and sometimes
+     * required to dispatch requests to places outside of the WEB-INF path (such as to another servlet).
+     *
+     * See <a href="http://code.google.com/p/owasp-esapi-java/issues/detail?id=70">http://code.google.com/p/owasp-esapi-java/issues/detail?id=70</a>
+     * and <a href="https://lists.owasp.org/pipermail/owasp-esapi/2009-December/001672.html">https://lists.owasp.org/pipermail/owasp-esapi/2009-December/001672.html</a>
+     * for details.
+     */
+    private String allowableResourcesRoot = "WEB-INF";
+
+    /**
+     *
+     * @param request
+     * @param response
+     * @param chain
+     * @throws java.io.IOException
+     * @throws javax.servlet.ServletException
+     */
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        if (!(request instanceof HttpServletRequest)) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        try {
+            HttpServletRequest hrequest = (HttpServletRequest)request;
+            HttpServletResponse hresponse = (HttpServletResponse)response;
+
+            SecurityWrapperRequest secureRequest = new SecurityWrapperRequest(hrequest);
+            SecurityWrapperResponse secureResponse = new SecurityWrapperResponse(hresponse);
+
+            // Set the configuration on the wrapped request
+            secureRequest.setAllowableContentRoot(allowableResourcesRoot);
+
+            ESAPI.httpUtilities().setCurrentHTTP(secureRequest, secureResponse);
+
+            chain.doFilter(ESAPI.currentRequest(), ESAPI.currentResponse());
+        } catch (Exception e) {
+            logger.error( Logger.SECURITY_FAILURE, "Error in SecurityWrapper: " + e.getMessage(), e );
+            request.setAttribute("message", e.getMessage() );
+        } finally {
+            // VERY IMPORTANT
+            // clear out the ThreadLocal variables in the authenticator
+            // some containers could possibly reuse this thread without clearing the User
+            // Issue 70 - http://code.google.com/p/owasp-esapi-java/issues/detail?id=70
+            ESAPI.httpUtilities().clearCurrent();
+        }
+    }
+
+    /**
+     *
+     */
+    public void destroy() {
+		// no special action
+	}
+
+    /**
+     *
+     * @param filterConfig
+     * @throws javax.servlet.ServletException
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+		this.allowableResourcesRoot = StringUtilities.replaceNull( filterConfig.getInitParameter( "allowableResourcesRoot" ), allowableResourcesRoot );
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java
new file mode 100644
index 0000000..2777e42
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java
@@ -0,0 +1,836 @@
+/**
+ * OWASP Enterprise Security API (ESAPI) This file is part of the Open Web
+ * Application Security Project (OWASP) Enterprise Security API (ESAPI) project.
+ * For details, please see <a
+ * href="http://www.owasp.org/index.php/ESAPI">http://
+ * www.owasp.org/index.php/ESAPI</a>. Copyright (c) 2007 - The OWASP Foundation
+ * The ESAPI is published by OWASP under the BSD license. You should read and
+ * accept the LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect
+ *         Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.errors.AccessControlException;
+
+/**
+ * This request wrapper simply overrides unsafe methods in the
+ * HttpServletRequest API with safe versions that return canonicalized data
+ * where possible. The wrapper returns a safe value when a validation error is
+ * detected, including stripped or empty strings.
+ */
+public class SecurityWrapperRequest extends HttpServletRequestWrapper implements HttpServletRequest {
+
+    private final Logger logger = ESAPI.getLogger("SecurityWrapperRequest");
+
+    private String allowableContentRoot = "WEB-INF";
+
+    /** 
+     * Construct a safe request that overrides the default request methods with
+     * safer versions.
+     * 
+     * @param request The {@code HttpServletRequest} we are wrapping.
+     */
+    public SecurityWrapperRequest(HttpServletRequest request) {
+    	super( request );
+    }
+
+    private HttpServletRequest getHttpServletRequest() {
+    	return (HttpServletRequest)super.getRequest();
+    }
+    
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The attribute name
+     * @return The attribute value
+     */
+    public Object getAttribute(String name) {
+        return getHttpServletRequest().getAttribute(name);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return An {@code Enumeration} of attribute names.
+     */
+    public Enumeration getAttributeNames() {
+        return getHttpServletRequest().getAttributeNames();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The authentication type
+     */
+    public String getAuthType() {
+        return getHttpServletRequest().getAuthType();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return  The character-encoding for this {@code HttpServletRequest}
+     */
+    public String getCharacterEncoding() {
+        return getHttpServletRequest().getCharacterEncoding();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The content-length for this {@code HttpServletRequest}
+     */
+    public int getContentLength() {
+        return getHttpServletRequest().getContentLength();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The content-type for this {@code HttpServletRequest}
+     */
+    public String getContentType() {
+        return getHttpServletRequest().getContentType();
+    }
+
+    /**
+     * Returns the context path from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return The context path for this {@code HttpServletRequest}
+     */
+    public String getContextPath() {
+        String path = getHttpServletRequest().getContextPath();
+
+		//Return empty String for the ROOT context
+		if (path == null || "".equals(path.trim())) return "";
+
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP context path: " + path, path, "HTTPContextPath", 150, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the array of Cookies from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return An array of {@code Cookie}s for this {@code HttpServletRequest}
+     */
+    public Cookie[] getCookies() {
+        Cookie[] cookies = getHttpServletRequest().getCookies();
+        if (cookies == null) return new Cookie[0];
+        
+        List<Cookie> newCookies = new ArrayList<Cookie>();
+        for (Cookie c : cookies) {
+            // build a new clean cookie
+            try {
+                // get data from original cookie
+                String name = ESAPI.validator().getValidInput("Cookie name: " + c.getName(), c.getName(), "HTTPCookieName", 150, true);
+                String value = ESAPI.validator().getValidInput("Cookie value: " + c.getValue(), c.getValue(), "HTTPCookieValue", 1000, true);
+                int maxAge = c.getMaxAge();
+                String domain = c.getDomain();
+                String path = c.getPath();
+				
+                Cookie n = new Cookie(name, value);
+                n.setMaxAge(maxAge);
+
+                if (domain != null) {
+                    n.setDomain(ESAPI.validator().getValidInput("Cookie domain: " + domain, domain, "HTTPHeaderValue", 200, false));
+                }
+                if (path != null) {
+                    n.setPath(ESAPI.validator().getValidInput("Cookie path: " + path, path, "HTTPHeaderValue", 200, false));
+                }
+                newCookies.add(n);
+            } catch (ValidationException e) {
+                logger.warning(Logger.SECURITY_FAILURE, "Skipping bad cookie: " + c.getName() + "=" + c.getValue(), e );
+            }
+        }
+        return newCookies.toArray(new Cookie[newCookies.size()]);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name Specifies the name of the HTTP request header; e.g.,
+     *             {@code If-Modified-Since}.
+     * @return a long value representing the date specified in the header
+     * expressed as the number of milliseconds since {@code January 1, 1970 GMT},
+     * or {@code -1} if the named header was not included with the request.
+     */
+    public long getDateHeader(String name) {
+        return getHttpServletRequest().getDateHeader(name);
+    }
+
+    /**
+     * Returns the named header from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @param name The name of an HTTP request header
+     * @return The specified header value is returned.
+     */
+    public String getHeader(String name) {
+        String value = getHttpServletRequest().getHeader(name);
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, true);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the enumeration of header names from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return An {@code Enumeration} of header names associated with this request.
+     */
+    public Enumeration getHeaderNames() {
+        Vector<String> v = new Vector<String>();
+        Enumeration en = getHttpServletRequest().getHeaderNames();
+        while (en.hasMoreElements()) {
+            try {
+                String name = (String) en.nextElement();
+                String clean = ESAPI.validator().getValidInput("HTTP header name: " + name, name, "HTTPHeaderName", 150, true);
+                v.add(clean);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return v.elements();
+    }
+
+    /**
+     * Returns the enumeration of headers from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The name of an HTTP request header.
+     * @return An {@code Enumeration} of headers from the request after
+     *         canonicalizing and filtering has been performed.
+     */
+    public Enumeration getHeaders(String name) {
+        Vector<String> v = new Vector<String>();
+        Enumeration en = getHttpServletRequest().getHeaders(name);
+        while (en.hasMoreElements()) {
+            try {
+                String value = (String) en.nextElement();
+                String clean = ESAPI.validator().getValidInput("HTTP header value (" + name + "): " + value, value, "HTTPHeaderValue", 150, true);
+                v.add(clean);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return v.elements();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required. Note that this
+     * input stream may contain attacks and the developer is responsible for
+     * canonicalizing, validating, and encoding any data from this stream.
+     * @return The {@code ServletInputStream} associated with this
+     *         {@code HttpServletRequest}.
+     * @throws IOException Thrown if an input exception is thrown, such as the
+     *         remote peer closing the connection.
+     */
+    public ServletInputStream getInputStream() throws IOException {
+        return getHttpServletRequest().getInputStream();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The name of an HTTP request header.
+     * @return Returns the value of the specified request header as an {@code int}.
+     */
+    public int getIntHeader(String name) {
+        return getHttpServletRequest().getIntHeader(name);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return A {@code String} containing the IP address on which the
+     *         request was received.
+     */
+    public String getLocalAddr() {
+        return getHttpServletRequest().getLocalAddr();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The preferred {@code Locale} for the client.
+     */
+    public Locale getLocale() {
+        return getHttpServletRequest().getLocale();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return An {@code Enumeration} of preferred {@code Locale}
+     *         objects for the client.
+     */
+    public Enumeration getLocales() {
+        return getHttpServletRequest().getLocales();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return A {@code String} containing the host name of the IP on which
+     *         the request was received.
+     */
+    public String getLocalName() {
+        return getHttpServletRequest().getLocalName();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the Internet Protocol (IP) port number of the interface
+     *         on which the request was received.
+     */
+    public int getLocalPort() {
+        return getHttpServletRequest().getLocalPort();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the name of the HTTP method with which this request was made.
+     */
+    public String getMethod() {
+        return getHttpServletRequest().getMethod();
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name) {
+        return getParameter(name, true);
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @param allowNull Whether null values are allowed
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name, boolean allowNull) {
+        return getParameter(name, allowNull, 2000, "HTTPParameterValue");
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @param allowNull Whether null values are allowed
+     * @param maxLength The maximum length allowed
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name, boolean allowNull, int maxLength) {
+        return getParameter(name,allowNull,maxLength,"HTTPParameterValue");
+    }
+
+    /**
+     * Returns the named parameter from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @param name The parameter name for the request
+     * @param allowNull Whether null values are allowed
+     * @param maxLength The maximum length allowed
+     * @param regexName The name of the regex mapped from ESAPI.properties
+     * @return The "scrubbed" parameter value.
+     */
+    public String getParameter(String name, boolean allowNull, int maxLength, String regexName) {
+        String orig = getHttpServletRequest().getParameter(name);
+        String clean = null;
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, orig, regexName, maxLength, allowNull);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the parameter map from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return A {@code Map} containing scrubbed parameter names / value pairs.
+     */
+    public Map getParameterMap() {
+        @SuppressWarnings({"unchecked"})
+        Map<String,String[]> map = getHttpServletRequest().getParameterMap();
+        Map<String,String[]> cleanMap = new HashMap<String,String[]>();
+        for (Object o : map.entrySet()) {
+            try {
+                Map.Entry e = (Map.Entry) o;
+                String name = (String) e.getKey();
+                String cleanName = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", 100, true);
+
+                String[] value = (String[]) e.getValue();
+                String[] cleanValues = new String[value.length];
+                for (int j = 0; j < value.length; j++) {
+                    String cleanValue = ESAPI.validator().getValidInput("HTTP parameter value: " + value[j], value[j], "HTTPParameterValue", 2000, true);
+                    cleanValues[j] = cleanValue;
+                }
+                cleanMap.put(cleanName, cleanValues);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return cleanMap;
+    }
+
+    /**
+     * Returns the enumeration of parameter names from the HttpServletRequest
+     * after canonicalizing and filtering out any dangerous characters.
+     * @return An {@code Enumeration} of properly "scrubbed" parameter names.
+     */
+    public Enumeration getParameterNames() {
+        Vector<String> v = new Vector<String>();
+        Enumeration en = getHttpServletRequest().getParameterNames();
+        while (en.hasMoreElements()) {
+            try {
+                String name = (String) en.nextElement();
+                String clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", 150, true);
+                v.add(clean);
+            } catch (ValidationException e) {
+                // already logged
+            }
+        }
+        return v.elements();
+    }
+
+    /**
+     * Returns the array of matching parameter values from the
+     * HttpServletRequest after canonicalizing and filtering out any dangerous
+     * characters.
+     * @param name The parameter name
+     * @return An array of matching "scrubbed" parameter values or
+     * <code>null</code> if the parameter does not exist.
+     */
+    public String[] getParameterValues(String name) {
+        String[] values = getHttpServletRequest().getParameterValues(name);
+        List<String> newValues;
+
+	if(values == null)
+		return null;
+        newValues = new ArrayList<String>();
+        for (String value : values) {
+            try {
+                String cleanValue = ESAPI.validator().getValidInput("HTTP parameter value: " + value, value, "HTTPParameterValue", 2000, true);
+                newValues.add(cleanValue);
+            } catch (ValidationException e) {
+                logger.warning(Logger.SECURITY_FAILURE, "Skipping bad parameter");
+            }
+        }
+        return newValues.toArray(new String[newValues.size()]);
+    }
+
+    /**
+     * Returns the path info from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return Returns any extra path information, appropriately scrubbed,
+     *         associated with the URL the client sent when it made this request.
+     */
+    public String getPathInfo() {
+        String path = getHttpServletRequest().getPathInfo();
+		if (path == null) return null;
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP path: " + path, path, "HTTPPath", 150, true);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns any extra path information, appropriate scrubbed,
+     *         after the servlet name but before the query string, and
+     *         translates it to a real path.
+     */
+    public String getPathTranslated() {
+        return getHttpServletRequest().getPathTranslated();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the name and version of the protocol the request uses in
+     *       the form protocol/majorVersion.minorVersion, for example, HTTP/1.1.
+     */
+    public String getProtocol() {
+        return getHttpServletRequest().getProtocol();
+    }
+
+    /**
+     * Returns the query string from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return The scrubbed query string is returned.
+     */
+    public String getQueryString() {
+        String query = getHttpServletRequest().getQueryString();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP query string: " + query, query, "HTTPQueryString", 2000, true);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required. Note that this
+     * reader may contain attacks and the developer is responsible for
+     * canonicalizing, validating, and encoding any data from this stream.
+     * @return aA {@code BufferedReader} containing the body of the request. 
+     * @throws IOException If an input error occurred while reading the request
+     *                     body (e.g., premature EOF).
+     */
+    public BufferedReader getReader() throws IOException {
+        return getHttpServletRequest().getReader();
+    }
+
+    // CHECKME: Should this be deprecated since ServletRequest.getRealPath(String)
+    //          is deprecated? Should use ServletContext.getRealPath(String) instead.
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param path A virtual path on a web or application server; e.g., "/index.htm".
+     * @return Returns a String containing the real path for a given virtual path.
+     * @deprecated in servlet spec 2.1. Use {@link javax.servlet.ServletContext#getRealPath(String)} instead.
+     */
+    @SuppressWarnings({"deprecation"})
+    @Deprecated
+    public String getRealPath(String path) {
+        return getHttpServletRequest().getRealPath(path);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Returns the IP address of the client or last proxy that sent the request.
+     */
+    public String getRemoteAddr() {
+        return getHttpServletRequest().getRemoteAddr();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The remote host
+     */
+    public String getRemoteHost() {
+        return getHttpServletRequest().getRemoteHost();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return The remote port
+     */
+    public int getRemotePort() {
+        return getHttpServletRequest().getRemotePort();
+    }
+
+    /**
+     * Returns the name of the ESAPI user associated with this getHttpServletRequest().
+     * @return Returns the fully qualified name of the client or the last proxy
+     *         that sent the request
+     */
+    public String getRemoteUser() {
+        return ESAPI.authenticator().getCurrentUser().getAccountName();
+    }
+
+    /**
+     * Checks to make sure the path to forward to is within the WEB-INF
+     * directory and then returns the dispatcher. Otherwise returns null.
+     * @param path The path to create a request dispatcher for
+     * @return A {@code RequestDispatcher} object that acts as a wrapper for the
+     *         resource at the specified path, or null if the servlet container
+     *         cannot return a {@code RequestDispatcher}.
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+        if (path.startsWith(allowableContentRoot)) {
+            return getHttpServletRequest().getRequestDispatcher(path);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the URI from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters. Code must be very careful not to
+     * depend on the value of a requested session id reported by the user.
+     * @return The requested Session ID
+     */
+    public String getRequestedSessionId() {
+        String id = getHttpServletRequest().getRequestedSessionId();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("Requested cookie: " + id, id, "HTTPJSESSIONID", 50, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the URI from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters.
+     * @return The current request URI
+     */
+    public String getRequestURI() {
+        String uri = getHttpServletRequest().getRequestURI();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP URI: " + uri, uri, "HTTPURI", 2000, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the URL from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters.
+     * @return The currect request URL
+     */
+    public StringBuffer getRequestURL() {
+        String url = getHttpServletRequest().getRequestURL().toString();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP URL: " + url, url, "HTTPURL", 2000, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return new StringBuffer(clean);
+    }
+
+    /**
+     * Returns the scheme from the HttpServletRequest after canonicalizing and
+     * filtering out any dangerous characters.
+     * @return The scheme of the current request
+     */
+    public String getScheme() {
+        String scheme = getHttpServletRequest().getScheme();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP scheme: " + scheme, scheme, "HTTPScheme", 10, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the server name (host header) from the HttpServletRequest after
+     * canonicalizing and filtering out any dangerous characters.
+     * @return The local server name
+     */
+    public String getServerName() {
+        String name = getHttpServletRequest().getServerName();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP server name: " + name, name, "HTTPServerName", 100, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns the server port (after the : in the host header) from the
+     * HttpServletRequest after parsing and checking the range 0-65536.
+     * @return The local server port
+     */
+	public int getServerPort() {
+		int port = getHttpServletRequest().getServerPort();
+		if ( port < 0 || port > 0xFFFF ) {
+			logger.warning( Logger.SECURITY_FAILURE, "HTTP server port out of range: " + port );
+			port = 0;
+		}
+		return port;
+	}
+ 	
+
+    /**
+     * Returns the server path from the HttpServletRequest after canonicalizing
+     * and filtering out any dangerous characters.
+     * @return The servlet path
+     */
+    public String getServletPath() {
+        String path = getHttpServletRequest().getServletPath();
+        String clean = "";
+        try {
+            clean = ESAPI.validator().getValidInput("HTTP servlet path: " + path, path, "HTTPServletPath", 100, false);
+        } catch (ValidationException e) {
+            // already logged
+        }
+        return clean;
+    }
+
+    /**
+     * Returns a session, creating it if necessary, and sets the HttpOnly flag
+     * on the Session ID cookie.
+     * @return The current session
+     */
+    public HttpSession getSession() {
+		HttpSession session = getHttpServletRequest().getSession();
+
+		// send a new cookie header with HttpOnly on first and second responses
+	    if (ESAPI.securityConfiguration().getForceHttpOnlySession()) {
+	        if (session.getAttribute("HTTP_ONLY") == null) {
+				session.setAttribute("HTTP_ONLY", "set");
+				Cookie cookie = new Cookie(ESAPI.securityConfiguration().getHttpSessionIdName(), session.getId());
+				cookie.setPath( getHttpServletRequest().getContextPath() );
+				cookie.setMaxAge(-1); // session cookie
+	            HttpServletResponse response = ESAPI.currentResponse();
+	            if (response != null) {
+	                ESAPI.currentResponse().addCookie(cookie);
+	            }
+	        }
+	    }
+        return session;
+    }
+
+    /**
+     * Returns a session, creating it if necessary, and sets the HttpOnly flag
+     * on the Session ID cookie.
+     * @param create Create a new session if one doesn't exist
+     * @return The current session
+     */
+    public HttpSession getSession(boolean create) {
+        HttpSession session = getHttpServletRequest().getSession(create);
+        if (session == null) {
+            return null;
+        }
+
+        // send a new cookie header with HttpOnly on first and second responses
+        if (ESAPI.securityConfiguration().getForceHttpOnlySession()) {
+	        if (session.getAttribute("HTTP_ONLY") == null) {
+	            session.setAttribute("HTTP_ONLY", "set");
+	            Cookie cookie = new Cookie(ESAPI.securityConfiguration().getHttpSessionIdName(), session.getId());
+	            cookie.setMaxAge(-1); // session cookie
+	            cookie.setPath( getHttpServletRequest().getContextPath() );
+	            HttpServletResponse response = ESAPI.currentResponse();
+	            if (response != null) {
+	                ESAPI.currentResponse().addCookie(cookie);
+	            }
+	        }
+        }
+        return session;
+    }
+
+    /**
+     * Returns the ESAPI User associated with this getHttpServletRequest().
+     * @return The ESAPI User
+     */
+    public Principal getUserPrincipal() {
+        return ESAPI.authenticator().getCurrentUser();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return if requested session id is from a cookie
+     */
+    public boolean isRequestedSessionIdFromCookie() {
+        return getHttpServletRequest().isRequestedSessionIdFromCookie();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the requested session id is from the URL
+     * @deprecated in servlet spec 2.1. Use {@link #isRequestedSessionIdFromURL()} instead.
+     */
+    @SuppressWarnings({"deprecation"})
+    @Deprecated
+    public boolean isRequestedSessionIdFromUrl() {
+        return getHttpServletRequest().isRequestedSessionIdFromUrl();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the requested session id is from the URL
+     */
+    public boolean isRequestedSessionIdFromURL() {
+        return getHttpServletRequest().isRequestedSessionIdFromURL();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the requested session id is valid
+     */
+    public boolean isRequestedSessionIdValid() {
+        return getHttpServletRequest().isRequestedSessionIdValid();
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @return Whether the current request is secure
+     */
+    public boolean isSecure() {
+        try {
+            ESAPI.httpUtilities().assertSecureChannel();
+        } catch (AccessControlException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the ESAPI User associated with this request has the
+     * specified role.
+     * @param role The role to check
+     * @return Whether the current user is in the passed role
+     */
+    public boolean isUserInRole(String role) {
+        return ESAPI.authenticator().getCurrentUser().isInRole(role);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The attribute name
+     */
+    public void removeAttribute(String name) {
+        getHttpServletRequest().removeAttribute(name);
+    }
+
+    /**
+     * Same as HttpServletRequest, no security changes required.
+     * @param name The attribute name
+     * @param o The attribute value
+     */
+    public void setAttribute(String name, Object o) {
+        getHttpServletRequest().setAttribute(name, o);
+    }
+
+    /**
+     * Sets the character encoding scheme to the ESAPI configured encoding scheme.
+     * @param enc The encoding scheme
+     * @throws UnsupportedEncodingException
+     */
+    public void setCharacterEncoding(String enc) throws UnsupportedEncodingException {
+        getHttpServletRequest().setCharacterEncoding(ESAPI.securityConfiguration().getCharacterEncoding());
+    }
+
+    public String getAllowableContentRoot() {
+        return allowableContentRoot;
+    }
+
+    public void setAllowableContentRoot(String allowableContentRoot) {
+        this.allowableContentRoot = allowableContentRoot.startsWith( "/" ) ? allowableContentRoot : "/" + allowableContentRoot;
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java
new file mode 100644
index 0000000..efc0013
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java
@@ -0,0 +1,513 @@
+/**
+ * OWASP Enterprise Security API (ESAPI) This file is part of the Open Web
+ * Application Security Project (OWASP) Enterprise Security API (ESAPI) project.
+ * For details, please see <a
+ * href="http://www.owasp.org/index.php/ESAPI">http://
+ * www.owasp.org/index.php/ESAPI</a>. Copyright (c) 2007 - The OWASP Foundation
+ * The ESAPI is published by OWASP under the BSD license. You should read and
+ * accept the LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect
+ *         Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * This response wrapper simply overrides unsafe methods in the
+ * HttpServletResponse API with safe versions.
+ */
+public class SecurityWrapperResponse extends HttpServletResponseWrapper implements HttpServletResponse {
+
+    private final Logger logger = ESAPI.getLogger("SecurityWrapperResponse");
+
+    // modes are "log", "skip", "sanitize", "throw"
+    // TODO: move this to SecurityConfiguration
+    private String mode = "log";
+
+    /**
+     * Construct a safe response that overrides the default response methods
+     * with safer versions. 
+     * 
+     * @param response
+     */
+    public SecurityWrapperResponse(HttpServletResponse response) {
+    	super( response );
+    }
+
+    /**
+     *
+     * @param response
+     * @param mode
+     */
+    public SecurityWrapperResponse(HttpServletResponse response, String mode) {
+    	super( response );
+        this.mode = mode;
+    }
+
+
+    private HttpServletResponse getHttpServletResponse() {
+    	return (HttpServletResponse)super.getResponse();
+    }
+
+    /**
+     * Add a cookie to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This method also sets
+     * the secure and HttpOnly flags on the cookie. This implementation uses a
+     * custom "set-cookie" header instead of using Java's cookie interface which
+     * doesn't allow the use of HttpOnly.
+     * @param cookie
+     */
+    public void addCookie(Cookie cookie) {
+        String name = cookie.getName();
+        String value = cookie.getValue();
+        int maxAge = cookie.getMaxAge();
+        String domain = cookie.getDomain();
+        String path = cookie.getPath();
+        boolean secure = cookie.getSecure();
+
+        // validate the name and value
+        ValidationErrorList errors = new ValidationErrorList();
+        String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors);
+        String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false, errors);
+
+        // if there are no errors, then just set a cookie header
+        if (errors.size() == 0) {
+            String header = createCookieHeader(name, value, maxAge, domain, path, secure);
+            this.addHeader("Set-Cookie", header);
+            return;
+        }
+
+        // if there was an error
+        if (mode.equals("skip")) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (skip mode). Skipping cookie and continuing.");
+            return;
+        }
+
+        // add the original cookie to the response and continue
+        if (mode.equals("log")) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (log mode). Adding unsafe cookie anyway and continuing.");
+            getHttpServletResponse().addCookie(cookie);
+            return;
+        }
+
+        // create a sanitized cookie header and continue
+        if (mode.equals("sanitize")) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (sanitize mode). Sanitizing cookie and continuing.");
+            String header = createCookieHeader(cookieName, cookieValue, maxAge, domain, path, secure);
+            this.addHeader("Set-Cookie", header);
+            return;
+        }
+
+        // throw an exception if necessary or add original cookie header
+        throw new IntrusionException("Security error", "Attempt to add unsafe data to cookie (throw mode)");
+    }
+
+    private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure) {
+        // create the special cookie header instead of creating a Java cookie
+        // Set-Cookie:<name>=<value>[; <name>=<value>][; expires=<date>][;
+        // domain=<domain_name>][; path=<some_path>][; secure][;HttpOnly
+        String header = name + "=" + value;
+        header += "; Max-Age=" + maxAge;
+        if (domain != null) {
+            header += "; Domain=" + domain;
+        }
+        if (path != null) {
+            header += "; Path=" + path;
+        }
+        if ( secure || ESAPI.securityConfiguration().getForceSecureCookies() ) {
+			header += "; Secure";
+        }
+        if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) {
+			header += "; HttpOnly";
+        }
+        return header;
+    }
+
+    /**
+     * Add a cookie to the response after ensuring that there are no encoded or
+     * illegal characters in the name.
+     * @param name 
+     * @param date
+     */
+    public void addDateHeader(String name, long date) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().addDateHeader(safeName, date);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e);
+        }
+    }
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and name and value. This implementation
+     * follows the following recommendation: "A recipient MAY replace any linear
+     * white space with a single SP before interpreting the field value or
+     * forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     * @param name
+     * @param value
+     */
+    public void addHeader(String name, String value) {
+        try {
+            // TODO: make stripping a global config
+            String strippedName = StringUtilities.stripControls(name);
+            String strippedValue = StringUtilities.stripControls(value);
+            String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false);
+            getHttpServletResponse().setHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e);
+        }
+    }
+
+    /**
+     * Add an int header to the response after ensuring that there are no
+     * encoded or illegal characters in the name and name.
+     * @param name 
+     * @param value
+     */
+    public void addIntHeader(String name, int value) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().addIntHeader(safeName, value);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e);
+        }
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param name
+     * @return
+     */
+    public boolean containsHeader(String name) {
+        return getHttpServletResponse().containsHeader(name);
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID. The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     * @deprecated in servlet spec 2.1. Use
+     * {@link #encodeRedirectUrl(String)} instead.
+     */
+    @Deprecated
+    public String encodeRedirectUrl(String url) {
+        return url;
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     */
+    public String encodeRedirectURL(String url) {
+        return url;
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     * @deprecated in servlet spec 2.1. Use
+     * {@link #encodeURL(String)} instead.
+     */
+    @Deprecated
+    public String encodeUrl(String url) {
+        return url;
+    }
+
+    /**
+     * Return the URL without any changes, to prevent disclosure of the
+     * Session ID The default implementation of this method can add the
+     * Session ID to the URL if support for cookies is not detected. This
+     * exposes the Session ID credential in bookmarks, referer headers, server
+     * logs, and more.
+     * 
+     * @param url
+     * @return original url
+     */
+    public String encodeURL(String url) {
+        return url;
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @throws IOException
+     */
+    public void flushBuffer() throws IOException {
+        getHttpServletResponse().flushBuffer();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public int getBufferSize() {
+        return getHttpServletResponse().getBufferSize();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public String getCharacterEncoding() {
+        return getHttpServletResponse().getCharacterEncoding();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public String getContentType() {
+        return getHttpServletResponse().getContentType();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public Locale getLocale() {
+        return getHttpServletResponse().getLocale();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return 
+     * @throws IOException
+     */
+    public ServletOutputStream getOutputStream() throws IOException {
+        return getHttpServletResponse().getOutputStream();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return 
+     * @throws IOException
+     */
+    public PrintWriter getWriter() throws IOException {
+        return getHttpServletResponse().getWriter();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @return
+     */
+    public boolean isCommitted() {
+        return getHttpServletResponse().isCommitted();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     */
+    public void reset() {
+        getHttpServletResponse().reset();
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     */
+    public void resetBuffer() {
+        getHttpServletResponse().resetBuffer();
+    }
+
+    /**
+     * Override the error code with a 200 in order to confound attackers using
+     * automated scanners.
+     * @param sc 
+     * @throws IOException
+     */
+    public void sendError(int sc) throws IOException {
+        getHttpServletResponse().sendError(HttpServletResponse.SC_OK, getHTTPMessage(sc));
+    }
+
+    /**
+     * Override the error code with a 200 in order to confound attackers using
+     * automated scanners. The message is canonicalized and filtered for
+     * dangerous characters.
+     * @param sc 
+     * @param msg
+     * @throws IOException
+     */
+    public void sendError(int sc, String msg) throws IOException {
+        getHttpServletResponse().sendError(HttpServletResponse.SC_OK, ESAPI.encoder().encodeForHTML(msg));
+    }
+
+    /**
+     * This method generates a redirect response that can only be used to
+     * redirect the browser to safe locations, as configured in the ESAPI
+     * security configuration. This method does not that redirect requests can
+     * be modified by attackers, so do not rely information contained within
+     * redirect requests, and do not include sensitive information in a
+     * redirect.
+     * @param location 
+     * @throws IOException
+     */
+    public void sendRedirect(String location) throws IOException {
+        if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location);
+            throw new IOException("Redirect failed");
+        }
+        getHttpServletResponse().sendRedirect(location);
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param size
+     */
+    public void setBufferSize(int size) {
+        getHttpServletResponse().setBufferSize(size);
+    }
+
+    /**
+     * Sets the character encoding to the ESAPI configured encoding.
+     * @param charset
+     */
+    public void setCharacterEncoding(String charset) {
+        getHttpServletResponse().setCharacterEncoding(ESAPI.securityConfiguration().getCharacterEncoding());
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param len
+     */
+    public void setContentLength(int len) {
+        getHttpServletResponse().setContentLength(len);
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param type
+     */
+    public void setContentType(String type) {
+        getHttpServletResponse().setContentType(type);
+    }
+
+    /**
+     * Add a date header to the response after ensuring that there are no
+     * encoded or illegal characters in the name.
+     * @param name 
+     * @param date
+     */
+    public void setDateHeader(String name, long date) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().setDateHeader(safeName, date);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e);
+        }
+    }
+
+    /**
+     * Add a header to the response after ensuring that there are no encoded or
+     * illegal characters in the name and value. "A recipient MAY replace any
+     * linear white space with a single SP before interpreting the field value
+     * or forwarding the message downstream."
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
+     * @param name 
+     * @param value
+     */
+    public void setHeader(String name, String value) {
+        try {
+            String strippedName = StringUtilities.stripControls(name);
+            String strippedValue = StringUtilities.stripControls(value);
+            String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false);
+            getHttpServletResponse().setHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e);
+        }
+    }
+
+    /**
+     * Add an int header to the response after ensuring that there are no
+     * encoded or illegal characters in the name.
+     * @param name 
+     * @param value
+     */
+    public void setIntHeader(String name, int value) {
+        try {
+            String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false);
+            getHttpServletResponse().setIntHeader(safeName, value);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e);
+        }
+    }
+
+    /**
+     * Same as HttpServletResponse, no security changes required.
+     * @param loc
+     */
+    public void setLocale(Locale loc) {
+        // TODO investigate the character set issues here
+        getHttpServletResponse().setLocale(loc);
+    }
+
+    /**
+     * Override the status code with a 200 in order to confound attackers using
+     * automated scanners.
+     * @param sc
+     */
+    public void setStatus(int sc) {
+        getHttpServletResponse().setStatus(HttpServletResponse.SC_OK);
+    }
+
+    /**
+     * Override the status code with a 200 in order to confound attackers using
+     * automated scanners. The message is canonicalized and filtered for
+     * dangerous characters.
+     * @param sc 
+     * @param sm
+     * @deprecated In Servlet spec 2.1.
+     */
+    @Deprecated
+    public void setStatus(int sc, String sm) {
+        try {
+            // setStatus is deprecated so use sendError instead
+            sendError(HttpServletResponse.SC_OK, sm);
+        } catch (IOException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set response status failed", e);
+        }
+    }
+
+    /**
+     * returns a text message for the HTTP response code
+     */
+    private String getHTTPMessage(int sc) {
+        return "HTTP error code: " + sc;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/filters/package.html b/src/main/java/org/owasp/esapi/filters/package.html
new file mode 100644
index 0000000..0efc5ab
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/filters/package.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains several filters that demonstrate ways of using the ESAPI security
+controls in front of your application. These filters are intended to be used as examples
+that you can customize for your particular application.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/package.html b/src/main/java/org/owasp/esapi/package.html
new file mode 100644
index 0000000..cb23a58
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/package.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+The ESAPI interfaces and {@code Exception} classes model the most
+important security functions to enterprise web applications.
+The interfaces in this package are intended to be extended and
+customized within an enterprise to match their custom data,
+security services, and application environment. A reference
+implementation of this interface is provided as an example of how this
+library can be implemented successfully, but is useful in many ways
+by itself as well.
+<p>
+OWASP ESAPI interfaces and reference implementation provides
+enterprise web application developers with the most important
+security functions they need in ordre to build secure web applications
+and web services that stand up to most common-day web-based attacks.
+</p>
+<h2>Sponsor</h2>
+<p>The <a href="http://www.owasp.org">The Open Web Application
+Security Project (OWASP)</a> is a worldwide free and open community focused
+on improving the security of application software. Our mission is to
+make application security "visible," so that people and organizations
+can make informed decisions about application security risks. Everyone
+is free to participate in OWASP and all of our materials are available
+under an open source license. The OWASP Foundation is a 501c3
+not-for-profit charitable organization that ensures the ongoing
+availability and support for our work.</p> 
+ 
+<p>The
+<a href="http://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API">
+OWASP ESAPI Project</a>
+is led by Jeff Williams,
+<a href="http://www.aspectsecurity.com">Aspect Security</a>.
+ 
+<p>You can find more information about the ESAPI Java project, or join
+the mailing list and help us make it better from the OWASP project page
+at <a
+href="http://www.owasp.org/index.php/ESAPI#tab=Java_EE">http://www.owasp.org/index.php/ESAPI#tab=Java_EE</a>.</p> 
+ 
+<h2>ESAPI Architecture</h2>
+ 
+<p>The ESAPI class library builds on the excellent security libraries available,
+such as Java Logging, JCE, and Adobe Commons FileUpload. It uses the
+concepts from many of the security packages out there, such as ACEGI,
+Apache Commons Validator, Microsoft's AntiXSS library, and many many
+more. This library provides a single consistent interface to security
+functions that is intuitive for enterprise developers.</p> 
+
+<img src="doc-files/Architecture.jpg"> 
+
+<h2>Addressing OWASP Top Ten</h2>
+ 
+<p>Used properly, the ESAPI provides enough functions to protect
+against most of the
+<a href="http://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project">
+OWASP Top Ten</a>.  The only real exception is the
+Insecure Communications category, which is generally outside the control
+of the software developer.</p> 
+<img src="doc-files/OWASPTopTen.jpg"> 
+
+<h2>Copyright and License</h2>
+ 
+<p>This project and all associated code is Copyright (c) 2007 - The OWASP Foundation</p> 
+ 
+<p>This project licensed under the <a href="http://en.wikipedia.org/wiki/BSD_license">BSD license</a>, 
+which is very permissive and about as close to public domain as is possible. You can use or modify 
+ESAPI however you want, even include it in commercial products.</p>
+
+<h2>References</h2>
+
+This library builds on some of the ideas found in:
+<ul>
+  <li><a href="http://www.owasp.org/">OWASP</a></li>
+  <li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=051ee83c-5ccf-48ed-8463-02f56a6bfc09&displaylang=en">Microsoft's AntiXSS</a></li>
+  <li><a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/JCERefGuide.html">Java JCE</a></li>
+  <li><a href="http://java.sun.com/j2se/1.5.0/docs/guide/logging/overview.html">Java Logging API</a></li>
+  <li><a href="http://commons.apache.org/fileupload/">Apache Commons FileUpload</a></li>
+</ul>
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/all-wcprops b/src/main/java/org/owasp/esapi/reference/.svn/all-wcprops
new file mode 100644
index 0000000..558ec76
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/all-wcprops
@@ -0,0 +1,119 @@
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference
+END
+DefaultIntrusionDetector.java
+K 25
+svn:wc:ra_dav:version-url
+V 105
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultIntrusionDetector.java
+END
+DefaultValidator.java
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultValidator.java
+END
+IntegerAccessReferenceMap.java
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/IntegerAccessReferenceMap.java
+END
+AbstractAuthenticator.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java
+END
+AbstractAccessReferenceMap.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/AbstractAccessReferenceMap.java
+END
+DefaultExecutor.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java
+END
+DefaultSecurityConfiguration.java
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java
+END
+Log4JLoggerFactory.java
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/Log4JLoggerFactory.java
+END
+Log4JLogFactory.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/Log4JLogFactory.java
+END
+FileBasedAuthenticator.java
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java
+END
+DefaultAccessController.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultAccessController.java
+END
+JavaLogFactory.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/JavaLogFactory.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/package.html
+END
+RandomAccessReferenceMap.java
+K 25
+svn:wc:ra_dav:version-url
+V 105
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/RandomAccessReferenceMap.java
+END
+DefaultRandomizer.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultRandomizer.java
+END
+Log4JLogger.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/Log4JLogger.java
+END
+DefaultHTTPUtilities.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java
+END
+DefaultUser.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultUser.java
+END
+DefaultEncoder.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java
+END
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/entries b/src/main/java/org/owasp/esapi/reference/.svn/entries
new file mode 100644
index 0000000..e658cf4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/entries
@@ -0,0 +1,683 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+DefaultValidator.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+614b136f195d7e2ab3d651cb63618041
+2011-07-23T20:25:45.260535Z
+1855
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+46200
+

+IntegerAccessReferenceMap.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+dd55473e38ec23c719065f3af545ded9
+2009-11-14T00:27:36.075200Z
+829
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2124
+

+AbstractAuthenticator.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+342a643c358f050052162bd11c65fece
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11763
+

+crypto
+dir
+

+AbstractAccessReferenceMap.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+3d28b12f489409af4efe4473890421cb
+2009-11-13T17:40:52.467682Z
+818
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7217
+

+DefaultExecutor.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+0587bb02e4fdcfc61b3dab3cf201c41f
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10056
+

+DefaultSecurityConfiguration.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+820d6509d6683258038ea84f4126c7d7
+2011-07-23T20:36:17.804569Z
+1856
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+44583
+

+Log4JLoggerFactory.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+a850cf11e781878b2846855a51638572
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1577
+

+Log4JLogFactory.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+cbb4f219802bd835130d4493a2f28324
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3317
+

+FileBasedAuthenticator.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+dcb9bc9fbc61477f645ce3f5db76393f
+2010-11-17T04:23:01.349932Z
+1656
+jtmelton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+30068
+

+DefaultAccessController.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+3798f8a13ca5539dc2e76cf8163ab9ab
+2010-06-08T04:39:48.001264Z
+1437
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5159
+

+validation
+dir
+

+JavaLogFactory.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+9913a4ad7dadd9378eb970116aaeae47
+2011-02-04T05:26:36.692007Z
+1697
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13802
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+e4bab74ee176297fa562c14cef719c93
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+707
+

+RandomAccessReferenceMap.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+ec5ad528e5ad4d1e1b6ac5003a893fb5
+2009-11-14T00:27:36.075200Z
+829
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2809
+

+DefaultRandomizer.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+3517cb6389c5d886131d93643de2b698
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4201
+

+Log4JLogger.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+28a062aba4bdefca0503ac13a2736336
+2011-02-04T05:27:41.584887Z
+1698
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+14329
+

+DefaultHTTPUtilities.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+3c90cf53a4162b314ba17d5dc82eeed2
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+35448
+

+accesscontrol
+dir
+

+DefaultUser.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+9974a61f4f39b3c3ebdde3f0dbe53dc0
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+16761
+

+DefaultEncoder.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+2c33c67acbc8ff23c6f1851d59763033
+2011-07-23T20:17:34.462251Z
+1854
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+12587
+

+DefaultIntrusionDetector.java
+file
+
+
+
+
+2014-02-18T16:19:52.933966Z
+8f3dc4d9e2efc8127e85a11199237c2f
+2010-01-18T01:56:02.250104Z
+957
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7521
+

diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/AbstractAccessReferenceMap.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/AbstractAccessReferenceMap.java.svn-base
new file mode 100644
index 0000000..b8fc30e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/AbstractAccessReferenceMap.java.svn-base
@@ -0,0 +1,204 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.AccessReferenceMap;
+import org.owasp.esapi.errors.AccessControlException;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Abstract Implementation of the AccessReferenceMap that is backed by ConcurrentHashMaps to
+ * provide a thread-safe implementation of the AccessReferenceMap. Implementations of this
+ * abstract class should implement the #getUniqueReference() method.
+ *
+ * @author  Chris Schmidt (chrisisbeef at gmail.com)
+ * @since   July 21, 2009
+ */
+public abstract class AbstractAccessReferenceMap<K> implements AccessReferenceMap<K>
+{
+   private static final long serialVersionUID = 238742764284682230L;
+
+   /** The Indirect to Direct Map */
+   protected Map<K,Object> itod;
+   /** The Direct to Indirect Map */
+   protected Map<Object,K> dtoi;
+
+   /**
+    * Instantiates a new access reference map. Note that this will create the underlying Maps with an initialSize
+    * of {@link ConcurrentHashMap#DEFAULT_INITIAL_CAPACITY} and that resizing a Map is an expensive process. Consider
+    * using a constructor where the initialSize is passed in to maximize performance of the AccessReferenceMap.
+    *
+    * @see #AbstractAccessReferenceMap(java.util.Set, int)
+    * @see #AbstractAccessReferenceMap(int)
+    */
+   public AbstractAccessReferenceMap() {
+      itod = new ConcurrentHashMap<K, Object>();
+      dtoi = new ConcurrentHashMap<Object,K>();
+   }
+
+   /**
+    * Instantiates a new access reference map with the specified size allotment
+    * to reduce Map resizing overhead.
+    *
+    * @param initialSize
+    *          The initial size of the underlying maps
+    */
+   public AbstractAccessReferenceMap( int initialSize ) {
+      itod = new ConcurrentHashMap<K, Object>(initialSize);
+      dtoi = new ConcurrentHashMap<Object,K>(initialSize);
+   }
+
+   /**
+    * Instantiates a new access reference map with a set of direct references.
+    *
+    * @param directReferences
+    *            the direct references
+    * @deprecated This constructor internally calls the abstract method
+    *	{@link #getUniqueReference()}. Since this is a constructor, any
+    *	subclass that implements getUniqueReference() has not had it's
+    *	own constructor run. This leads to strange bugs because subclass
+    *	internal state is initializaed after calls to getUniqueReference()
+    *	have already happened. If this constructor is desired in a
+    *	subclass, consider running {@link #update(Set)} in the subclass
+    *	constructor instead.
+    */
+   @Deprecated
+   public AbstractAccessReferenceMap( Set<Object> directReferences ) {
+      itod = new ConcurrentHashMap<K, Object>(directReferences.size());
+      dtoi = new ConcurrentHashMap<Object,K>(directReferences.size());
+      update(directReferences);
+   }
+
+   /**
+    * Instantiates a new access reference map with the specified size allotment
+    * and initializes the map with the passed in references. Note that if you pass
+    * in an initialSize that is less than the size of the passed in set, the map will
+    * need to be resized while it is being loaded with the references so it is
+    * best practice to verify that the size being passed in is always larger than
+    * the size of the set that is being passed in.
+    *
+    * @param directReferences
+    *          The references to initialize the access reference map
+    * @param initialSize
+    *          The initial size to set the map to.
+    *
+    * @deprecated This constructor internally calls the abstract method
+    *	{@link #getUniqueReference()}. Since this is a constructor, any
+    *	subclass that implements getUniqueReference() has not had it's
+    *	own constructor run. This leads to strange bugs because subclass
+    *	internal state is initializaed after calls to getUniqueReference()
+    *	have already happened. If this constructor is desired in a
+    *	subclass, consider running {@link #update(Set)} in the subclass
+    *	constructor instead.
+    */
+   @Deprecated
+   public AbstractAccessReferenceMap( Set<Object> directReferences, int initialSize ) {
+      itod = new ConcurrentHashMap<K, Object>(initialSize);
+      dtoi = new ConcurrentHashMap<Object,K>(initialSize);
+      update(directReferences);
+   }
+
+   /**
+    * Returns a Unique Reference Key to be associated with a new directReference being
+    * inserted into the AccessReferenceMap.
+    *
+    * @return Reference Identifier
+    */
+   protected abstract K getUniqueReference();
+
+   /**
+   * {@inheritDoc}
+   */
+   public synchronized Iterator iterator() {
+      TreeSet sorted = new TreeSet(dtoi.keySet());
+      return sorted.iterator();
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> K addDirectReference(T direct) {
+      if ( dtoi.keySet().contains( direct ) ) {
+         return dtoi.get( direct );
+      }
+      K indirect = getUniqueReference();
+      itod.put(indirect, direct);
+      dtoi.put(direct, indirect);
+      return indirect;
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> K removeDirectReference(T direct) throws AccessControlException
+   {
+      K indirect = dtoi.get(direct);
+      if ( indirect != null ) {
+         itod.remove(indirect);
+         dtoi.remove(direct);
+      }
+      return indirect;
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public final synchronized void update(Set directReferences) {
+      Map<Object,K> new_dtoi = new ConcurrentHashMap<Object,K>( directReferences.size() );
+      Map<K,Object> new_itod = new ConcurrentHashMap<K,Object>( directReferences.size() );
+
+      for ( Object o : directReferences ) {
+         K indirect = dtoi.get( o );
+
+         if ( indirect == null ) {
+            indirect = getUniqueReference();
+         }
+         new_dtoi.put( o, indirect );
+         new_itod.put( indirect, o );
+      }
+      dtoi = new_dtoi;
+      itod = new_itod;
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> K getIndirectReference(T directReference) {
+      return dtoi.get(directReference);
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> T getDirectReference(K indirectReference) throws AccessControlException {
+      if (itod.containsKey(indirectReference) ) {
+         try
+         {
+            return (T) itod.get(indirectReference);
+         }
+         catch (ClassCastException e)
+         {
+            throw new AccessControlException("Access denied.", "Request for incorrect type reference: " + indirectReference);
+         }
+      }
+      throw new AccessControlException("Access denied", "Request for invalid indirect reference: " + indirectReference);
+   }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/AbstractAuthenticator.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/AbstractAuthenticator.java.svn-base
new file mode 100644
index 0000000..4876da4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/AbstractAuthenticator.java.svn-base
@@ -0,0 +1,297 @@
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationCredentialsException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.AuthenticationLoginException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+/**
+ * A partial implementation of the Authenticator interface.
+ * This class should not implement any methods that would be meant
+ * to modify a User object, since that's probably implementation specific.
+ *
+ */
+public abstract class AbstractAuthenticator implements org.owasp.esapi.Authenticator {
+
+	/**
+     * Key for user in session
+     */
+    protected static final String USER = "ESAPIUserSessionKey";
+    
+    private final Logger logger = ESAPI.getLogger("Authenticator");
+    
+    /**
+     * The currentUser ThreadLocal variable is used to make the currentUser available to any call in any part of an
+     * application. Otherwise, each thread would have to pass the User object through the calltree to any methods that
+     * need it. Because we want exceptions and log calls to contain user data, that could be almost anywhere. Therefore,
+     * the ThreadLocal approach simplifies things greatly. <P> As a possible extension, one could create a delegation
+     * framework by adding another ThreadLocal to hold the delegating user identity.
+     */
+    private final ThreadLocalUser currentUser = new ThreadLocalUser();
+
+    private class ThreadLocalUser extends InheritableThreadLocal<User> {
+
+        public User initialValue() {
+            return User.ANONYMOUS;
+        }
+
+        public User getUser() {
+            return super.get();
+        }
+
+        public void setUser(User newUser) {
+            super.set(newUser);
+        }
+    }
+    
+	/**
+	 *
+	 */
+	public AbstractAuthenticator() {
+		super();
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+    public void clearCurrent() {
+        // logger.logWarning(Logger.SECURITY, "************Clearing threadlocals. Thread" + Thread.currentThread().getName() );
+        currentUser.setUser(null);
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(String accountName) {
+        return getUser(accountName) != null;
+    }
+    
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Returns the currently logged user as set by the setCurrentUser() methods. Must not log in this method because the
+     * logger calls getCurrentUser() and this could cause a loop.
+     */
+    public User getCurrentUser() {
+        User user = currentUser.get();
+        if (user == null) {
+            user = User.ANONYMOUS;
+        }
+        return user;
+    }
+    
+    /**
+     * Gets the user from session.
+     *
+     * @return the user from session or null if no user is found in the session
+     */
+    protected User getUserFromSession() {
+        HttpSession session = ESAPI.httpUtilities().getCurrentRequest().getSession(false);
+        if (session == null) return null;
+        return ESAPI.httpUtilities().getSessionAttribute(USER);
+    }
+    
+    /**
+     * Returns the user if a matching remember token is found, or null if the token
+     * is missing, token is corrupt, token is expired, account name does not match
+     * and existing account, or hashed password does not match user's hashed password.
+     *
+     * @return the user if a matching remember token is found, or null if the token
+     *         is missing, token is corrupt, token is expired, account name does not match
+     *         and existing account, or hashed password does not match user's hashed password.
+     */
+    protected DefaultUser getUserFromRememberToken() {
+        try {
+            String token = ESAPI.httpUtilities().getCookie(ESAPI.currentRequest(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
+            if (token == null) return null;
+            
+            // See Google Issue 144 regarding first URLDecode the token and THEN unsealing.
+            // Note that this Google Issue was marked as "WontFix".
+
+            String[] data = ESAPI.encryptor().unseal(token).split("\\|");
+            if (data.length != 2) {
+                logger.warning(Logger.SECURITY_FAILURE, "Found corrupt or expired remember token");
+                ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
+                return null;
+            }
+
+            String username = data[0];
+            String password = data[1];
+            DefaultUser user = (DefaultUser) getUser(username);
+            if (user == null) {
+                logger.warning(Logger.SECURITY_FAILURE, "Found valid remember token but no user matching " + username);
+                return null;
+            }
+
+            logger.info(Logger.SECURITY_SUCCESS, "Logging in user with remember token: " + user.getAccountName());
+            user.loginWithPassword(password);
+            return user;
+        } catch (AuthenticationException ae) {
+            logger.warning(Logger.SECURITY_FAILURE, "Login via remember me cookie failed", ae);
+        } catch (EnterpriseSecurityException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Remember token was missing, corrupt, or expired");
+        }
+        ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
+        return null;
+    }
+    
+    /**
+     * Utility method to extract credentials and verify them.
+     *
+     * @param request The current HTTP request
+     * @return The user that successfully authenticated
+     * @throws AuthenticationException if the submitted credentials are invalid.
+     */
+    private User loginWithUsernameAndPassword(HttpServletRequest request) throws AuthenticationException {
+
+        String username = request.getParameter(ESAPI.securityConfiguration().getUsernameParameterName());
+        String password = request.getParameter(ESAPI.securityConfiguration().getPasswordParameterName());
+
+        // if a logged-in user is requesting to login, log them out first
+        User user = getCurrentUser();
+        if (user != null && !user.isAnonymous()) {
+            logger.warning(Logger.SECURITY_SUCCESS, "User requested relogin. Performing logout then authentication");
+            user.logout();
+        }
+
+        // now authenticate with username and password
+        if (username == null || password == null) {
+            if (username == null) {
+                username = "unspecified user";
+            }
+            throw new AuthenticationCredentialsException("Authentication failed", "Authentication failed for " + username + " because of null username or password");
+        }
+        user = getUser(username);
+        if (user == null) {
+            throw new AuthenticationCredentialsException("Authentication failed", "Authentication failed because user " + username + " doesn't exist");
+        }
+        user.loginWithPassword(password);
+
+        request.setAttribute(user.getCSRFToken(), "authenticated");
+        return user;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public User login() throws AuthenticationException {
+        return login(ESAPI.currentRequest(), ESAPI.currentResponse());
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
+
+        if (request == null || response == null) {
+            throw new AuthenticationCredentialsException("Invalid request", "Request or response objects were null");
+        }
+
+        // if there's a user in the session then use that
+        DefaultUser user = (DefaultUser) getUserFromSession();
+
+        // else if there's a remember token then use that
+        if (user == null) {
+            user = getUserFromRememberToken();
+        }
+
+        // else try to verify credentials - throws exception if login fails
+        if (user == null) {
+            user = (DefaultUser) loginWithUsernameAndPassword(request);
+        }
+
+        // set last host address
+        user.setLastHostAddress(request.getRemoteHost());
+
+        // warn if this authentication request was not POST or non-SSL connection, exposing credentials or session id
+        try {
+            ESAPI.httpUtilities().assertSecureRequest(ESAPI.currentRequest());
+        } catch (AccessControlException e) {
+            throw new AuthenticationException("Attempt to login with an insecure request", e.getLogMessage(), e);
+        }
+
+        // don't let anonymous user log in
+        if (user.isAnonymous()) {
+            user.logout();
+            throw new AuthenticationLoginException("Login failed", "Anonymous user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // don't let disabled users log in
+        if (!user.isEnabled()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Disabled user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // don't let locked users log in
+        if (user.isLocked()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Locked user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // don't let expired users log in
+        if (user.isExpired()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Expired user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // check session inactivity timeout
+        if (user.isSessionTimeout()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Session inactivity timeout: " + user.getAccountName());
+        }
+
+        // check session absolute timeout
+        if (user.isSessionAbsoluteTimeout()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Session absolute timeout: " + user.getAccountName());
+        }
+
+        //set Locale to the user object in the session from request
+        user.setLocale(request.getLocale());
+
+        // create new session for this User
+        HttpSession session = request.getSession();
+        user.addSession(session);
+        session.setAttribute(USER, user);
+        setCurrentUser(user);
+        return user;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void logout() {
+        User user = getCurrentUser();
+        if (user != null && !user.isAnonymous()) {
+            user.logout();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCurrentUser(User user) {
+        currentUser.setUser(user);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultAccessController.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultAccessController.java.svn-base
new file mode 100644
index 0000000..3d51338
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultAccessController.java.svn-base
@@ -0,0 +1,146 @@
+package org.owasp.esapi.reference;
+
+import java.util.Map;
+
+import org.owasp.esapi.AccessControlRule;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.reference.accesscontrol.policyloader.ACRPolicyFileLoader;
+import org.owasp.esapi.reference.accesscontrol.policyloader.PolicyDTO;
+
+public class DefaultAccessController implements AccessController {
+	private Map ruleMap;
+
+    private static volatile AccessController singletonInstance = null;
+
+    public static AccessController getInstance() throws AccessControlException {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultAccessController.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultAccessController();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+	protected final Logger logger = ESAPI.getLogger("DefaultAccessController");
+
+	private DefaultAccessController() throws AccessControlException {
+		ACRPolicyFileLoader policyDescriptor = new ACRPolicyFileLoader();
+		PolicyDTO policyDTO = policyDescriptor.load();		
+		ruleMap = policyDTO.getAccessControlRules();
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+	public boolean isAuthorized(Object key, Object runtimeParameter) {
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied",
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Evaluating Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			return rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			try {
+				//Log the exception by throwing and then catching it.
+				//TODO figure out what which string goes where.		
+				throw new AccessControlException("Access Denied",
+					"An unhandled Exception was " +
+					"caught, so access is denied.",  
+					e);	
+			} catch(AccessControlException ace) {
+				//the exception was just logged. There's nothing left to do.
+			}
+			return false; //fail closed
+		}
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorized(Object key, Object runtimeParameter) throws AccessControlException {
+		boolean isAuthorized;
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied", 
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Asserting Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			isAuthorized = rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			//TODO figure out what which string goes where.		
+			throw new AccessControlException("Access Denied", "An unhandled Exception was " +
+					"caught, so access is denied." +
+					"AccessControlException.",
+					e);
+		}
+		if(!isAuthorized) {
+			throw new AccessControlException("Access Denied", 
+					"Access Denied for key: " + key + 
+					" runtimeParameter: " + runtimeParameter);
+		}
+	}
+	
+    /** {@inheritDoc} */
+	public void assertAuthorizedForData(String action, Object data)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+	/**
+     * {@inheritDoc}
+	 * @deprecated
+	 */
+	public void assertAuthorizedForFile(String filepath)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorizedForFunction(String functionName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorizedForService(String serviceName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorizedForURL(String url)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForData(String action, Object data) {
+		return this.isAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForFile(String filepath) {
+		return this.isAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForFunction(String functionName) {
+		return this.isAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForService(String serviceName) {
+		return this.isAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForURL(String url) {
+		return this.isAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultEncoder.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultEncoder.java.svn-base
new file mode 100644
index 0000000..4fadcf0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultEncoder.java.svn-base
@@ -0,0 +1,448 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.codecs.Base64;
+import org.owasp.esapi.codecs.CSSCodec;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.HTMLEntityCodec;
+import org.owasp.esapi.codecs.JavaScriptCodec;
+import org.owasp.esapi.codecs.PercentCodec;
+import org.owasp.esapi.codecs.VBScriptCodec;
+import org.owasp.esapi.codecs.XMLEntityCodec;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.IntrusionException;
+
+
+/**
+ * Reference implementation of the Encoder interface. This implementation takes
+ * a whitelist approach to encoding, meaning that everything not specifically identified in a
+ * list of "immune" characters is encoded.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class DefaultEncoder implements Encoder {
+
+    private static volatile Encoder singletonInstance;
+
+    public static Encoder getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultEncoder.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultEncoder();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+	// Codecs
+	private List codecs = new ArrayList();
+	private HTMLEntityCodec htmlCodec = new HTMLEntityCodec();
+	private XMLEntityCodec xmlCodec = new XMLEntityCodec();
+	private PercentCodec percentCodec = new PercentCodec();
+	private JavaScriptCodec javaScriptCodec = new JavaScriptCodec();
+	private VBScriptCodec vbScriptCodec = new VBScriptCodec();
+	private CSSCodec cssCodec = new CSSCodec();
+
+	private final Logger logger = ESAPI.getLogger("Encoder");
+	
+	/**
+	 *  Character sets that define characters (in addition to alphanumerics) that are
+	 * immune from encoding in various formats
+	 */
+	private final static char[]     IMMUNE_HTML = { ',', '.', '-', '_', ' ' };
+	private final static char[] IMMUNE_HTMLATTR = { ',', '.', '-', '_' };
+	private final static char[] IMMUNE_CSS = {};
+	private final static char[] IMMUNE_JAVASCRIPT = { ',', '.', '_' };
+	private final static char[] IMMUNE_VBSCRIPT = { ',', '.', '_' };
+	private final static char[] IMMUNE_XML = { ',', '.', '-', '_', ' ' };
+	private final static char[] IMMUNE_SQL = { ' ' };
+	private final static char[] IMMUNE_OS = { '-' };
+	private final static char[] IMMUNE_XMLATTR = { ',', '.', '-', '_' };
+	private final static char[] IMMUNE_XPATH = { ',', '.', '-', '_', ' ' };
+	
+	
+	/**
+	 * Instantiates a new DefaultEncoder
+	 */
+	private DefaultEncoder() {
+		codecs.add( htmlCodec );
+		codecs.add( percentCodec );
+		codecs.add( javaScriptCodec );
+	}
+	
+	public DefaultEncoder( List<String> codecNames ) {
+		for ( String clazz : codecNames ) {
+			try {
+				if ( clazz.indexOf( '.' ) == -1 ) clazz = "org.owasp.esapi.codecs." + clazz;
+				codecs.add( Class.forName( clazz ).newInstance() );
+			} catch ( Exception e ) {
+				logger.warning( Logger.EVENT_FAILURE, "Codec " + clazz + " listed in ESAPI.properties not on classpath" );
+			}
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String canonicalize( String input ) {
+		if ( input == null ) {
+			return null;
+		}
+
+        // Issue 231 - These are reverse boolean logic in the Encoder interface, so we need to invert these values - CS
+		return canonicalize(input, 
+							!ESAPI.securityConfiguration().getAllowMultipleEncoding(),
+							!ESAPI.securityConfiguration().getAllowMixedEncoding() );
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String canonicalize( String input, boolean strict) {
+		return canonicalize(input, strict, strict);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String canonicalize( String input, boolean restrictMultiple, boolean restrictMixed ) {
+		if ( input == null ) {
+			return null;
+		}
+		
+        String working = input;
+        Codec codecFound = null;
+        int mixedCount = 1;
+        int foundCount = 0;
+        boolean clean = false;
+        while( !clean ) {
+            clean = true;
+            
+            // try each codec and keep track of which ones work
+            Iterator i = codecs.iterator();
+            while ( i.hasNext() ) {
+                Codec codec = (Codec)i.next();
+                String old = working;
+                working = codec.decode( working );
+                if ( !old.equals( working ) ) {
+                    if ( codecFound != null && codecFound != codec ) {
+                        mixedCount++;
+                    }
+                    codecFound = codec;
+                    if ( clean ) {
+                        foundCount++;
+                    }
+                    clean = false;
+                }
+            }
+        }
+        
+        // do strict tests and handle if any mixed, multiple, nested encoding were found
+        if ( foundCount >= 2 && mixedCount > 1 ) {
+            if ( restrictMultiple || restrictMixed ) {
+                throw new IntrusionException( "Input validation failure", "Multiple ("+ foundCount +"x) and mixed encoding ("+ mixedCount +"x) detected in " + input );
+            } else {
+                logger.warning( Logger.SECURITY_FAILURE, "Multiple ("+ foundCount +"x) and mixed encoding ("+ mixedCount +"x) detected in " + input );
+            }
+        }
+        else if ( foundCount >= 2 ) {
+            if ( restrictMultiple ) {
+                throw new IntrusionException( "Input validation failure", "Multiple ("+ foundCount +"x) encoding detected in " + input );
+            } else {
+                logger.warning( Logger.SECURITY_FAILURE, "Multiple ("+ foundCount +"x) encoding detected in " + input );
+            }
+        }
+        else if ( mixedCount > 1 ) {
+            if ( restrictMixed ) {
+                throw new IntrusionException( "Input validation failure", "Mixed encoding ("+ mixedCount +"x) detected in " + input );
+            } else {
+                logger.warning( Logger.SECURITY_FAILURE, "Mixed encoding ("+ mixedCount +"x) detected in " + input );
+            }
+        }
+        return working;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForHTML(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return htmlCodec.encode( IMMUNE_HTML, input);	    
+	 }
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String decodeForHTML(String input) {
+		
+		if( input == null ) {
+	    	return null;
+	    }
+	    return htmlCodec.decode( input);	 
+    }
+	 
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForHTMLAttribute(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return htmlCodec.encode( IMMUNE_HTMLATTR, input);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForCSS(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return cssCodec.encode( IMMUNE_CSS, input);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForJavaScript(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return javaScriptCodec.encode(IMMUNE_JAVASCRIPT, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForVBScript(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return vbScriptCodec.encode(IMMUNE_VBSCRIPT, input);	    
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForSQL(Codec codec, String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return codec.encode(IMMUNE_SQL, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForOS(Codec codec, String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return codec.encode( IMMUNE_OS, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForLDAP(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+		// TODO: replace with LDAP codec
+	    StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			switch (c) {
+			case '\\':
+				sb.append("\\5c");
+				break;
+			case '*':
+				sb.append("\\2a");
+				break;
+			case '(':
+				sb.append("\\28");
+				break;
+			case ')':
+				sb.append("\\29");
+				break;
+			case '\0':
+				sb.append("\\00");
+				break;
+			default:
+				sb.append(c);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForDN(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+		// TODO: replace with DN codec
+	    StringBuilder sb = new StringBuilder();
+		if ((input.length() > 0) && ((input.charAt(0) == ' ') || (input.charAt(0) == '#'))) {
+			sb.append('\\'); // add the leading backslash if needed
+		}
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			switch (c) {
+			case '\\':
+				sb.append("\\\\");
+				break;
+			case ',':
+				sb.append("\\,");
+				break;
+			case '+':
+				sb.append("\\+");
+				break;
+			case '"':
+				sb.append("\\\"");
+				break;
+			case '<':
+				sb.append("\\<");
+				break;
+			case '>':
+				sb.append("\\>");
+				break;
+			case ';':
+				sb.append("\\;");
+				break;
+			default:
+				sb.append(c);
+			}
+		}
+		// add the trailing backslash if needed
+		if ((input.length() > 1) && (input.charAt(input.length() - 1) == ' ')) {
+			sb.insert(sb.length() - 1, '\\');
+		}
+		return sb.toString();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForXPath(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return htmlCodec.encode( IMMUNE_XPATH, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForXML(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return xmlCodec.encode( IMMUNE_XML, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForXMLAttribute(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return xmlCodec.encode( IMMUNE_XMLATTR, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForURL(String input) throws EncodingException {
+		if ( input == null ) {
+			return null;
+		}
+		try {
+			return URLEncoder.encode(input, ESAPI.securityConfiguration().getCharacterEncoding());
+		} catch (UnsupportedEncodingException ex) {
+			throw new EncodingException("Encoding failure", "Character encoding not supported", ex);
+		} catch (Exception e) {
+			throw new EncodingException("Encoding failure", "Problem URL encoding input", e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String decodeFromURL(String input) throws EncodingException {
+		if ( input == null ) {
+			return null;
+		}
+		String canonical = canonicalize(input);
+		try {
+			return URLDecoder.decode(canonical, ESAPI.securityConfiguration().getCharacterEncoding());
+		} catch (UnsupportedEncodingException ex) {
+			throw new EncodingException("Decoding failed", "Character encoding not supported", ex);
+		} catch (Exception e) {
+			throw new EncodingException("Decoding failed", "Problem URL decoding input", e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForBase64(byte[] input, boolean wrap) {
+		if ( input == null ) {
+			return null;
+		}
+		int options = 0;
+		if ( !wrap ) {
+			options |= Base64.DONT_BREAK_LINES;
+		}
+		return Base64.encodeBytes(input, options);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public byte[] decodeFromBase64(String input) throws IOException {
+		if ( input == null ) {
+			return null;
+		}
+		return Base64.decode( input );
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultExecutor.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultExecutor.java.svn-base
new file mode 100644
index 0000000..9f49765
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultExecutor.java.svn-base
@@ -0,0 +1,234 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Map;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.ExecuteResult;
+import org.owasp.esapi.Executor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.UnixCodec;
+import org.owasp.esapi.codecs.WindowsCodec;
+import org.owasp.esapi.errors.ExecutorException;
+
+/**
+ * Reference implementation of the Executor interface. This implementation is very restrictive. Commands must exactly
+ * equal the canonical path to an executable on the system. 
+ * 
+ * <p>Valid characters for parameters are codec dependent, but will usually only include alphanumeric, forward-slash, and dash.</p>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Executor
+ */
+public class DefaultExecutor implements org.owasp.esapi.Executor {
+    private static volatile Executor singletonInstance;
+
+    public static Executor getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultExecutor.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultExecutor();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    /** The logger. */
+    private final Logger logger = ESAPI.getLogger("Executor");
+    private Codec codec = null;
+    //private final int MAX_SYSTEM_COMMAND_LENGTH = 2500;
+    
+
+    /**
+     * Instantiate a new Executor
+     */
+    private DefaultExecutor() {
+		if ( System.getProperty("os.name").indexOf("Windows") != -1 ) {
+			logger.warning( Logger.SECURITY_SUCCESS, "Using WindowsCodec for Executor. If this is not running on Windows this could allow injection" );
+			codec = new WindowsCodec();
+		} else {
+			logger.warning( Logger.SECURITY_SUCCESS, "Using UnixCodec for Executor. If this is not running on Unix this could allow injection" );
+			codec = new UnixCodec();
+		}
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ExecuteResult executeSystemCommand(File executable, List params) throws ExecutorException {
+    	File workdir = ESAPI.securityConfiguration().getWorkingDirectory();
+    	boolean logParams = false;
+    	boolean redirectErrorStream = false;
+    	return executeSystemCommand( executable, params, workdir, codec, logParams, redirectErrorStream );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * The reference implementation sets the work directory, escapes the parameters as per the Codec in use,
+     * and then executes the command without using concatenation. The exact, absolute, canonical path of each
+     * executable must be listed as an approved executable in the ESAPI properties. The executable must also
+     * exist on the disk. All failures will be logged, along with parameters if specified. Set the logParams to false if
+     * you are going to invoke this interface with confidential information.
+     */
+    public ExecuteResult executeSystemCommand(File executable, List params, File workdir, Codec codec, boolean logParams, boolean redirectErrorStream ) throws ExecutorException {
+        try {
+            // executable must exist
+            if (!executable.exists()) {
+                throw new ExecutorException("Execution failure", "No such executable: " + executable);
+            }
+            
+            // executable must use canonical path
+            if ( !executable.isAbsolute() ) {
+                throw new ExecutorException("Execution failure", "Attempt to invoke an executable using a non-absolute path: " + executable);
+            }
+            
+            // executable must use canonical path
+            if ( !executable.getPath().equals( executable.getCanonicalPath() ) ) {
+            	throw new ExecutorException("Execution failure", "Attempt to invoke an executable using a non-canonical path: " + executable);
+        	}
+            
+            // exact, absolute, canonical path to executable must be listed in ESAPI configuration 
+            List approved = ESAPI.securityConfiguration().getAllowedExecutables();
+            if (!approved.contains(executable.getPath())) {
+                throw new ExecutorException("Execution failure", "Attempt to invoke executable that is not listed as an approved executable in ESAPI configuration: " + executable.getPath() + " not listed in " + approved );
+            }
+
+            // escape any special characters in the parameters
+            for ( int i = 0; i < params.size(); i++ ) {
+            	String param = (String)params.get(i);
+            	params.set( i, ESAPI.encoder().encodeForOS(codec, param));
+            }
+            
+            // working directory must exist
+            if (!workdir.exists()) {
+                throw new ExecutorException("Execution failure", "No such working directory for running executable: " + workdir.getPath());
+            }
+            
+            // set the command into the list and create command array
+            params.add(0, executable.getCanonicalPath());
+
+            // Legacy - this is how to implement in Java 1.4
+            // String[] command = (String[])params.toArray( new String[0] );
+            // Process process = Runtime.getRuntime().exec(command, new String[0], workdir);
+            
+            // The following is host to implement in Java 1.5+
+            ProcessBuilder pb = new ProcessBuilder(params);
+            Map env = pb.environment();
+            env.clear();  // Security check - clear environment variables!
+            pb.directory(workdir);
+            pb.redirectErrorStream(redirectErrorStream);
+
+            if ( logParams ) {
+            	logger.warning(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " " + params + " in " + workdir);
+            } else {
+            	logger.warning(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " [sensitive parameters obscured] in " + workdir);
+            }
+
+            final StringBuilder outputBuffer = new StringBuilder();
+            final StringBuilder errorsBuffer = new StringBuilder();
+            final Process process = pb.start();
+            try {
+                ReadThread errorReader;
+                if (!redirectErrorStream) {
+                	errorReader = new ReadThread(process.getErrorStream(), errorsBuffer);
+                	errorReader.start();
+                } else {
+                	errorReader = null;
+                }
+            	readStream( process.getInputStream(), outputBuffer );
+            	if (errorReader != null) {
+            		errorReader.join();
+            		if (errorReader.exception != null) {
+            			throw errorReader.exception;
+            		}
+            	}
+            	process.waitFor();
+            } catch (Throwable e) {
+            	process.destroy();
+            	throw new ExecutorException("Execution failure", "Exception thrown during execution of system command: " + e.getMessage(), e);
+            }
+
+            String output = outputBuffer.toString();
+            String errors = errorsBuffer.toString();
+            int exitValue = process.exitValue();
+            if ( errors != null && errors.length() > 0 ) {
+            	String logErrors = errors;
+            	final int MAX_LEN = 256;
+            	if (logErrors.length() > MAX_LEN) {
+            		logErrors = logErrors.substring(0, MAX_LEN) + "(truncated at "+MAX_LEN+" characters)";
+            	}
+            	logger.warning( Logger.SECURITY_SUCCESS, "Error during system command: " + logErrors );
+            }
+            if ( exitValue != 0 ) {
+            	logger.warning( Logger.EVENT_FAILURE, "System command exited with non-zero status: " + exitValue );
+            }
+
+            logger.warning(Logger.SECURITY_SUCCESS, "System command complete");
+            return new ExecuteResult(exitValue, output, errors);
+        } catch (IOException e) {
+            throw new ExecutorException("Execution failure", "Exception thrown during execution of system command: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * readStream reads lines from an input stream and returns all of them in a single string
+     * 
+     * @param is
+     * 			input stream to read from
+     * @throws IOException
+     */
+    private static void readStream( InputStream is, StringBuilder sb ) throws IOException {
+	    InputStreamReader isr = new InputStreamReader(is);
+	    BufferedReader br = new BufferedReader(isr);
+	    String line;
+	    while ((line = br.readLine()) != null) {
+	        sb.append(line).append('\n');
+	    }
+    }
+    
+    private static class ReadThread extends Thread {
+    	volatile IOException exception;
+    	private final InputStream stream;
+    	private final StringBuilder buffer;
+
+    	ReadThread(InputStream stream, StringBuilder buffer) {
+    		this.stream = stream;
+    		this.buffer = buffer;
+    	}
+
+    	@Override
+		public void run() {
+    		try {
+    			readStream(stream, buffer);
+    		} catch (IOException e) {
+    			exception = e;
+    		}
+    	}
+
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultHTTPUtilities.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultHTTPUtilities.java.svn-base
new file mode 100644
index 0000000..dc99d76
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultHTTPUtilities.java.svn-base
@@ -0,0 +1,1022 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.ProgressListener;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.owasp.esapi.*;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.*;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Reference implementation of the HTTPUtilities interface. This implementation
+ * uses the Apache Commons FileUploader library, which in turn uses the Apache
+ * Commons IO library.
+ * <P>
+ * To simplify the interface, some methods use the current request and response that
+ * are tracked by ThreadLocal variables in the Authenticator. This means that you
+ * must have called ESAPI.authenticator().setCurrentHTTP(request, response) before
+ * calling these methods.
+ * <P>
+ * Typically, this is done by calling the Authenticator.login() method, which
+ * calls setCurrentHTTP() automatically. However if you want to use these methods
+ * in another application, you should explicitly call setCurrentHTTP() in your
+ * own code. In either case, you *must* call ESAPI.clearCurrent() to clear threadlocal
+ * variables before the thread is reused. The advantages of having identity everywhere
+ * outweigh the disadvantages of this approach.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.HTTPUtilities
+ */
+public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities {
+    private static volatile HTTPUtilities instance = null;
+
+    public static HTTPUtilities getInstance() {
+        if ( instance == null ) {
+            synchronized ( DefaultHTTPUtilities.class ) {
+                if ( instance == null ) {
+                    instance = new DefaultHTTPUtilities();
+                }
+            }
+        }
+        return instance;
+    }
+
+	/**
+     * Defines the ThreadLocalRequest to store the current request for this thread.
+     */
+    private class ThreadLocalRequest extends InheritableThreadLocal<HttpServletRequest> {
+
+        public HttpServletRequest getRequest() {
+            return super.get();
+        }
+
+        public HttpServletRequest initialValue() {
+        	return null;
+        }
+
+        public void setRequest(HttpServletRequest newRequest) {
+            super.set(newRequest);
+        }
+    }
+
+	/**
+     * Defines the ThreadLocalResponse to store the current response for this thread.
+     */
+    private class ThreadLocalResponse extends InheritableThreadLocal<HttpServletResponse> {
+
+        public HttpServletResponse getResponse() {
+            return super.get();
+        }
+
+        public HttpServletResponse initialValue() {
+        	return null;
+        }
+
+        public void setResponse(HttpServletResponse newResponse) {
+            super.set(newResponse);
+        }
+    }
+
+    /** The logger. */
+	private final Logger logger = ESAPI.getLogger("HTTPUtilities");
+
+    /** The max bytes. */
+	static final int maxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize();
+
+
+    /*
+     * The currentRequest ThreadLocal variable is used to make the currentRequest available to any call in any part of an
+     * application. This enables API's for actions that require the request to be much simpler. For example, the logout()
+     * method in the Authenticator class requires the currentRequest to get the session in order to invalidate it.
+     */
+    private ThreadLocalRequest currentRequest = new ThreadLocalRequest();
+
+	/*
+     * The currentResponse ThreadLocal variable is used to make the currentResponse available to any call in any part of an
+     * application. This enables API's for actions that require the response to be much simpler. For example, the logout()
+     * method in the Authenticator class requires the currentResponse to kill the Session ID cookie.
+     */
+    private ThreadLocalResponse currentResponse = new ThreadLocalResponse();
+
+
+
+	/**
+     * No arg constructor.
+     */
+    public DefaultHTTPUtilities() {
+	}
+
+
+	/**
+	 * {@inheritDoc}
+     * This implementation uses a custom "set-cookie" header rather than Java's
+     * cookie interface which doesn't allow the use of HttpOnly. Configure the
+     * HttpOnly and Secure settings in ESAPI.properties.
+	 */
+	public void addCookie( Cookie cookie ) {
+		addCookie( getCurrentResponse(), cookie );
+    }
+
+    /**
+	 * {@inheritDoc}
+     * This implementation uses a custom "set-cookie" header rather than Java's
+     * cookie interface which doesn't allow the use of HttpOnly. Configure the
+     * HttpOnly and Secure settings in ESAPI.properties.
+	 */
+    public void addCookie(HttpServletResponse response, Cookie cookie) {
+        String name = cookie.getName();
+        String value = cookie.getValue();
+        int maxAge = cookie.getMaxAge();
+        String domain = cookie.getDomain();
+        String path = cookie.getPath();
+        boolean secure = cookie.getSecure();
+
+        // validate the name and value
+        ValidationErrorList errors = new ValidationErrorList();
+        String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors);
+        String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", 5000, false, errors);
+
+        // if there are no errors, then set the cookie either with a header or normally
+        if (errors.size() == 0) {
+        	if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) {
+	            String header = createCookieHeader(cookieName, cookieValue, maxAge, domain, path, secure);
+	            addHeader(response, "Set-Cookie", header);
+        	} else {
+                // Issue 23 - If the ESAPI Configuration is set to force secure cookies, force the secure flag on the cookie before setting it
+                cookie.setSecure( secure || ESAPI.securityConfiguration().getForceSecureCookies() );
+        		response.addCookie(cookie);
+        	}
+            return;
+        }
+        logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (skip mode). Skipping cookie and continuing.");
+    }
+
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String addCSRFToken(String href) {
+		User user = ESAPI.authenticator().getCurrentUser();
+		if (user.isAnonymous()) {
+			return href;
+		}
+
+		// if there are already parameters append with &, otherwise append with ?
+		String token = CSRF_TOKEN_NAME + "=" + user.getCSRFToken();
+		return href.indexOf( '?') != -1 ? href + "&" + token : href + "?" + token;
+	}
+
+    /**
+	 * {@inheritDoc}
+     */
+    public void addHeader(String name, String value) {
+    	addHeader( getCurrentResponse(), name, value );
+    }
+
+    /**
+	 * {@inheritDoc}
+     */
+    public void addHeader(HttpServletResponse response, String name, String value) {
+        try {
+            String strippedName = StringUtilities.replaceLinearWhiteSpace(name);
+            String strippedValue = StringUtilities.replaceLinearWhiteSpace(value);
+            String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", 500, false);
+            response.addHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e);
+        }
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertSecureChannel() throws AccessControlException {
+    	assertSecureChannel( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * This implementation ignores the built-in isSecure() method
+	 * and uses the URL to determine if the request was transmitted over SSL.
+	 * This is because SSL may have been terminated somewhere outside the
+	 * container.
+	 */
+	public void assertSecureChannel(HttpServletRequest request) throws AccessControlException {
+	    if ( request == null ) {
+	    	throw new AccessControlException( "Insecure request received", "HTTP request was null" );
+	    }
+	    StringBuffer sb = request.getRequestURL();
+	    if ( sb == null ) {
+	    	throw new AccessControlException( "Insecure request received", "HTTP request URL was null" );
+	    }
+	    String url = sb.toString();
+	    if ( !url.startsWith( "https" ) ) {
+	    	throw new AccessControlException( "Insecure request received", "HTTP request did not use SSL" );
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertSecureRequest() throws AccessControlException {
+    	assertSecureRequest( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     */
+	public void assertSecureRequest(HttpServletRequest request) throws AccessControlException {
+		assertSecureChannel( request );
+		String receivedMethod = request.getMethod();
+		String requiredMethod = "POST";
+		if ( !receivedMethod.equals( requiredMethod ) ) {
+			throw new AccessControlException( "Insecure request received", "Received request using " + receivedMethod + " when only " + requiredMethod + " is allowed" );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public HttpSession changeSessionIdentifier() throws AuthenticationException {
+    	return changeSessionIdentifier( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     */
+	public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException {
+
+		// get the current session
+		HttpSession oldSession = request.getSession();
+
+		// make a copy of the session content
+		Map<String,Object> temp = new ConcurrentHashMap<String,Object>();
+		Enumeration e = oldSession.getAttributeNames();
+		while (e != null && e.hasMoreElements()) {
+			String name = (String) e.nextElement();
+			Object value = oldSession.getAttribute(name);
+			temp.put(name, value);
+		}
+
+		// kill the old session and create a new one
+		oldSession.invalidate();
+		HttpSession newSession = request.getSession();
+		User user = ESAPI.authenticator().getCurrentUser();
+		user.addSession( newSession );
+		user.removeSession( oldSession );
+
+		// copy back the session content
+      for (Map.Entry<String, Object> stringObjectEntry : temp.entrySet())
+      {
+         newSession.setAttribute(stringObjectEntry.getKey(), stringObjectEntry.getValue());
+		}
+		return newSession;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void clearCurrent() {
+		currentRequest.set(null);
+		currentResponse.set(null);
+	}
+
+	private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure) {
+        // create the special cookie header instead of creating a Java cookie
+        // Set-Cookie:<name>=<value>[; <name>=<value>][; expires=<date>][;
+        // domain=<domain_name>][; path=<some_path>][; secure][;HttpOnly]
+        String header = name + "=" + value;
+        header += "; Max-Age=" + maxAge;
+        if (domain != null) {
+            header += "; Domain=" + domain;
+        }
+        if (path != null) {
+            header += "; Path=" + path;
+        }
+        if ( secure || ESAPI.securityConfiguration().getForceSecureCookies() ) {
+            header += "; Secure";
+        }
+        if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) {
+            header += "; HttpOnly";
+        }
+        return header;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String decryptHiddenField(String encrypted) {
+    	try {
+    		return decryptString(encrypted);
+    	} catch( EncryptionException e ) {
+    		throw new IntrusionException("Invalid request","Tampering detected. Hidden field data did not decrypt properly.", e);
+    	}
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Map<String,String> decryptQueryString(String encrypted) throws EncryptionException {
+        String plaintext = decryptString(encrypted);
+		return queryToMap(plaintext);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Map<String,String> decryptStateFromCookie() throws EncryptionException {
+		return decryptStateFromCookie( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param request
+     */
+    public Map<String,String> decryptStateFromCookie(HttpServletRequest request) throws EncryptionException {
+    	try {
+    		String encrypted = getCookie( request, ESAPI_STATE );
+    		if ( encrypted == null ) return new HashMap<String,String>();
+    		String plaintext = decryptString(encrypted);
+    		return queryToMap( plaintext );
+    	} catch( ValidationException e ) {
+        	return null;
+    	}
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encryptHiddenField(String value) throws EncryptionException {
+    	return encryptString(value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encryptQueryString(String query) throws EncryptionException {
+	    return encryptString(query);
+	}
+
+	/**
+	 * {@inheritDoc}
+     */
+    public void encryptStateInCookie(HttpServletResponse response, Map<String,String> cleartext) throws EncryptionException {
+    	StringBuilder sb = new StringBuilder();
+    	Iterator i = cleartext.entrySet().iterator();
+    	while ( i.hasNext() ) {
+    		try {
+	    		Map.Entry entry = (Map.Entry)i.next();
+	    		
+	    		    // What do these need to be URL encoded? They are encrypted!
+	    		String name = ESAPI.encoder().encodeForURL( entry.getKey().toString() );
+	    		String value = ESAPI.encoder().encodeForURL( entry.getValue().toString() );
+             sb.append(name).append("=").append(value);
+	    		if ( i.hasNext() ) sb.append( "&" );
+    		} catch( EncodingException e ) {
+    			logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry", e );
+    		}
+    	}
+
+		String encrypted = encryptString(sb.toString());
+
+		if ( encrypted.length() > (MAX_COOKIE_LEN ) ) {
+			logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry");
+			throw new EncryptionException("Encryption failure", "Encrypted cookie state of " + encrypted.length() + " longer than allowed " + MAX_COOKIE_LEN );
+		}
+
+    	Cookie cookie = new Cookie( ESAPI_STATE, encrypted );
+    	addCookie( response, cookie );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void encryptStateInCookie( Map<String,String> cleartext ) throws EncryptionException {
+		encryptStateInCookie( getCurrentResponse(), cleartext );
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getCookie( HttpServletRequest request, String name ) throws ValidationException {
+        Cookie c = getFirstCookie( request, name );
+        if ( c == null ) return null;
+		String value = c.getValue();
+		return ESAPI.validator().getValidInput("HTTP cookie value: " + value, value, "HTTPCookieValue", 1000, false);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public String getCookie( String name ) throws ValidationException {
+    	return getCookie( getCurrentRequest(), name );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getCSRFToken() {
+		User user = ESAPI.authenticator().getCurrentUser();
+		if (user == null) return null;
+		return user.getCSRFToken();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public HttpServletRequest getCurrentRequest() {
+    	return currentRequest.getRequest();
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public HttpServletResponse getCurrentResponse() {
+        return currentResponse.getResponse();
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public List<File> getFileUploads() throws ValidationException {
+    	return getFileUploads( getCurrentRequest(), ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions() );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public List<File> getFileUploads(HttpServletRequest request) throws ValidationException {
+    	return getFileUploads(request, ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions());
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public List<File> getFileUploads(HttpServletRequest request, File finalDir ) throws ValidationException {
+    	return getFileUploads(request, finalDir, ESAPI.securityConfiguration().getAllowedFileExtensions());
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public List<File> getFileUploads(HttpServletRequest request, File finalDir, List allowedExtensions) throws ValidationException {
+        File tempDir = ESAPI.securityConfiguration().getUploadTempDirectory();
+		if ( !tempDir.exists() ) {
+		    if ( !tempDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create temp directory: " + tempDir.getAbsolutePath() );
+		}
+
+		if( finalDir != null){
+			if ( !finalDir.exists() ) {
+				if ( !finalDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + finalDir.getAbsolutePath() );
+			}
+		}
+		else {
+			if ( !ESAPI.securityConfiguration().getUploadDirectory().exists()) {
+				if ( !ESAPI.securityConfiguration().getUploadDirectory().mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + ESAPI.securityConfiguration().getUploadDirectory().getAbsolutePath() );
+			}
+			finalDir = ESAPI.securityConfiguration().getUploadDirectory();
+		}
+
+		List<File> newFiles = new ArrayList<File>();
+		try {
+			final HttpSession session = request.getSession(false);
+			if (!ServletFileUpload.isMultipartContent(request)) {
+				throw new ValidationUploadException("Upload failed", "Not a multipart request");
+			}
+
+			// this factory will store ALL files in the temp directory,
+			// regardless of size
+			DiskFileItemFactory factory = new DiskFileItemFactory(0, tempDir);
+			ServletFileUpload upload = new ServletFileUpload(factory);
+			upload.setSizeMax(maxBytes);
+
+			// Create a progress listener
+			ProgressListener progressListener = new ProgressListener() {
+				private long megaBytes = -1;
+				private long progress = 0;
+
+				public void update(long pBytesRead, long pContentLength, int pItems) {
+					if (pItems == 0)
+						return;
+					long mBytes = pBytesRead / 1000000;
+					if (megaBytes == mBytes)
+						return;
+					megaBytes = mBytes;
+					progress = (long) (((double) pBytesRead / (double) pContentLength) * 100);
+					if ( session != null ) {
+					    session.setAttribute("progress", Long.toString(progress));
+					}
+					// logger.logSuccess(Logger.SECURITY, "   Item " + pItems + " (" + progress + "% of " + pContentLength + " bytes]");
+				}
+			};
+			upload.setProgressListener(progressListener);
+
+			List<FileItem> items = upload.parseRequest(request);
+         for (FileItem item : items)
+         {
+            if (!item.isFormField() && item.getName() != null && !(item.getName().equals("")))
+            {
+					String[] fparts = item.getName().split("[\\/\\\\]");
+					String filename = fparts[fparts.length - 1];
+
+               if (!ESAPI.validator().isValidFileName("upload", filename, allowedExtensions, false))
+               {
+						throw new ValidationUploadException("Upload only simple filenames with the following extensions " + allowedExtensions, "Upload failed isValidFileName check");
+					}
+
+					logger.info(Logger.SECURITY_SUCCESS, "File upload requested: " + filename);
+					File f = new File(finalDir, filename);
+               if (f.exists())
+               {
+						String[] parts = filename.split("\\/.");
+						String extension = "";
+                  if (parts.length > 1)
+                  {
+							extension = parts[parts.length - 1];
+						}
+						String filenm = filename.substring(0, filename.length() - extension.length());
+						f = File.createTempFile(filenm, "." + extension, finalDir);
+					}
+					item.write(f);
+               newFiles.add(f);
+					// delete temporary file
+					item.delete();
+					logger.fatal(Logger.SECURITY_SUCCESS, "File successfully uploaded: " + f);
+               if (session != null)
+               {
+					    session.setAttribute("progress", Long.toString(0));
+					}
+				}
+			}
+		} catch (Exception e) {
+			if (e instanceof ValidationUploadException) {
+				throw (ValidationException)e;
+			}
+			throw new ValidationUploadException("Upload failure", "Problem during upload:" + e.getMessage(), e);
+		}
+		return Collections.synchronizedList(newFiles);
+	}
+
+
+
+	/**
+     * Utility to return the first cookie matching the provided name.
+     * @param request
+     * @param name
+     */
+	private Cookie getFirstCookie(HttpServletRequest request, String name) {
+		Cookie[] cookies = request.getCookies();
+		if (cookies != null) {
+         for (Cookie cookie : cookies)
+         {
+            if (cookie.getName().equals(name))
+            {
+					return cookie;
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getHeader( HttpServletRequest request, String name ) throws ValidationException {
+        String value = request.getHeader(name);
+        return ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, false);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public String getHeader( String name ) throws ValidationException {
+    	return getHeader( getCurrentRequest(), name );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public String getParameter( HttpServletRequest request, String name ) throws ValidationException {
+	    String value = request.getParameter(name);
+	    return ESAPI.validator().getValidInput("HTTP parameter value: " + value, value, "HTTPParameterValue", 2000, true);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public String getParameter( String name ) throws ValidationException {
+    	return getParameter( getCurrentRequest(), name );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void killAllCookies() {
+    	killAllCookies( getCurrentRequest(), getCurrentResponse() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param request
+     * @param response
+     */
+	public void killAllCookies(HttpServletRequest request, HttpServletResponse response) {
+		Cookie[] cookies = request.getCookies();
+		if (cookies != null) {
+         for (Cookie cookie : cookies)
+         {
+				killCookie(request, response, cookie.getName());
+			}
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param request
+     * @param response
+     * @param name
+     */
+	public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) {
+		String path = "//";
+		String domain="";
+		Cookie cookie = getFirstCookie(request, name);
+		if ( cookie != null ) {
+			path = cookie.getPath();
+			domain = cookie.getDomain();
+		}
+		Cookie deleter = new Cookie( name, "deleted" );
+		deleter.setMaxAge( 0 );
+		if ( domain != null ) deleter.setDomain( domain );
+		if ( path != null ) deleter.setPath( path );
+		response.addCookie( deleter );
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void killCookie( String name ) {
+    	killCookie( getCurrentRequest(), getCurrentResponse(), name );
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void logHTTPRequest() {
+    	logHTTPRequest( getCurrentRequest(), logger, null );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void logHTTPRequest(HttpServletRequest request, Logger logger) {
+    	logHTTPRequest( request, logger, null );
+    }
+
+		/**
+		 * Formats an HTTP request into a log suitable string. This implementation logs the remote host IP address (or
+		 * hostname if available), the request method (GET/POST), the URL, and all the querystring and form parameters. All
+		 * the parameters are presented as though they were in the URL even if they were in a form. Any parameters that
+		 * match items in the parameterNamesToObfuscate are shown as eight asterisks.
+		 *
+		 *
+		 * @param request
+		 */
+		public void logHTTPRequest(HttpServletRequest request, Logger logger, List parameterNamesToObfuscate) {
+			StringBuilder params = new StringBuilder();
+		    Iterator i = request.getParameterMap().keySet().iterator();
+		    while (i.hasNext()) {
+		        String key = (String) i.next();
+		        String[] value = (String[]) request.getParameterMap().get(key);
+		        for (int j = 0; j < value.length; j++) {
+                 params.append(key).append("=");
+		            if (parameterNamesToObfuscate != null && parameterNamesToObfuscate.contains(key)) {
+		                params.append("********");
+		            } else {
+		                params.append(value[j]);
+		            }
+		            if (j < value.length - 1) {
+		                params.append("&");
+		            }
+		        }
+		        if (i.hasNext())
+		            params.append("&");
+		    }
+		    Cookie[] cookies = request.getCookies();
+		    if ( cookies != null ) {
+             for (Cookie cooky : cookies)
+             {
+                if (!cooky.getName().equals(ESAPI.securityConfiguration().getHttpSessionIdName())) 
+                {
+                   params.append("+").append(cooky.getName()).append("=").append(cooky.getValue());
+		                    }
+		            }
+		    }
+		    String msg = request.getMethod() + " " + request.getRequestURL() + (params.length() > 0 ? "?" + params : "");
+		    logger.info(Logger.SECURITY_SUCCESS, msg);
+		}
+
+	private Map<String,String> queryToMap(String query) {
+		TreeMap<String,String> map = new TreeMap<String,String>();
+		String[] parts = query.split("&");
+      for (String part : parts)
+      {
+         try
+         {
+            String[] nvpair = part.split("=");
+				String name = ESAPI.encoder().decodeFromURL(nvpair[0]);
+				String value = ESAPI.encoder().decodeFromURL(nvpair[1]);
+            map.put(name, value);
+         }
+         catch (EncodingException e)
+         {
+				// skip the nvpair with the encoding problem - note this is already logged.
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * This implementation simply checks to make sure that the forward location starts with "WEB-INF" and
+	 * is intended for use in frameworks that forward to JSP files inside the WEB-INF folder.
+	 */
+	public void sendForward(HttpServletRequest request, HttpServletResponse response, String location) throws AccessControlException,ServletException,IOException {
+		if (!location.startsWith("WEB-INF")) {
+			throw new AccessControlException("Forward failed", "Bad forward location: " + location);
+		}
+		RequestDispatcher dispatcher = request.getRequestDispatcher(location);
+		dispatcher.forward( request, response );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void sendForward( String location )  throws AccessControlException,ServletException,IOException {
+    	sendForward( getCurrentRequest(), getCurrentResponse(), location);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * This implementation checks against the list of safe redirect locations defined in ESAPI.properties.
+     *
+     * @param response
+     */
+    public void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException {
+        if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location);
+            throw new IOException("Redirect failed");
+        }
+        response.sendRedirect(location);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void sendRedirect( String location )  throws AccessControlException,IOException {
+    	sendRedirect( getCurrentResponse(), location);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void setContentType() {
+    	setContentType( getCurrentResponse() );
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setContentType(HttpServletResponse response) {
+		response.setContentType((ESAPI.securityConfiguration()).getResponseContentType());
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setCurrentHTTP(HttpServletRequest request, HttpServletResponse response) {
+     	currentRequest.setRequest(request);
+        currentResponse.setResponse(response);
+    }
+
+    /**
+	 * {@inheritDoc}
+     */
+    public void setHeader(HttpServletResponse response, String name, String value) {
+        try {
+            String strippedName = StringUtilities.replaceLinearWhiteSpace(name);
+            String strippedValue = StringUtilities.replaceLinearWhiteSpace(value);
+            String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", 500, false);
+            response.setHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e);
+        }
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void setHeader( String name, String value ) {
+    	setHeader( getCurrentResponse(), name, value );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setNoCacheHeaders() {
+    	setNoCacheHeaders( getCurrentResponse() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param response
+     */
+	public void setNoCacheHeaders(HttpServletResponse response) {
+		// HTTP 1.1
+		response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
+
+		// HTTP 1.0
+		response.setHeader("Pragma","no-cache");
+		response.setDateHeader("Expires", -1);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * Save the user's remember me data in an encrypted cookie and send it to the user.
+	 * Any old remember me cookie is destroyed first. Setting this cookie will keep the user
+	 * logged in until the maxAge passes, the password is changed, or the cookie is deleted.
+	 * If the cookie exists for the current user, it will automatically be used by ESAPI to
+	 * log the user in, if the data is valid and not expired.
+     *
+     * @param request
+     * @param response
+     */
+	public String setRememberToken( HttpServletRequest request, HttpServletResponse response, String password, int maxAge, String domain, String path ) {
+		User user = ESAPI.authenticator().getCurrentUser();
+		try {
+			killCookie(request, response, REMEMBER_TOKEN_COOKIE_NAME );
+			// seal already contains random data
+			String clearToken = user.getAccountName() + "|" + password;
+			long expiry = ESAPI.encryptor().getRelativeTimeStamp(maxAge * 1000);
+			String cryptToken = ESAPI.encryptor().seal(clearToken, expiry);
+
+            // Do NOT URLEncode cryptToken before creating cookie. See Google Issue # 144,
+			// which was marked as "WontFix".
+
+			Cookie cookie = new Cookie( REMEMBER_TOKEN_COOKIE_NAME, cryptToken );
+			cookie.setMaxAge( maxAge );
+			cookie.setDomain( domain );
+			cookie.setPath( path );
+			response.addCookie( cookie );
+			logger.info(Logger.SECURITY_SUCCESS, "Enabled remember me token for " + user.getAccountName() );
+			return cryptToken;
+		} catch( IntegrityException e ) {
+			logger.warning(Logger.SECURITY_FAILURE, "Attempt to set remember me token failed for " + user.getAccountName(), e );
+			return null;
+		}
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String setRememberToken( String password, int maxAge, String domain, String path ) {
+    	return setRememberToken( getCurrentRequest(), getCurrentResponse(), password, maxAge, domain, path );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public void verifyCSRFToken() throws IntrusionException {
+    	verifyCSRFToken( getCurrentRequest() );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 *
+	 * This implementation uses the CSRF_TOKEN_NAME parameter for the token.
+     *
+     * @param request
+     */
+	public void verifyCSRFToken(HttpServletRequest request) throws IntrusionException {
+		User user = ESAPI.authenticator().getCurrentUser();
+
+		// check if user authenticated with this request - no CSRF protection required
+		if( request.getAttribute(user.getCSRFToken()) != null ) {
+			return;
+		}
+		String token = request.getParameter(CSRF_TOKEN_NAME);
+		if ( !user.getCSRFToken().equals( token ) ) {
+			throw new IntrusionException("Authentication failed", "Possibly forged HTTP request without proper CSRF token detected");
+		}
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getSessionAttribute( String key ) {
+        final HttpSession session = ESAPI.currentRequest().getSession(false);
+        if ( session != null )
+            return (T) session.getAttribute(key);
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getSessionAttribute(HttpSession session, String key)
+    {
+        return (T) session.getAttribute(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getRequestAttribute(String key)
+    {
+        return (T)  ESAPI.currentRequest().getAttribute(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getRequestAttribute(HttpServletRequest request, String key)
+    {
+        return (T) request.getAttribute( key );
+    }
+    
+    /////////////////////
+    
+    /* Helper method to encrypt using new Encryptor encryption methods and
+     * return the serialized ciphertext as a hex-encoded string.
+     */
+    private String encryptString(String plaintext) throws EncryptionException {
+        PlainText pt = new PlainText(plaintext);
+        CipherText ct = ESAPI.encryptor().encrypt(pt);
+        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+        return Hex.encode(serializedCiphertext, false);
+    }
+    
+    /* Helper method to decrypt a hex-encode serialized ciphertext string and
+     * to decrypt it using the new Encryptor decryption methods.
+     */
+    private String decryptString(String ciphertext) throws EncryptionException {
+        byte[] serializedCiphertext = Hex.decode(ciphertext);
+        CipherText restoredCipherText =
+            CipherText.fromPortableSerializedBytes(serializedCiphertext);
+        PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
+        return plaintext.toString();
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultIntrusionDetector.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultIntrusionDetector.java.svn-base
new file mode 100644
index 0000000..2a71e14
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultIntrusionDetector.java.svn-base
@@ -0,0 +1,193 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.SecurityConfiguration.Threshold;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.IntrusionException;
+
+/**
+ * Reference implementation of the IntrusionDetector interface. This
+ * implementation monitors EnterpriseSecurityExceptions to see if any user
+ * exceeds a configurable threshold in a configurable time period. For example,
+ * it can monitor to see if a user exceeds 10 input validation issues in a 1
+ * minute period. Or if there are more than 3 authentication problems in a 10
+ * second period. More complex implementations are certainly possible, such as
+ * one that establishes a baseline of expected behavior, and then detects
+ * deviations from that baseline. This implementation stores state in the
+ * user's session, so that it will be properly cleaned up when the session is
+ * terminated. State is not otherwise persisted, so attacks that span sessions
+ * will not be detectable.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.IntrusionDetector
+ */
+public class DefaultIntrusionDetector implements org.owasp.esapi.IntrusionDetector {
+
+	/** The logger. */
+	private final Logger logger = ESAPI.getLogger("IntrusionDetector");
+
+    public DefaultIntrusionDetector() {
+	}
+	
+	/**
+	 * {@inheritDoc}
+     *
+     * @param e
+     */
+	public void addException(Exception e) {
+		if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+		
+        if ( e instanceof EnterpriseSecurityException ) {
+            logger.warning( Logger.SECURITY_FAILURE, ((EnterpriseSecurityException)e).getLogMessage(), e );
+        } else {
+            logger.warning( Logger.SECURITY_FAILURE, e.getMessage(), e );
+        }
+
+        // add the exception to the current user, which may trigger a detector 
+		User user = ESAPI.authenticator().getCurrentUser();
+        String eventName = e.getClass().getName();
+
+        if ( e instanceof IntrusionException) {
+            return;
+        }
+        
+        // add the exception to the user's store, handle IntrusionException if thrown
+		try {
+			addSecurityEvent(user, eventName);
+		} catch( IntrusionException ex ) {
+            Threshold quota = ESAPI.securityConfiguration().getQuota(eventName);
+            Iterator i = quota.actions.iterator();
+            while ( i.hasNext() ) {
+                String action = (String)i.next();
+                String message = "User exceeded quota of " + quota.count + " per "+ quota.interval +" seconds for event " + eventName + ". Taking actions " + quota.actions;
+                takeSecurityAction( action, message );
+            }
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void addEvent(String eventName, String logMessage) throws IntrusionException {
+    	if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+    	
+        logger.warning( Logger.SECURITY_FAILURE, "Security event " + eventName + " received : " + logMessage );
+
+        // add the event to the current user, which may trigger a detector 
+        User user = ESAPI.authenticator().getCurrentUser();
+        try {
+            addSecurityEvent(user, "event." + eventName);
+        } catch( IntrusionException ex ) {
+            Threshold quota = ESAPI.securityConfiguration().getQuota("event." + eventName);
+            Iterator i = quota.actions.iterator();
+            while ( i.hasNext() ) {
+                String action = (String)i.next();
+                String message = "User exceeded quota of " + quota.count + " per "+ quota.interval +" seconds for event " + eventName + ". Taking actions " + quota.actions;
+                takeSecurityAction( action, message );
+            }
+        }
+    }
+
+    /**
+     * Take a specified security action.  In this implementation, acceptable
+     * actions are: log, disable, logout.
+     * 
+     * @param action
+     * 		the action to take (log, disable, logout)
+     * @param message
+     * 		the message to log if the action is "log"
+     */
+    private void takeSecurityAction( String action, String message ) {
+    	if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+    	
+        if ( action.equals( "log" ) ) {
+            logger.fatal( Logger.SECURITY_FAILURE, "INTRUSION - " + message );
+        }
+        User user = ESAPI.authenticator().getCurrentUser();
+        if (user == User.ANONYMOUS)
+        	return;
+        if ( action.equals( "disable" ) ) {
+            user.disable();
+        }
+        if ( action.equals( "logout" ) ) {
+            user.logout();
+        }
+    }
+
+	 /**
+	 * Adds a security event to the user.  These events are used to check that the user has not
+	 * reached the security thresholds set in the properties file.
+	 * 
+	 * @param user
+	 * 			The user that caused the event.
+	 * @param eventName
+	 * 			The name of the event that occurred.
+	 */
+	private void addSecurityEvent(User user, String eventName) {
+		if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+		
+		if ( user.isAnonymous() ) return;
+		
+		HashMap eventMap = user.getEventMap();
+		
+		// if there is a threshold, then track this event
+		Threshold threshold = ESAPI.securityConfiguration().getQuota( eventName );
+		if ( threshold != null ) {
+			Event event = (Event)eventMap.get( eventName );
+			if ( event == null ) {
+				event = new Event( eventName );
+				eventMap.put( eventName, event );
+			}
+			// increment
+			event.increment(threshold.count, threshold.interval);
+		}
+	}
+
+    private static class Event {
+        public String key;
+        public Stack times = new Stack();
+        //public long count = 0;
+        public Event( String key ) {
+            this.key = key;
+        }
+        public void increment(int count, long interval) throws IntrusionException {
+        	if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+        	
+            Date now = new Date();
+            times.add( 0, now );
+            while ( times.size() > count ) times.remove( times.size()-1 );
+            if ( times.size() == count ) {
+                Date past = (Date)times.get( count-1 );
+                long plong = past.getTime();
+                long nlong = now.getTime(); 
+                if ( nlong - plong < interval * 1000 ) {
+                    throw new IntrusionException( "Threshold exceeded", "Exceeded threshold for " + key );
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultRandomizer.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultRandomizer.java.svn-base
new file mode 100644
index 0000000..3272a69
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultRandomizer.java.svn-base
@@ -0,0 +1,134 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.UUID;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.Randomizer;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Reference implementation of the Randomizer interface. This implementation builds on the JCE provider to provide a
+ * cryptographically strong source of entropy. The specific algorithm used is configurable in ESAPI.properties.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Randomizer
+ */
+public class DefaultRandomizer implements org.owasp.esapi.Randomizer {
+    private static volatile Randomizer singletonInstance;
+
+    public static Randomizer getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultRandomizer.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultRandomizer();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    /** The sr. */
+    private SecureRandom secureRandom = null;
+
+    /** The logger. */
+    private final Logger logger = ESAPI.getLogger("Randomizer");
+
+    private DefaultRandomizer() {
+        String algorithm = ESAPI.securityConfiguration().getRandomAlgorithm();
+        try {
+            secureRandom = SecureRandom.getInstance(algorithm);
+        } catch (NoSuchAlgorithmException e) {
+            // Can't throw an exception from the constructor, but this will get
+            // it logged and tracked
+            new EncryptionException("Error creating randomizer", "Can't find random algorithm " + algorithm, e);
+        }
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomString(int length, char[] characterSet) {
+    	StringBuilder sb = new StringBuilder();
+        for (int loop = 0; loop < length; loop++) {
+            int index = secureRandom.nextInt(characterSet.length);
+            sb.append(characterSet[index]);
+        }
+        String nonce = sb.toString();
+        return nonce;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getRandomBoolean() {
+        return secureRandom.nextBoolean();
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getRandomInteger(int min, int max) {
+        return secureRandom.nextInt(max - min) + min;
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public long getRandomLong() {
+        return secureRandom.nextLong();    
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public float getRandomReal(float min, float max) {
+        float factor = max - min;
+        return secureRandom.nextFloat() * factor + min;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomFilename(String extension) {
+        String fn = getRandomString(12, EncoderConstants.CHAR_ALPHANUMERICS) + "." + extension;
+        logger.debug(Logger.SECURITY_SUCCESS, "Generated new random filename: " + fn );
+        return fn;
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomGUID() throws EncryptionException {
+    	return UUID.randomUUID().toString();
+    }
+    	
+    /**
+     * {@inheritDoc}
+     */
+    public byte[] getRandomBytes(int n) {
+    	byte[] result = new byte[ n ];
+    	secureRandom.nextBytes(result);
+    	return result;
+    }
+    	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultSecurityConfiguration.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultSecurityConfiguration.java.svn-base
new file mode 100644
index 0000000..c2e3821
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultSecurityConfiguration.java.svn-base
@@ -0,0 +1,1224 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.SecurityConfiguration;
+import org.owasp.esapi.errors.ConfigurationException;
+
+/**
+ * The reference {@code SecurityConfiguration} manages all the settings used by the ESAPI in a single place. In this reference
+ * implementation, resources can be put in several locations, which are searched in the following order:
+ * <p>
+ * 1) Inside a directory set with a call to SecurityConfiguration.setResourceDirectory( "C:\temp\resources" ).
+ * <p>
+ * 2) Inside the System.getProperty( "org.owasp.esapi.resources" ) directory.
+ * You can set this on the java command line as follows (for example):
+ * <pre>
+ * 		java -Dorg.owasp.esapi.resources="C:\temp\resources"
+ * </pre>
+ * You may have to add this to the start-up script that starts your web server. For example, for Tomcat,
+ * in the "catalina" script that starts Tomcat, you can set the JAVA_OPTS variable to the {@code -D} string above.
+ * <p>
+ * 3) Inside the {@code System.getProperty( "user.home" ) + "/.esapi"} directory (supported for backward compatibility) or
+ * inside the {@code System.getProperty( "user.home" ) + "/esapi"} directory.
+ * <p>
+ * 4) The first ".esapi" or "esapi" directory on the classpath. (The former for backward compatibility.)
+ * <p>
+ * Once the Configuration is initialized with a resource directory, you can edit it to set things like master
+ * keys and passwords, logging locations, error thresholds, and allowed file extensions.
+ * <p>
+ * WARNING: Do not forget to update ESAPI.properties to change the master key and other security critical settings.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim .at. manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @author Kevin Wall (kevin.w.wall .at. gmail.com)
+ */
+
+public class DefaultSecurityConfiguration implements SecurityConfiguration {
+    private static volatile SecurityConfiguration instance = null;
+
+    public static SecurityConfiguration getInstance() {
+        if ( instance == null ) {
+            synchronized (DefaultSecurityConfiguration.class) {
+                if ( instance == null ) {
+                    instance = new DefaultSecurityConfiguration();
+                }
+            }
+        }
+        return instance;
+    }
+    
+    private Properties properties = null;
+    private String cipherXformFromESAPIProp = null;	// New in ESAPI 2.0
+    private String cipherXformCurrent = null;		// New in ESAPI 2.0
+
+	/** The name of the ESAPI property file */
+	public static final String RESOURCE_FILE = "ESAPI.properties";
+	
+    public static final String REMEMBER_TOKEN_DURATION = "Authenticator.RememberTokenDuration";
+    public static final String IDLE_TIMEOUT_DURATION = "Authenticator.IdleTimeoutDuration";
+    public static final String ABSOLUTE_TIMEOUT_DURATION = "Authenticator.AbsoluteTimeoutDuration";
+    public static final String ALLOWED_LOGIN_ATTEMPTS = "Authenticator.AllowedLoginAttempts";
+    public static final String USERNAME_PARAMETER_NAME = "Authenticator.UsernameParameterName";
+    public static final String PASSWORD_PARAMETER_NAME = "Authenticator.PasswordParameterName";
+    public static final String MAX_OLD_PASSWORD_HASHES = "Authenticator.MaxOldPasswordHashes";
+
+    public static final String ALLOW_MULTIPLE_ENCODING = "Encoder.AllowMultipleEncoding";
+    public static final String ALLOW_MIXED_ENCODING	= "Encoder.AllowMixedEncoding";
+    public static final String CANONICALIZATION_CODECS = "Encoder.DefaultCodecList";
+
+    public static final String DISABLE_INTRUSION_DETECTION  = "IntrusionDetector.Disable";
+    
+    public static final String MASTER_KEY = "Encryptor.MasterKey";
+    public static final String MASTER_SALT = "Encryptor.MasterSalt";
+    public static final String KEY_LENGTH = "Encryptor.EncryptionKeyLength";
+    public static final String ENCRYPTION_ALGORITHM = "Encryptor.EncryptionAlgorithm";
+    public static final String HASH_ALGORITHM = "Encryptor.HashAlgorithm";
+    public static final String HASH_ITERATIONS = "Encryptor.HashIterations";
+    public static final String CHARACTER_ENCODING = "Encryptor.CharacterEncoding";
+    public static final String RANDOM_ALGORITHM = "Encryptor.RandomAlgorithm";
+    public static final String DIGITAL_SIGNATURE_ALGORITHM = "Encryptor.DigitalSignatureAlgorithm";
+    public static final String DIGITAL_SIGNATURE_KEY_LENGTH = "Encryptor.DigitalSignatureKeyLength";
+    			// ==================================//
+    			//		New in ESAPI Java 2.0		 //
+    			// ================================= //
+    public static final String PREFERRED_JCE_PROVIDER = "Encryptor.PreferredJCEProvider";
+    public static final String CIPHER_TRANSFORMATION_IMPLEMENTATION = "Encryptor.CipherTransformation";
+    public static final String CIPHERTEXT_USE_MAC = "Encryptor.CipherText.useMAC";
+    public static final String PLAINTEXT_OVERWRITE = "Encryptor.PlainText.overwrite";
+    public static final String IV_TYPE = "Encryptor.ChooseIVMethod";
+    public static final String FIXED_IV = "Encryptor.fixedIV";
+    public static final String COMBINED_CIPHER_MODES = "Encryptor.cipher_modes.combined_modes";
+    public static final String ADDITIONAL_ALLOWED_CIPHER_MODES = "Encryptor.cipher_modes.additional_allowed";
+    public static final String KDF_PRF_ALG = "Encryptor.KDF.PRF";
+	public static final String PRINT_PROPERTIES_WHEN_LOADED = "ESAPI.printProperties";
+
+    public static final String WORKING_DIRECTORY = "Executor.WorkingDirectory";
+    public static final String APPROVED_EXECUTABLES = "Executor.ApprovedExecutables";
+
+    public static final String FORCE_HTTPONLYSESSION = "HttpUtilities.ForceHttpOnlySession";
+    public static final String FORCE_SECURESESSION = "HttpUtilities.SecureSession";
+    public static final String FORCE_HTTPONLYCOOKIES = "HttpUtilities.ForceHttpOnlyCookies";
+    public static final String FORCE_SECURECOOKIES = "HttpUtilities.ForceSecureCookies";
+	public static final String MAX_HTTP_HEADER_SIZE = "HttpUtilities.MaxHeaderSize";
+    public static final String UPLOAD_DIRECTORY = "HttpUtilities.UploadDir";
+    public static final String UPLOAD_TEMP_DIRECTORY = "HttpUtilities.UploadTempDir";
+    public static final String APPROVED_UPLOAD_EXTENSIONS = "HttpUtilities.ApprovedUploadExtensions";
+    public static final String MAX_UPLOAD_FILE_BYTES = "HttpUtilities.MaxUploadFileBytes";
+    public static final String RESPONSE_CONTENT_TYPE = "HttpUtilities.ResponseContentType";
+    public static final String HTTP_SESSION_ID_NAME = "HttpUtilities.HttpSessionIdName";
+
+    public static final String APPLICATION_NAME = "Logger.ApplicationName";
+    public static final String LOG_LEVEL = "Logger.LogLevel";
+    public static final String LOG_FILE_NAME = "Logger.LogFileName";
+    public static final String MAX_LOG_FILE_SIZE = "Logger.MaxLogFileSize";
+    public static final String LOG_ENCODING_REQUIRED = "Logger.LogEncodingRequired";
+    public static final String LOG_APPLICATION_NAME = "Logger.LogApplicationName";
+    public static final String LOG_SERVER_IP = "Logger.LogServerIP";
+    public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile";
+    public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates";
+
+
+
+    /**
+	 * The default max log file size is set to 10,000,000 bytes (10 Meg). If the current log file exceeds the current
+	 * max log file size, the logger will move the old log data into another log file. There currently is a max of
+	 * 1000 log files of the same name. If that is exceeded it will presumably start discarding the oldest logs.
+	 */
+	public static final int DEFAULT_MAX_LOG_FILE_SIZE = 10000000;
+	
+    protected final int MAX_REDIRECT_LOCATION = 1000;
+    
+    /**
+     * @deprecated	It is not clear whether this is intended to be the max file name length for the basename(1) of
+     *				a file or the max full path name length of a canonical full path name. Since it is not used anywhere
+     *				in the ESAPI code it is being deprecated and scheduled to be removed in release 2.1.
+     */
+    protected final int MAX_FILE_NAME_LENGTH = 1000;	// DISCUSS: Is this for given directory or refer to canonicalized full path name?
+    													// Too long if the former! (Usually 255 is limit there.) Hard to tell since not used
+    													// here in this class and it's protected, so not sure what it's intent is. It's not
+    													// used anywhere in the ESAPI code base. I am going to deprecate it because of this. -kww
+
+    /*
+     * Implementation Keys
+     */
+    public static final String LOG_IMPLEMENTATION = "ESAPI.Logger";
+    public static final String AUTHENTICATION_IMPLEMENTATION = "ESAPI.Authenticator";
+    public static final String ENCODER_IMPLEMENTATION = "ESAPI.Encoder";
+    public static final String ACCESS_CONTROL_IMPLEMENTATION = "ESAPI.AccessControl";
+    public static final String ENCRYPTION_IMPLEMENTATION = "ESAPI.Encryptor";
+    public static final String INTRUSION_DETECTION_IMPLEMENTATION = "ESAPI.IntrusionDetector";
+    public static final String RANDOMIZER_IMPLEMENTATION = "ESAPI.Randomizer";
+	public static final String EXECUTOR_IMPLEMENTATION = "ESAPI.Executor";
+	public static final String VALIDATOR_IMPLEMENTATION = "ESAPI.Validator";
+	public static final String HTTP_UTILITIES_IMPLEMENTATION = "ESAPI.HTTPUtilities";
+
+    /*
+     * Default Implementations
+     */
+    public static final String DEFAULT_LOG_IMPLEMENTATION = "org.owasp.esapi.reference.JavaLogFactory";
+    public static final String DEFAULT_AUTHENTICATION_IMPLEMENTATION = "org.owasp.esapi.reference.FileBasedAuthenticator";
+    public static final String DEFAULT_ENCODER_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultEncoder";
+    public static final String DEFAULT_ACCESS_CONTROL_IMPLEMENTATION = "org.owasp.esapi.reference.accesscontrol.DefaultAccessController";
+    public static final String DEFAULT_ENCRYPTION_IMPLEMENTATION = "org.owasp.esapi.reference.crypto.JavaEncryptor";
+    public static final String DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultIntrusionDetector";
+    public static final String DEFAULT_RANDOMIZER_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultRandomizer";
+    public static final String DEFAULT_EXECUTOR_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultExecutor";
+    public static final String DEFAULT_HTTP_UTILITIES_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultHTTPUtilities";
+    public static final String DEFAULT_VALIDATOR_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultValidator";
+
+    private static final Map<String, Pattern> patternCache = new HashMap<String, Pattern>();
+
+    /*
+     * Absolute path to the user.home. No longer includes the ESAPI portion as it used to.
+     */
+    private static final String userHome = System.getProperty("user.home" );
+    /*
+     * Absolute path to the customDirectory
+     */	// DISCUSS: Implicit assumption here that there is no SecurityManager installed enforcing the
+        //			prevention of reading system properties. Otherwise this will fail with SecurityException.
+    private static String customDirectory = System.getProperty("org.owasp.esapi.resources");
+    /*
+     * Relative path to the resourceDirectory. Relative to the classpath.
+     * Specifically, ClassLoader.getResource(resourceDirectory + filename) will
+     * be used to load the file.
+     */
+    private String resourceDirectory = ".esapi";	// For backward compatibility (vs. "esapi")
+
+//    private static long lastModified = -1;
+
+    /**
+     * Instantiates a new configuration.
+     */
+    public DefaultSecurityConfiguration() {
+    	// load security configuration
+    	try {
+        	loadConfiguration();
+        	this.setCipherXProperties();
+        } catch( IOException e ) {
+	        logSpecial("Failed to load security configuration", e );
+	        throw new ConfigurationException("Failed to load security configuration", e);
+        }
+    }
+    
+    /**
+     * Instantiates a new configuration with the supplied properties.
+     * 
+     * Warning - if the setResourceDirectory() method is invoked the properties will
+     * be re-loaded, replacing the supplied properties.
+     * 
+     * @param properties
+     */
+    public DefaultSecurityConfiguration(Properties properties) {
+    	super();
+    	this.properties = properties; 
+    	this.setCipherXProperties();
+    }
+    
+    private void setCipherXProperties() {
+		// TODO: FUTURE: Replace by future CryptoControls class???
+		// See SecurityConfiguration.setCipherTransformation() for
+		// explanation of this.
+        // (Propose this in 2.1 via future email to ESAPI-DEV list.)
+		cipherXformFromESAPIProp =
+			getESAPIProperty(CIPHER_TRANSFORMATION_IMPLEMENTATION,
+							 "AES/CBC/PKCS5Padding");
+		cipherXformCurrent = cipherXformFromESAPIProp;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getApplicationName() {
+    	return getESAPIProperty(APPLICATION_NAME, "DefaultName");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getLogImplementation() {
+    	return getESAPIProperty(LOG_IMPLEMENTATION, DEFAULT_LOG_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getAuthenticationImplementation() {
+    	return getESAPIProperty(AUTHENTICATION_IMPLEMENTATION, DEFAULT_AUTHENTICATION_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getEncoderImplementation() {
+    	return getESAPIProperty(ENCODER_IMPLEMENTATION, DEFAULT_ENCODER_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getAccessControlImplementation() {
+    	return getESAPIProperty(ACCESS_CONTROL_IMPLEMENTATION, DEFAULT_ACCESS_CONTROL_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getEncryptionImplementation() {
+    	return getESAPIProperty(ENCRYPTION_IMPLEMENTATION, DEFAULT_ENCRYPTION_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getIntrusionDetectionImplementation() {
+    	return getESAPIProperty(INTRUSION_DETECTION_IMPLEMENTATION, DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomizerImplementation() {
+    	return getESAPIProperty(RANDOMIZER_IMPLEMENTATION, DEFAULT_RANDOMIZER_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getExecutorImplementation() {
+    	return getESAPIProperty(EXECUTOR_IMPLEMENTATION, DEFAULT_EXECUTOR_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getHTTPUtilitiesImplementation() {
+    	return getESAPIProperty(HTTP_UTILITIES_IMPLEMENTATION, DEFAULT_HTTP_UTILITIES_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getValidationImplementation() {
+    	return getESAPIProperty(VALIDATOR_IMPLEMENTATION, DEFAULT_VALIDATOR_IMPLEMENTATION);
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public byte[] getMasterKey() {
+    	byte[] key = getESAPIPropertyEncoded( MASTER_KEY, null );
+    	if ( key == null || key.length == 0 ) {
+    		throw new ConfigurationException("Property '" + MASTER_KEY +
+    							"' missing or empty in ESAPI.properties file.");
+    }
+    	return key;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setResourceDirectory( String dir ) {
+    	resourceDirectory = dir;
+        logSpecial( "Reset resource directory to: " + dir, null );
+
+        // reload configuration if necessary
+    	try {
+    		this.loadConfiguration();
+    	} catch( IOException e ) {
+	        logSpecial("Failed to load security configuration from " + dir, e);
+    	}
+    }
+
+    public int getEncryptionKeyLength() {
+    	return getESAPIProperty(KEY_LENGTH, 128 );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public byte[] getMasterSalt() {
+    	byte[] salt = getESAPIPropertyEncoded( MASTER_SALT, null );
+    	if ( salt == null || salt.length == 0 ) {
+    		throw new ConfigurationException("Property '" + MASTER_SALT +
+    							"' missing or empty in ESAPI.properties file.");
+    }
+    	return salt;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public List<String> getAllowedExecutables() {
+    	String def = "";
+        String[] exList = getESAPIProperty(APPROVED_EXECUTABLES,def).split(",");
+        return Arrays.asList(exList);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public List<String> getAllowedFileExtensions() {
+    	String def = ".zip,.pdf,.tar,.gz,.xls,.properties,.txt,.xml";
+        String[] extList = getESAPIProperty(APPROVED_UPLOAD_EXTENSIONS,def).split(",");
+        return Arrays.asList(extList);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getAllowedFileUploadSize() {
+        return getESAPIProperty(MAX_UPLOAD_FILE_BYTES, 5000000);
+    }
+
+
+    private Properties loadPropertiesFromStream( InputStream is, String name ) throws IOException {
+    	Properties config = new Properties();
+        try {
+	        config.load(is);
+	        logSpecial("Loaded '" + name + "' properties file", null);
+        } finally {
+            if ( is != null ) try { is.close(); } catch( Exception e ) {}
+        }
+        return config;
+    }
+
+	/**
+	 * Load configuration. Never prints properties.
+	 * 
+	 * @throws java.io.IOException
+	 *             if the file is inaccessible
+	 */
+	protected void loadConfiguration() throws IOException {
+		
+		try {
+		    //first attempt file IO loading of properties
+			logSpecial("Attempting to load " + RESOURCE_FILE + " via file I/O.");
+			properties = loadPropertiesFromStream(getResourceStream(RESOURCE_FILE), RESOURCE_FILE);
+			
+		} catch (Exception iae) {
+		    //if file I/O loading fails, attempt classpath based loading next
+		    logSpecial("Loading " + RESOURCE_FILE + " via file I/O failed. Exception was: " + iae);
+			logSpecial("Attempting to load " + RESOURCE_FILE + " via the classpath.");
+			try {
+				properties = loadConfigurationFromClasspath(RESOURCE_FILE);
+			} catch (Exception e) {				
+				logSpecial(RESOURCE_FILE + " could not be loaded by any means. Fail.", e);
+				throw new ConfigurationException(RESOURCE_FILE + " could not be loaded by any means. Fail.", e);
+			}			
+		}
+		
+		// if properties loaded properly above, get validation properties and merge them into the main properties
+		if (properties != null) {
+			
+			String validationPropFileName = getESAPIProperty(VALIDATION_PROPERTIES, "validation.properties");
+			Properties validationProperties = null;
+
+			//clear any cached validation patterns so they can be reloaded from validation.properties
+			patternCache.clear();
+			
+			try {
+			    //first attempt file IO loading of properties
+				logSpecial("Attempting to load " + validationPropFileName + " via file I/O.");
+				validationProperties = loadPropertiesFromStream(getResourceStream(validationPropFileName), validationPropFileName);
+				
+			} catch (Exception iae) {
+			    //if file I/O loading fails, attempt classpath based loading next
+			    logSpecial("Loading " + validationPropFileName + " via file I/O failed.");
+				logSpecial("Attempting to load " + validationPropFileName + " via the classpath.");		
+				try {
+					validationProperties = loadConfigurationFromClasspath(validationPropFileName);
+				} catch (Exception e) {				
+					logSpecial(validationPropFileName + " could not be loaded by any means. fail.", e);
+				}			
+			}
+			
+			if (validationProperties != null) {
+		    	Iterator<?> i = validationProperties.keySet().iterator();
+		    	while( i.hasNext() ) {
+		    		String key = (String)i.next();
+		    		String value = validationProperties.getProperty(key);
+		    		properties.put( key, value);
+		    	}
+			}
+			
+	        if ( shouldPrintProperties() ) {
+	    	
+	    	//FIXME - make this chunk configurable
+	    	/*
+	        logSpecial("  ========Master Configuration========", null);
+	        //logSpecial( "  ResourceDirectory: " + DefaultSecurityConfiguration.resourceDirectory );
+	        Iterator j = new TreeSet( properties.keySet() ).iterator();
+	        while (j.hasNext()) {
+	            String key = (String)j.next();
+	            // print out properties, but not sensitive ones like MasterKey and MasterSalt
+	            if ( !key.contains( "Master" ) ) {
+	            		logSpecial("  |   " + key + "=" + properties.get(key), null);
+	        	}
+	        }
+	        */
+	        	
+	        }
+		}
+	}	
+	
+	/**
+	 * @param filename
+	 * @return An {@code InputStream} associated with the specified file name as
+	 *         a resource stream.
+	 * @throws IOException
+	 *             If the file cannot be found or opened for reading.
+	 */
+	public InputStream getResourceStream(String filename) throws IOException {
+		if (filename == null) {
+			return null;
+		}
+
+		try {
+			File f = getResourceFile(filename);
+			if (f != null && f.exists()) {
+				return new FileInputStream(f);
+			}
+		} catch (Exception e) {
+		}
+
+		throw new FileNotFoundException();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public File getResourceFile(String filename) {
+		logSpecial("Attempting to load " + filename + " as resource file via file I/O.");
+
+		if (filename == null) {
+			logSpecial("Failed to load properties via FileIO. Filename is null.");
+			return null; // not found.
+		}
+
+		File f = null;
+
+		// first, allow command line overrides. -Dorg.owasp.esapi.resources
+		// directory
+		f = new File(customDirectory, filename);
+		if (customDirectory != null && f.canRead()) {
+			logSpecial("Found in 'org.owasp.esapi.resources' directory: " + f.getAbsolutePath());
+			return f;
+		} else {
+			logSpecial("Not found in 'org.owasp.esapi.resources' directory or file not readable: " + f.getAbsolutePath());
+		}
+
+		// if not found, then try the programmatically set resource directory
+		// (this defaults to SystemResource directory/RESOURCE_FILE
+		URL fileUrl = ClassLoader.getSystemResource(resourceDirectory + "/" + filename);
+        if ( fileUrl == null ) {
+            fileUrl = ClassLoader.getSystemResource("esapi/" + filename);
+        }
+
+		if (fileUrl != null) {
+			String fileLocation = fileUrl.getFile();
+			f = new File(fileLocation);
+			if (f.exists()) {
+				logSpecial("Found in SystemResource Directory/resourceDirectory: " + f.getAbsolutePath());
+				return f;
+			} else {
+				logSpecial("Not found in SystemResource Directory/resourceDirectory (this should never happen): " + f.getAbsolutePath());
+			}
+		} else {
+			logSpecial("Not found in SystemResource Directory/resourceDirectory: " + resourceDirectory + File.separator + filename);
+		}
+
+		// If not found, then try immediately under user's home directory first in
+		//		userHome + "/.esapi"		and secondly under
+		//		userHome + "/esapi"
+		// We look in that order because of backward compatibility issues.
+		String homeDir = userHome;
+		if ( homeDir == null ) {
+			homeDir = "";	// Without this,	homeDir + "/.esapi"	would produce
+							// the string		"null/.esapi"		which surely is not intended.
+		}
+		// First look under ".esapi" (for reasons of backward compatibility).
+		f = new File(homeDir + "/.esapi", filename);
+		if ( f.canRead() ) {
+			logSpecial("[Compatibility] Found in 'user.home' directory: " + f.getAbsolutePath());
+			return f;
+		} else {
+			// Didn't find it under old directory ".esapi" so now look under the "esapi" directory.
+			f = new File(homeDir + "/esapi", filename);
+			if ( f.canRead() ) {
+				logSpecial("Found in 'user.home' directory: " + f.getAbsolutePath());
+				return f;
+			} else {
+				logSpecial("Not found in 'user.home' (" + homeDir + ") directory: " + f.getAbsolutePath());
+			}
+		}
+
+		// return null if not found
+		return null;
+	}
+	
+    /**
+     * Used to load ESAPI.properties from a variety of different classpath locations.
+     *
+     * @param fileName The properties file filename.
+     */
+	private Properties loadConfigurationFromClasspath(String fileName) throws IllegalArgumentException {
+		Properties result = null;
+		InputStream in = null;
+
+		ClassLoader[] loaders = new ClassLoader[] {
+				Thread.currentThread().getContextClassLoader(),
+				ClassLoader.getSystemClassLoader(),
+				getClass().getClassLoader() 
+		};
+		String[] classLoaderNames = {
+				"current thread context class loader",
+				"system class loader",
+				"class loader for DefaultSecurityConfiguration class"
+		};
+
+		ClassLoader currentLoader = null;
+		for (int i = 0; i < loaders.length; i++) {
+			if (loaders[i] != null) {
+				currentLoader = loaders[i];
+				try {
+					// try root
+					String currentClasspathSearchLocation = "/ (root)";
+					in = loaders[i].getResourceAsStream(fileName);
+					
+					// try resourceDirectory folder
+					if (in == null) {
+						currentClasspathSearchLocation = resourceDirectory + "/";
+						in = currentLoader.getResourceAsStream(resourceDirectory + "/" + fileName);
+					}
+
+					// try .esapi folder. Look here first for backward compatibility.
+					if (in == null) {
+						currentClasspathSearchLocation = ".esapi/";
+						in = currentLoader.getResourceAsStream(".esapi/" + fileName);
+					} 
+					
+					// try esapi folder (new directory)
+					if (in == null) {
+						currentClasspathSearchLocation = "esapi/";
+						in = currentLoader.getResourceAsStream("esapi/" + fileName);
+					} 
+					
+					// try resources folder
+					if (in == null) {
+						currentClasspathSearchLocation = "resources/";
+						in = currentLoader.getResourceAsStream("resources/" + fileName);
+					}
+		
+					// now load the properties
+					if (in != null) {
+						result = new Properties();
+						result.load(in); // Can throw IOException
+						logSpecial("SUCCESSFULLY LOADED " + fileName + " via the CLASSPATH from '" +
+								currentClasspathSearchLocation + "' using " + classLoaderNames[i] + "!");
+						break;	// Outta here since we've found and loaded it.
+					}
+				} catch (Exception e) {
+					result = null;
+		
+				} finally {
+					try {
+						in.close();
+					} catch (Exception e) {
+					}
+				}
+			}
+		}
+
+		if (result == null) {
+			// CHECKME: This is odd...why not ConfigurationException?
+		    throw new IllegalArgumentException("Failed to load " + RESOURCE_FILE + " as a classloader resource.");
+		}
+
+		return result;
+	}
+
+    /**
+     * Used to log errors to the console during the loading of the properties file itself. Can't use
+     * standard logging in this case, since the Logger may not be initialized yet. Output is sent to
+     * {@code PrintStream} {@code System.out}.
+     *
+     * @param message The message to send to the console.
+     * @param e The error that occurred. (This value printed via {@code e.toString()}.)
+     */
+    private void logSpecial(String message, Throwable e) {
+    	StringBuffer msg = new StringBuffer(message);
+    	if (e != null) {
+    		msg.append(" Exception was: ").append( e.toString() );
+    	}
+		System.out.println( msg.toString() );
+		// if ( e != null) e.printStackTrace();		// TODO ??? Do we want this?
+    }
+
+    /**
+     * Used to log errors to the console during the loading of the properties file itself. Can't use
+     * standard logging in this case, since the Logger may not be initialized yet. Output is sent to
+     * {@code PrintStream} {@code System.out}.
+     *
+     * @param message The message to send to the console.
+     */
+    private void logSpecial(String message) {
+		System.out.println(message);
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getPasswordParameterName() {
+        return getESAPIProperty(PASSWORD_PARAMETER_NAME, "password");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getUsernameParameterName() {
+        return getESAPIProperty(USERNAME_PARAMETER_NAME, "username");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getEncryptionAlgorithm() {
+        return getESAPIProperty(ENCRYPTION_ALGORITHM, "AES");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getCipherTransformation() {
+    	assert cipherXformCurrent != null : "Current cipher transformation is null";
+    	return cipherXformCurrent;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String setCipherTransformation(String cipherXform) {
+    	String previous = getCipherTransformation();
+    	if ( cipherXform == null ) {
+    		// Special case... means set it to original value from ESAPI.properties
+    		cipherXformCurrent = cipherXformFromESAPIProp;
+    	} else {
+    		assert ! cipherXform.trim().equals("") :
+    			"Cipher transformation cannot be just white space or empty string";
+    		cipherXformCurrent = cipherXform;	// Note: No other sanity checks!!!
+    	}
+    	return previous;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean useMACforCipherText() {
+    	return getESAPIProperty(CIPHERTEXT_USE_MAC, true);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean overwritePlainText() {
+    	return getESAPIProperty(PLAINTEXT_OVERWRITE, true);
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getIVType() {
+    	String value = getESAPIProperty(IV_TYPE, "random");
+    	if ( value.equalsIgnoreCase("fixed") || value.equalsIgnoreCase("random") ) {
+    		return value;
+    	} else if ( value.equalsIgnoreCase("specified") ) {
+    		// This is planned for future implementation where setting
+    		// Encryptor.ChooseIVMethod=specified   will require setting some
+    		// other TBD property that will specify an implementation class that
+    		// will generate appropriate IVs. The intent of this would be to use
+    		// such a class with various feedback modes where it is imperative
+    		// that for a given key, any particular IV is *NEVER* reused. For
+    		// now, we will assume that generating a random IV is usually going
+    		// to be sufficient to prevent this.
+    		throw new ConfigurationException("'" + IV_TYPE + "=specified' is not yet implemented. Use 'fixed' or 'random'");
+    	} else {
+    		// TODO: Once 'specified' is legal, adjust exception msg, below.
+    		// DISCUSS: Could just log this and then silently return "random" instead.
+    		throw new ConfigurationException(value + " is illegal value for " + IV_TYPE +
+    										 ". Use 'random' (preferred) or 'fixed'.");
+    	}
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getFixedIV() {
+    	if ( getIVType().equalsIgnoreCase("fixed") ) {
+    		String ivAsHex = getESAPIProperty(FIXED_IV, ""); // No default
+    		if ( ivAsHex == null || ivAsHex.trim().equals("") ) {
+    			throw new ConfigurationException("Fixed IV requires property " +
+    						FIXED_IV + " to be set, but it is not.");
+    		}
+    		return ivAsHex;		// We do no further checks here as we have no context.
+    	} else {
+    		// DISCUSS: Should we just log a warning here and return null instead?
+    		//			If so, may cause NullPointException somewhere later.
+    		throw new ConfigurationException("IV type not 'fixed' (set to '" +
+    										 getIVType() + "'), so no fixed IV applicable.");
+    	}
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getHashAlgorithm() {
+        return getESAPIProperty(HASH_ALGORITHM, "SHA-512");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getHashIterations() {
+    	return getESAPIProperty(HASH_ITERATIONS, 1024);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+	public String getKDFPseudoRandomFunction() {
+		return getESAPIProperty(KDF_PRF_ALG, "HmacSHA256");  // NSA recommended SHA2 or better.
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getCharacterEncoding() {
+        return getESAPIProperty(CHARACTER_ENCODING, "UTF-8");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public boolean getAllowMultipleEncoding() {
+		return getESAPIProperty( ALLOW_MULTIPLE_ENCODING, false );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public boolean getAllowMixedEncoding() {
+		return getESAPIProperty( ALLOW_MIXED_ENCODING, false );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public List<String> getDefaultCanonicalizationCodecs() {
+		List<String> def = new ArrayList<String>();
+		def.add( "org.owasp.esapi.codecs.HTMLEntityCodec" );
+		def.add( "org.owasp.esapi.codecs.PercentCodec" );
+		def.add( "org.owasp.esapi.codecs.JavaScriptCodec" );
+		return getESAPIProperty( CANONICALIZATION_CODECS, def );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getDigitalSignatureAlgorithm() {
+        return getESAPIProperty(DIGITAL_SIGNATURE_ALGORITHM, "SHAwithDSA");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getDigitalSignatureKeyLength() {
+        return getESAPIProperty(DIGITAL_SIGNATURE_KEY_LENGTH, 1024);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomAlgorithm() {
+        return getESAPIProperty(RANDOM_ALGORITHM, "SHA1PRNG");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getAllowedLoginAttempts() {
+        return getESAPIProperty(ALLOWED_LOGIN_ATTEMPTS, 5);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getMaxOldPasswordHashes() {
+        return getESAPIProperty(MAX_OLD_PASSWORD_HASHES, 12);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public File getUploadDirectory() {
+    	String dir = getESAPIProperty( UPLOAD_DIRECTORY, "UploadDir");
+    	return new File( dir );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public File getUploadTempDirectory() {
+    	String dir = getESAPIProperty(UPLOAD_TEMP_DIRECTORY,
+            System.getProperty("java.io.tmpdir","UploadTempDir"));
+    	return new File( dir );
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+	public boolean getDisableIntrusionDetection() {
+    	String value = properties.getProperty( DISABLE_INTRUSION_DETECTION );
+    	if ("true".equalsIgnoreCase(value)) return true;
+    	return false;	// Default result
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public Threshold getQuota(String eventName) {
+        int count = getESAPIProperty("IntrusionDetector." + eventName + ".count", 0);
+        int interval =  getESAPIProperty("IntrusionDetector." + eventName + ".interval", 0);
+        List<String> actions = new ArrayList<String>();
+        String actionString = getESAPIProperty("IntrusionDetector." + eventName + ".actions", "");
+        if (actionString != null) {
+            String[] actionList = actionString.split(",");
+            actions = Arrays.asList(actionList);
+        }
+        if ( count > 0 && interval > 0 && actions.size() > 0 ) {
+        	return new Threshold(eventName, count, interval, actions);
+        }
+        return null;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getLogLevel() {
+        String level = getESAPIProperty(LOG_LEVEL, "WARNING" );
+
+        if (level.equalsIgnoreCase("OFF"))
+            return Logger.OFF;
+        if (level.equalsIgnoreCase("FATAL"))
+            return Logger.FATAL;
+        if (level.equalsIgnoreCase("ERROR"))
+            return Logger.ERROR ;
+        if (level.equalsIgnoreCase("WARNING"))
+            return Logger.WARNING;
+        if (level.equalsIgnoreCase("INFO"))
+            return Logger.INFO;
+        if (level.equalsIgnoreCase("DEBUG"))
+            return Logger.DEBUG;
+        if (level.equalsIgnoreCase("TRACE"))
+            return Logger.TRACE;
+        if (level.equalsIgnoreCase("ALL"))
+            return Logger.ALL;
+
+		// This error is NOT logged the normal way because the logger constructor calls getLogLevel() and if this error occurred it would cause
+		// an infinite loop.
+        logSpecial("The LOG-LEVEL property in the ESAPI properties file has the unrecognized value: " + level + ". Using default: WARNING", null);
+        return Logger.WARNING;  // Note: The default logging level is WARNING.
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getLogFileName() {
+    	return getESAPIProperty( LOG_FILE_NAME, "ESAPI_logging_file" );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public int getMaxLogFileSize() {
+    	return getESAPIProperty( MAX_LOG_FILE_SIZE, DEFAULT_MAX_LOG_FILE_SIZE );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getLogEncodingRequired() {
+    	return getESAPIProperty( LOG_ENCODING_REQUIRED, false );
+	}
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getLogApplicationName() {
+    	return getESAPIProperty( LOG_APPLICATION_NAME, true );
+	}
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getLogServerIP() {
+    	return getESAPIProperty( LOG_SERVER_IP, true );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceHttpOnlySession() {
+    	return getESAPIProperty( FORCE_HTTPONLYSESSION, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceSecureSession() {
+    	return getESAPIProperty( FORCE_SECURESESSION, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceHttpOnlyCookies() {
+    	return getESAPIProperty( FORCE_HTTPONLYCOOKIES, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceSecureCookies() {
+    	return getESAPIProperty( FORCE_SECURECOOKIES, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public int getMaxHttpHeaderSize() {
+        return getESAPIProperty( MAX_HTTP_HEADER_SIZE, 4096 );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public String getResponseContentType() {
+        return getESAPIProperty( RESPONSE_CONTENT_TYPE, "text/html; charset=UTF-8" );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getHttpSessionIdName() {
+        return getESAPIProperty( HTTP_SESSION_ID_NAME, "JSESSIONID" );
+    }
+	
+	/**
+	 * {@inheritDoc}
+	 */
+    public long getRememberTokenDuration() {
+        int days = getESAPIProperty( REMEMBER_TOKEN_DURATION, 14 );
+        return (long) (1000 * 60 * 60 * 24 * days);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public int getSessionIdleTimeoutLength() {
+        int minutes = getESAPIProperty( IDLE_TIMEOUT_DURATION, 20 );
+        return 1000 * 60 * minutes;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public int getSessionAbsoluteTimeoutLength() {
+        int minutes = getESAPIProperty(ABSOLUTE_TIMEOUT_DURATION, 20 );
+        return 1000 * 60 * minutes;
+	}
+
+   /**
+    * getValidationPattern returns a single pattern based upon key
+    *
+    *  @param key
+    *  			validation pattern name you'd like
+    *  @return
+    *  			if key exists, the associated validation pattern, null otherwise
+	*/
+    public Pattern getValidationPattern( String key ) {
+    	String value = getESAPIProperty( "Validator." + key, "" );
+    	// check cache
+    	Pattern p = patternCache.get( value );
+    	if ( p != null ) return p;
+
+    	// compile a new pattern
+    	if ( value == null || value.equals( "" ) ) return null;
+    	try {
+    		Pattern q = Pattern.compile(value);
+    		patternCache.put( value, q );
+    		return q;
+    	} catch ( PatternSyntaxException e ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not a valid regex in ESAPI.properties. Returning null", null );
+    		return null;
+    	}
+    }
+
+    /**
+     * getWorkingDirectory returns the default directory where processes will be executed
+     * by the Executor.
+     */
+	public File getWorkingDirectory() {
+		String dir = getESAPIProperty( WORKING_DIRECTORY, System.getProperty( "user.dir") );
+		if ( dir != null ) {
+			return new File( dir );
+		}
+		return null;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getPreferredJCEProvider() {
+	    return properties.getProperty(PREFERRED_JCE_PROVIDER); // No default!
+	}  
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public List<String> getCombinedCipherModes()
+	{
+	    List<String> empty = new ArrayList<String>();     // Default is empty list
+	    return getESAPIProperty(COMBINED_CIPHER_MODES, empty);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public List<String> getAdditionalAllowedCipherModes()
+	{
+	    List<String> empty = new ArrayList<String>();     // Default is empty list
+	    return getESAPIProperty(ADDITIONAL_ALLOWED_CIPHER_MODES, empty);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean getLenientDatesAccepted() {
+		return getESAPIProperty( ACCEPT_LENIENT_DATES, false);
+	}
+
+	protected String getESAPIProperty( String key, String def ) {
+		String value = properties.getProperty(key);
+		if ( value == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+		return value;
+	}
+
+	protected boolean getESAPIProperty( String key, boolean def ) {
+		String property = properties.getProperty(key);
+		if ( property == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+		if ( property.equalsIgnoreCase("true") || property.equalsIgnoreCase("yes" ) ) {
+			return true;
+		}
+		if ( property.equalsIgnoreCase("false") || property.equalsIgnoreCase( "no" ) ) {
+			return false;
+		}
+		logSpecial( "SecurityConfiguration for " + key + " not either \"true\" or \"false\" in ESAPI.properties. Using default: " + def, null );
+		return def;
+	}
+
+	protected byte[] getESAPIPropertyEncoded( String key, byte[] def ) {
+		String property = properties.getProperty(key);
+		if ( property == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+        try {
+            return ESAPI.encoder().decodeFromBase64(property);
+        } catch( IOException e ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not properly Base64 encoded in ESAPI.properties. Using default: " + def, null );
+            return null;
+        }
+	}
+
+	protected int getESAPIProperty( String key, int def ) {
+		String property = properties.getProperty(key);
+		if ( property == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+		try {
+            return Integer.parseInt( property );
+		} catch( NumberFormatException e ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not an integer in ESAPI.properties. Using default: " + def, null );
+			return def;
+		}
+	}
+
+	/**
+     * Returns a {@code List} representing the parsed, comma-separated property.
+     * 
+	 * @param key  The specified property name
+	 * @param def  A default value for the property name to return if the property
+	 *             is not set.
+	 * @return A list of strings.
+	 */
+	protected List<String> getESAPIProperty( String key, List<String> def ) {
+	    String property = properties.getProperty( key );
+	    if ( property == null ) {
+	        logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+	        return def;
+	    }
+	    String[] parts = property.split(",");
+	    return Arrays.asList( parts );
+	}
+
+	protected boolean shouldPrintProperties() {
+        return getESAPIProperty(PRINT_PROPERTIES_WHEN_LOADED, false);
+	}
+
+    protected Properties getESAPIProperties() {
+        return properties;
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultUser.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultUser.java.svn-base
new file mode 100644
index 0000000..6b24f1a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultUser.java.svn-base
@@ -0,0 +1,614 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.*;
+
+import javax.servlet.http.HttpSession;
+import java.io.Serializable;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Collections;
+import java.util.HashMap;
+/**
+ * Reference implementation of the User interface. This implementation is serialized into a flat file in a simple format.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com) <a href="http://www.digital-ritual.com">Digital Ritual Software</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.User
+ */
+public class DefaultUser implements User, Serializable {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/** The idle timeout length specified in the ESAPI config file. */
+	private static final int IDLE_TIMEOUT_LENGTH = ESAPI.securityConfiguration().getSessionIdleTimeoutLength();
+	
+	/** The absolute timeout length specified in the ESAPI config file. */
+	private static final int ABSOLUTE_TIMEOUT_LENGTH = ESAPI.securityConfiguration().getSessionAbsoluteTimeoutLength();
+	
+	/** The logger used by the class. */
+	private transient final Logger logger = ESAPI.getLogger("DefaultUser");
+    
+	/** This user's account id. */
+	long accountId = 0;
+
+	/** This user's account name. */
+	private String accountName = "";
+
+	/** This user's screen name (account name alias). */
+	private String screenName = "";
+
+	/** This user's CSRF token. */
+	private String csrfToken = resetCSRFToken();
+
+	/** This user's assigned roles. */
+	private Set<String> roles = new HashSet<String>();
+
+	/** Whether this user's account is locked. */
+	private boolean locked = false;
+
+	/** Whether this user is logged in. */
+	private boolean loggedIn = true;
+
+    /** Whether this user's account is enabled. */
+	private boolean enabled = false;
+
+    /** The last host address used by this user. */
+    private String lastHostAddress;
+
+	/** The last password change time for this user. */
+	private Date lastPasswordChangeTime = new Date(0);
+
+	/** The last login time for this user. */
+	private Date lastLoginTime = new Date(0);
+
+	/** The last failed login time for this user. */
+	private Date lastFailedLoginTime = new Date(0);
+	
+	/** The expiration date/time for this user's account. */
+	private Date expirationTime = new Date(Long.MAX_VALUE);
+
+	/** The sessions this user is associated with */
+	private transient Set<HttpSession> sessions = new HashSet<HttpSession>();
+	
+	/** The event map for this User */ 
+	private transient HashMap eventMap = new HashMap();
+	
+	/* A flag to indicate that the password must be changed before the account can be used. */
+	// private boolean requiresPasswordChange = true;
+	
+	/** The failed login count for this user's account. */
+	private int failedLoginCount = 0;
+	
+	/** This user's Locale. */
+	private Locale locale;
+    
+    private static final int MAX_ROLE_LENGTH = 250;
+    
+	/**
+	 * Instantiates a new user.
+	 *
+	 * @param accountName
+	 * 		The name of this user's account.
+	 */
+	public DefaultUser(String accountName) {
+		this.accountName = accountName.toLowerCase();
+		while( true ) {
+			long id = Math.abs( ESAPI.randomizer().getRandomLong() );
+			if ( ESAPI.authenticator().getUser( id ) == null && id != 0 ) {
+				this.accountId = id;
+				break;
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void addRole(String role) throws AuthenticationException {
+		String roleName = role.toLowerCase();
+		if ( ESAPI.validator().isValidInput("addRole", roleName, "RoleName", MAX_ROLE_LENGTH, false) ) {
+			roles.add(roleName);
+			logger.info(Logger.SECURITY_SUCCESS, "Role " + roleName + " added to " + getAccountName() );
+		} else {
+			throw new AuthenticationAccountsException( "Add role failed", "Attempt to add invalid role " + roleName + " to " + getAccountName() );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void addRoles(Set<String> newRoles) throws AuthenticationException {
+        for (String newRole : newRoles)
+        {
+            addRole(newRole);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void changePassword(String oldPassword, String newPassword1, String newPassword2) throws AuthenticationException, EncryptionException {
+		ESAPI.authenticator().changePassword(this, oldPassword, newPassword1, newPassword2);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void disable() {
+		enabled = false;
+		logger.info( Logger.SECURITY_SUCCESS, "Account disabled: " + getAccountName() );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void enable() {
+		this.enabled = true;
+		logger.info( Logger.SECURITY_SUCCESS, "Account enabled: " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public long getAccountId() {
+        return accountId;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getAccountName() {
+		return accountName;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getCSRFToken() {
+		return csrfToken;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getExpirationTime() {
+		return (Date)expirationTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public int getFailedLoginCount() {
+		return failedLoginCount;
+	}
+	
+	/**
+	 * Set the failed login count
+	 * 
+	 * @param count
+	 * 			the number of failed logins
+	 */
+	void setFailedLoginCount(int count) {
+		failedLoginCount = count;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getLastFailedLoginTime() {
+		return (Date)lastFailedLoginTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getLastHostAddress() {
+		if ( lastHostAddress == null ) {
+			return "unknown";
+		}
+        return lastHostAddress;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getLastLoginTime() {
+		return (Date)lastLoginTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getLastPasswordChangeTime() {
+		return (Date)lastPasswordChangeTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @return
+     */
+	public String getName() {
+		return this.getAccountName();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public Set<String> getRoles() {
+		return Collections.unmodifiableSet(roles);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getScreenName() {
+		return screenName;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void addSession( HttpSession s ) {
+        sessions.add( s );
+    }
+    
+	/**
+	 * {@inheritDoc}
+	 */
+    public void removeSession( HttpSession s ) {
+        sessions.remove( s );
+    }
+    
+	/**
+	 * {@inheritDoc}
+     *
+     * @return
+     */
+	public Set getSessions() {
+	    return sessions;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void incrementFailedLoginCount() {
+		failedLoginCount++;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isAnonymous() {
+		// User cannot be anonymous, since we have a special User.ANONYMOUS instance
+		// for the anonymous user
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isEnabled() {
+		return enabled;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isExpired() {
+		return getExpirationTime().before( new Date() );
+
+		// If expiration should happen automatically or based on lastPasswordChangeTime?
+		//		long from = lastPasswordChangeTime.getTime();
+		//		long to = new Date().getTime();
+		//		double difference = to - from;
+		//		long days = Math.round((difference / (1000 * 60 * 60 * 24)));
+		//		return days > 60;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isInRole(String role) {
+		return roles.contains(role.toLowerCase());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isLocked() {
+		return locked;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isLoggedIn() {
+		return loggedIn;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isSessionAbsoluteTimeout() {
+		HttpSession session = ESAPI.httpUtilities().getCurrentRequest().getSession(false);
+		if ( session == null ) return true;
+		Date deadline = new Date( session.getCreationTime() + ABSOLUTE_TIMEOUT_LENGTH);
+		Date now = new Date();
+		return now.after(deadline);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isSessionTimeout() {
+		HttpSession session = ESAPI.httpUtilities().getCurrentRequest().getSession(false);
+		if ( session == null ) return true;
+		Date deadline = new Date( session.getLastAccessedTime() + IDLE_TIMEOUT_LENGTH);
+		Date now = new Date();
+		return now.after(deadline);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void lock() {
+		this.locked = true;
+		logger.info(Logger.SECURITY_SUCCESS, "Account locked: " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void loginWithPassword(String password) throws AuthenticationException {
+		if ( password == null || password.equals("") ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException( "Login failed", "Missing password: " + accountName  );
+		}
+		
+		// don't let disabled users log in
+		if ( !isEnabled() ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException("Login failed", "Disabled user attempt to login: " + accountName );
+		}
+		
+		// don't let locked users log in
+		if ( isLocked() ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException("Login failed", "Locked user attempt to login: " + accountName );
+		}
+		
+		// don't let expired users log in
+		if ( isExpired() ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException("Login failed", "Expired user attempt to login: " + accountName );
+		}
+		
+		logout();
+
+		if ( verifyPassword( password ) ) {
+			loggedIn = true;
+			ESAPI.httpUtilities().changeSessionIdentifier( ESAPI.currentRequest() );
+			ESAPI.authenticator().setCurrentUser(this);
+			setLastLoginTime(new Date());
+            setLastHostAddress( ESAPI.httpUtilities().getCurrentRequest().getRemoteAddr() );
+			logger.trace(Logger.SECURITY_SUCCESS, "User logged in: " + accountName );
+		} else {
+			loggedIn = false;
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			if (getFailedLoginCount() >= ESAPI.securityConfiguration().getAllowedLoginAttempts()) {
+				lock();
+			}
+			throw new AuthenticationLoginException("Login failed", "Incorrect password provided for " + getAccountName() );
+		}
+	}
+ 
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void logout() {
+		ESAPI.httpUtilities().killCookie( ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME );
+		
+		HttpSession session = ESAPI.currentRequest().getSession(false);
+		if (session != null) {
+            removeSession(session);
+			session.invalidate();
+		}
+		ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), ESAPI.securityConfiguration().getHttpSessionIdName());
+		loggedIn = false;
+		logger.info(Logger.SECURITY_SUCCESS, "Logout successful" );
+		ESAPI.authenticator().setCurrentUser(User.ANONYMOUS);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void removeRole(String role) {
+		roles.remove(role.toLowerCase());
+		logger.trace(Logger.SECURITY_SUCCESS, "Role " + role + " removed from " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * In this implementation, we have chosen to use a random token that is
+	 * stored in the User object. Note that it is possible to avoid the use of
+	 * server side state by using either the hash of the users's session id or
+	 * an encrypted token that includes a timestamp and the user's IP address.
+	 * user's IP address. A relatively short 8 character string has been chosen
+	 * because this token will appear in all links and forms.
+	 * 
+	 * @return the string
+	 */
+	public String resetCSRFToken() {
+		// user.csrfToken = ESAPI.encryptor().hash( session.getId(),user.name );
+		// user.csrfToken = ESAPI.encryptor().encrypt( address + ":" + ESAPI.encryptor().getTimeStamp();
+		csrfToken = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		return csrfToken;
+	}
+
+	/**
+	 * Sets the account id for this user's account.
+	 */
+	private void setAccountId(long accountId) {
+		this.accountId = accountId;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setAccountName(String accountName) {
+		String old = getAccountName();
+		this.accountName = accountName.toLowerCase();
+		if (old != null) {
+			if ( old.equals( "" ) ) {
+				old = "[nothing]";
+			}
+			logger.info(Logger.SECURITY_SUCCESS, "Account name changed from " + old + " to " + getAccountName() );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setExpirationTime(Date expirationTime) {
+		this.expirationTime = new Date( expirationTime.getTime() );
+		logger.info(Logger.SECURITY_SUCCESS, "Account expiration time set to " + expirationTime + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastFailedLoginTime(Date lastFailedLoginTime) {
+		this.lastFailedLoginTime = lastFailedLoginTime;
+		logger.info(Logger.SECURITY_SUCCESS, "Set last failed login time to " + lastFailedLoginTime + " for " + getAccountName() );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastHostAddress(String remoteHost) throws AuthenticationHostException
+    {
+		if ( lastHostAddress != null && !lastHostAddress.equals(remoteHost)) {
+        	// returning remote address not remote hostname to prevent DNS lookup
+			throw new AuthenticationHostException("Host change", "User session just jumped from " + lastHostAddress + " to " + remoteHost );
+		}
+		lastHostAddress = remoteHost;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastLoginTime(Date lastLoginTime) {
+		this.lastLoginTime = lastLoginTime;
+		logger.info(Logger.SECURITY_SUCCESS, "Set last successful login time to " + lastLoginTime + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastPasswordChangeTime(Date lastPasswordChangeTime) {
+		this.lastPasswordChangeTime = lastPasswordChangeTime;
+		logger.info(Logger.SECURITY_SUCCESS, "Set last password change time to " + lastPasswordChangeTime + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setRoles(Set<String> roles) throws AuthenticationException {
+		this.roles = new HashSet<String>();
+		addRoles(roles);
+		logger.info(Logger.SECURITY_SUCCESS, "Adding roles " + roles + " to " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setScreenName(String screenName) {
+		this.screenName = screenName;
+		logger.info(Logger.SECURITY_SUCCESS, "ScreenName changed to " + screenName + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @return
+     */
+	public String toString() {
+		return "USER:" + accountName;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void unlock() {
+		this.locked = false;
+		this.failedLoginCount = 0;
+		logger.info( Logger.SECURITY_SUCCESS, "Account unlocked: " + getAccountName() );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean verifyPassword(String password) {
+		return ESAPI.authenticator().verifyPassword(this, password);
+	}
+    
+    /**
+     * Override clone and make final to prevent duplicate user objects.
+     * @return 
+     * @throws java.lang.CloneNotSupportedException
+     */
+    public final Object clone() throws java.lang.CloneNotSupportedException {
+    	  throw new java.lang.CloneNotSupportedException();
+    }
+	/**
+	 * @return the locale
+	 */
+	public Locale getLocale() {
+		return locale;
+	}
+
+	/**
+	 * @param locale the locale to set
+	 */
+	public void setLocale(Locale locale) {
+		this.locale = locale;
+	}
+    
+    public HashMap getEventMap() {
+    	return eventMap;
+    }
+    
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultValidator.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultValidator.java.svn-base
new file mode 100644
index 0000000..85c3343
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/DefaultValidator.java.svn-base
@@ -0,0 +1,1194 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ *
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.ValidationRule;
+import org.owasp.esapi.Validator;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationAvailabilityException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.reference.validation.CreditCardValidationRule;
+import org.owasp.esapi.reference.validation.DateValidationRule;
+import org.owasp.esapi.reference.validation.HTMLValidationRule;
+import org.owasp.esapi.reference.validation.IntegerValidationRule;
+import org.owasp.esapi.reference.validation.NumberValidationRule;
+import org.owasp.esapi.reference.validation.StringValidationRule;
+
+/**
+ * Reference implementation of the Validator interface. This implementation
+ * relies on the ESAPI Encoder, Java Pattern (regex), Date,
+ * and several other classes to provide basic validation functions. This library
+ * has a heavy emphasis on whitelist validation and canonicalization.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ *
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class DefaultValidator implements org.owasp.esapi.Validator {
+    private static volatile Validator instance = null;
+
+    public static Validator getInstance() {
+        if ( instance == null ) {
+            synchronized ( Validator.class ) {
+                if ( instance == null ) {
+                    instance = new DefaultValidator();
+                }
+            }
+        }
+        return instance;
+    }
+
+	/** A map of validation rules */
+	private Map<String, ValidationRule> rules = new HashMap<String, ValidationRule>();
+
+	/** The encoder to use for canonicalization */
+	private Encoder encoder = null;
+
+	/** The encoder to use for file system */
+	private static Validator fileValidator = null;
+
+	/** Initialize file validator with an appropriate set of codecs */
+	static {
+		List<String> list = new ArrayList<String>();
+		list.add( "HTMLEntityCodec" );
+		list.add( "PercentCodec" );
+		Encoder fileEncoder = new DefaultEncoder( list );
+		fileValidator = new DefaultValidator( fileEncoder );
+	}
+
+
+	/**
+	 * Default constructor uses the ESAPI standard encoder for canonicalization.
+	 */
+	public DefaultValidator() {
+	    this.encoder = ESAPI.encoder();
+	}
+
+	/**
+	 * Construct a new DefaultValidator that will use the specified
+	 * Encoder for canonicalization.
+     *
+     * @param encoder
+     */
+	public DefaultValidator( Encoder encoder ) {
+	    this.encoder = encoder;
+	}
+
+
+	/**
+	 * Add a validation rule to the registry using the "type name" of the rule as the key.
+	 */
+	public void addRule( ValidationRule rule ) {
+		rules.put( rule.getTypeName(), rule );
+	}
+
+	/**
+	 * Get a validation rule from the registry with the "type name" of the rule as the key.
+	 */
+	public ValidationRule getRule( String name ) {
+		return rules.get( name );
+	}
+
+
+	/**
+	 * Returns true if data received from browser is valid. Double encoding is treated as an attack. The
+	 * default encoder supports html encoding, URL encoding, and javascript escaping. Input is canonicalized
+	 * by default before validation.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum post-canonicalized String length allowed.
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @return The canonicalized user input.
+	 * @throws IntrusionException
+	 */
+	public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws IntrusionException  {
+		return isValidInput(context, input, type, maxLength, allowNull, true);
+	}
+
+        public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException  {
+		return isValidInput(context, input, type, maxLength, allowNull, true, errors);
+	}
+
+	public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws IntrusionException  {
+		try {
+			getValidInput( context, input, type, maxLength, allowNull, canonicalize);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errors) throws IntrusionException  {
+		try {
+			getValidInput( context, input, type, maxLength, allowNull, canonicalize);
+			return true;
+		} catch( ValidationException e ) {
+			errors.addError( context, e );
+			return false;
+		}
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version.
+	 * Double encoding is treated as an attack. The default encoder supports
+	 * html encoding, URL encoding, and javascript escaping. Input is
+	 * canonicalized by default before validation.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name which maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum post-canonicalized String length allowed.
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @return The canonicalized user input.
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws ValidationException {
+		return getValidInput(context, input, type, maxLength, allowNull, true);
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version. Only
+	 * URL encoding is supported. Double encoding is treated as an attack.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name which maps to the actual regular expression in the ESAPI validation configuration file
+	 * @param maxLength The maximum String length allowed. If input is canonicalized per the canonicalize argument, then maxLength must be verified after canonicalization
+     * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param canonicalize If canonicalize is true then input will be canonicalized before validation
+	 * @return The user input, may be canonicalized if canonicalize argument is true
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws ValidationException {
+		StringValidationRule rvr = new StringValidationRule( type, encoder );
+		Pattern p = ESAPI.securityConfiguration().getValidationPattern( type );
+		if ( p != null ) {
+			rvr.addWhitelistPattern( p );
+		} else {
+            // Issue 232 - Specify requested type in exception message - CS
+			throw new IllegalArgumentException("The selected type [" + type + "] was not set via the ESAPI validation configuration");
+		}
+		rvr.setMaximumLength(maxLength);
+		rvr.setAllowNull(allowNull);
+		rvr.setValidateInputAndCanonical(canonicalize);
+		return rvr.getValid(context, input);
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version. Only
+	 * URL encoding is supported. Double encoding is treated as an attack. Input
+	 * is canonicalized by default before validation.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum String length allowed. If input is canonicalized per the canonicalize argument, then maxLength must be verified after canonicalization
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param errors If ValidationException is thrown, then add to error list instead of throwing out to caller
+	 * @return The canonicalized user input.
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return getValidInput(context, input, type, maxLength, allowNull, true, errors);
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version. Only
+	 * URL encoding is supported. Double encoding is treated as an attack.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum post-canonicalized String length allowed
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param canonicalize If canonicalize is true then input will be canonicalized before validation
+	 * @param errors If ValidationException is thrown, then add to error list instead of throwing out to caller
+	 * @return The user input, may be canonicalized if canonicalize argument is true
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidInput(context,  input,  type,  maxLength,  allowNull, canonicalize);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDate(String context, String input, DateFormat format, boolean allowNull) throws IntrusionException {
+		try {
+			getValidDate( context, input, format, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidDate( context, input, format, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getValidDate(String context, String input, DateFormat format, boolean allowNull) throws ValidationException, IntrusionException {
+		DateValidationRule dvr = new DateValidationRule( "SimpleDate", encoder, format);
+		dvr.setAllowNull(allowNull);
+		return dvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidDate(context, input, format, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return null
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws IntrusionException {
+		try {
+			getValidSafeHTML( context, input, maxLength, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidSafeHTML( context, input, maxLength, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * This implementation relies on the OWASP AntiSamy project.
+	 */
+	public String getValidSafeHTML( String context, String input, int maxLength, boolean allowNull ) throws ValidationException, IntrusionException {
+		HTMLValidationRule hvr = new HTMLValidationRule( "safehtml", encoder );
+		hvr.setMaximumLength(maxLength);
+		hvr.setAllowNull(allowNull);
+		hvr.setValidateInputAndCanonical(false);
+		return hvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidSafeHTML(context, input, maxLength, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidCreditCard(String context, String input, boolean allowNull) throws IntrusionException {
+		try {
+			getValidCreditCard( context, input, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidCreditCard( context, input, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidCreditCard(String context, String input, boolean allowNull) throws ValidationException, IntrusionException {
+		CreditCardValidationRule ccvr = new CreditCardValidationRule( "creditcard", encoder );
+		ccvr.setAllowNull(allowNull);
+		return ccvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidCreditCard(context, input, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+	 */
+	public boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws IntrusionException {
+		try {
+			getValidDirectoryPath( context, input, parent, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+	 */
+	public boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidDirectoryPath( context, input, parent, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws ValidationException, IntrusionException {
+		try {
+			if (isEmpty(input)) {
+				if (allowNull) return null;
+       			throw new ValidationException( context + ": Input directory path required", "Input directory path required: context=" + context + ", input=" + input, context );
+			}
+
+			File dir = new File( input );
+
+			// check dir exists and parent exists and dir is inside parent
+			if ( !dir.exists() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, does not exist: context=" + context + ", input=" + input );
+			}
+			if ( !dir.isDirectory() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, not a directory: context=" + context + ", input=" + input );
+			}
+			if ( !parent.exists() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, specified parent does not exist: context=" + context + ", input=" + input + ", parent=" + parent );
+			}
+			if ( !parent.isDirectory() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, specified parent is not a directory: context=" + context + ", input=" + input + ", parent=" + parent );
+			}
+			if ( !dir.getCanonicalPath().startsWith(parent.getCanonicalPath() ) ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, not inside specified parent: context=" + context + ", input=" + input + ", parent=" + parent );
+			}
+
+			// check canonical form matches input
+			String canonicalPath = dir.getCanonicalPath();
+			String canonical = fileValidator.getValidInput( context, canonicalPath, "DirectoryName", 255, false);
+			if ( !canonical.equals( input ) ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory name does not match the canonical path: context=" + context + ", input=" + input + ", canonical=" + canonical, context );
+			}
+			return canonical;
+		} catch (Exception e) {
+			throw new ValidationException( context + ": Invalid directory name", "Failure to validate directory path: context=" + context + ", input=" + input, e, context );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+
+		try {
+			return getValidDirectoryPath(context, input, parent, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, boolean allowNull) throws IntrusionException {
+		return isValidFileName( context, input, ESAPI.securityConfiguration().getAllowedFileExtensions(), allowNull );
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return isValidFileName( context, input, ESAPI.securityConfiguration().getAllowedFileExtensions(), allowNull, errors );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws IntrusionException {
+		try {
+			getValidFileName( context, input, allowedExtensions, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidFileName( context, input, allowedExtensions, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException {
+		if ((allowedExtensions == null) || (allowedExtensions.isEmpty())) {
+			throw new ValidationException( "Internal Error", "getValidFileName called with an empty or null list of allowed Extensions, therefore no files can be uploaded" );
+		}
+
+		String canonical = "";
+		// detect path manipulation
+		try {
+			if (isEmpty(input)) {
+				if (allowNull) return null;
+	   			throw new ValidationException( context + ": Input file name required", "Input required: context=" + context + ", input=" + input, context );
+			}
+
+			// do basic validation
+	        canonical = new File(input).getCanonicalFile().getName();
+	        getValidInput( context, input, "FileName", 255, true );
+
+			File f = new File(canonical);
+			String c = f.getCanonicalPath();
+			String cpath = c.substring(c.lastIndexOf(File.separator) + 1);
+
+
+			// the path is valid if the input matches the canonical path
+			if (!input.equals(cpath)) {
+				throw new ValidationException( context + ": Invalid file name", "Invalid directory name does not match the canonical path: context=" + context + ", input=" + input + ", canonical=" + canonical, context );
+			}
+
+		} catch (IOException e) {
+			throw new ValidationException( context + ": Invalid file name", "Invalid file name does not exist: context=" + context + ", canonical=" + canonical, e, context );
+		}
+
+		// verify extensions
+		Iterator<String> i = allowedExtensions.iterator();
+		while (i.hasNext()) {
+			String ext = i.next();
+			if (input.toLowerCase().endsWith(ext.toLowerCase())) {
+				return canonical;
+			}
+		}
+		throw new ValidationException( context + ": Invalid file name does not have valid extension ( "+allowedExtensions+")", "Invalid file name does not have valid extension ( "+allowedExtensions+"): context=" + context+", input=" + input, context );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidFileName(String context, String input, List<String> allowedParameters, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidFileName(context, input, allowedParameters, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws IntrusionException {
+		try {
+			getValidNumber(context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidNumber(context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws ValidationException, IntrusionException {
+		Double minDoubleValue = new Double(minValue);
+		Double maxDoubleValue = new Double(maxValue);
+		return getValidDouble(context, input, minDoubleValue.doubleValue(), maxDoubleValue.doubleValue(), allowNull);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidNumber(context, input, minValue, maxValue, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws IntrusionException {
+        try {
+            getValidDouble( context, input, minValue, maxValue, allowNull );
+            return true;
+        } catch( Exception e ) {
+            return false;
+        }
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+        try {
+            getValidDouble( context, input, minValue, maxValue, allowNull );
+            return true;
+        } catch( ValidationException e ) {
+            errors.addError(context, e);
+            return false;
+        }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws ValidationException, IntrusionException {
+		NumberValidationRule nvr = new NumberValidationRule( "number", encoder, minValue, maxValue );
+		nvr.setAllowNull(allowNull);
+		return nvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidDouble(context, input, minValue, maxValue, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return new Double(Double.NaN);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws IntrusionException {
+		try {
+			getValidInteger( context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidInteger( context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws ValidationException, IntrusionException {
+		IntegerValidationRule ivr = new IntegerValidationRule( "number", encoder, minValue, maxValue );
+		ivr.setAllowNull(allowNull);
+		return ivr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidInteger(context, input, minValue, maxValue, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws IntrusionException {
+		try {
+			getValidFileContent( context, input, maxBytes, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidFileContent( context, input, maxBytes, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException {
+		if (isEmpty(input)) {
+			if (allowNull) return null;
+   			throw new ValidationException( context + ": Input required", "Input required: context=" + context + ", input=" + input, context );
+		}
+
+		long esapiMaxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize();
+		if (input.length > esapiMaxBytes ) throw new ValidationException( context + ": Invalid file content can not exceed " + esapiMaxBytes + " bytes", "Exceeded ESAPI max length", context );
+		if (input.length > maxBytes ) throw new ValidationException( context + ": Invalid file content can not exceed " + maxBytes + " bytes", "Exceeded maxBytes ( " + input.length + ")", context );
+
+		return input;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidFileContent(context, input, maxBytes, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// return empty byte array on error
+		return new byte[0];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+     */
+	public boolean isValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull) throws IntrusionException {
+		return( isValidFileName( context, filename, allowNull ) &&
+				isValidDirectoryPath( context, directorypath, parent, allowNull ) &&
+				isValidFileContent( context, content, maxBytes, allowNull ) );
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+     */
+	public boolean isValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return( isValidFileName( context, filename, allowNull, errors ) &&
+				isValidDirectoryPath( context, directorypath, parent, allowNull, errors ) &&
+				isValidFileContent( context, content, maxBytes, allowNull, errors ) );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException {
+		getValidFileName( context, filename, allowedExtensions, allowNull );
+		getValidDirectoryPath( context, directorypath, parent, allowNull );
+		getValidFileContent( context, content, maxBytes, allowNull );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errors)
+		throws IntrusionException {
+		try {
+			assertValidFileUpload(context, filepath, filename, parent, content, maxBytes, allowedExtensions, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+	}
+
+	 /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is a valid list item.
+	 */
+	public boolean isValidListItem(String context, String input, List<String> list) {
+		try {
+			getValidListItem( context, input, list);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is a valid list item.
+	 */
+	public boolean isValidListItem(String context, String input, List<String> list, ValidationErrorList errors) {
+		try {
+			getValidListItem( context, input, list);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Returns the list item that exactly matches the canonicalized input. Invalid or non-matching input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 */
+	public String getValidListItem(String context, String input, List<String> list) throws ValidationException, IntrusionException {
+		if (list.contains(input)) return input;
+		throw new ValidationException( context + ": Invalid list item", "Invalid list item: context=" + context + ", input=" + input, context );
+	}
+
+
+	/**
+	 * ValidationErrorList variant of getValidListItem
+     *
+     * @param errors
+     */
+	public String getValidListItem(String context, String input, List<String> list, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidListItem(context, input, list);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+	 /**
+	 * {@inheritDoc}
+     */
+	public boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> requiredNames, Set<String> optionalNames) {
+		try {
+			assertValidHTTPRequestParameterSet( context, request, requiredNames, optionalNames);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+         /**
+	 * {@inheritDoc}
+     */
+	public boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> requiredNames, Set<String> optionalNames, ValidationErrorList errors) {
+		try {
+			assertValidHTTPRequestParameterSet( context, request, requiredNames, optionalNames);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Validates that the parameters in the current request contain all required parameters and only optional ones in
+	 * addition. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * Uses current HTTPRequest
+	 */
+	public void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional) throws ValidationException, IntrusionException {
+		Set<String> actualNames = request.getParameterMap().keySet();
+
+		// verify ALL required parameters are present
+		Set<String> missing = new HashSet<String>(required);
+		missing.removeAll(actualNames);
+		if (missing.size() > 0) {
+			throw new ValidationException( context + ": Invalid HTTP request missing parameters", "Invalid HTTP request missing parameters " + missing + ": context=" + context, context );
+		}
+
+		// verify ONLY optional + required parameters are present
+		Set<String> extra = new HashSet<String>(actualNames);
+		extra.removeAll(required);
+		extra.removeAll(optional);
+		if (extra.size() > 0) {
+			throw new ValidationException( context + ": Invalid HTTP request extra parameters " + extra, "Invalid HTTP request extra parameters " + extra + ": context=" + context, context );
+		}
+	}
+
+	/**
+	 * ValidationErrorList variant of assertIsValidHTTPRequestParameterSet
+     *
+	 * Uses current HTTPRequest saved in ESAPI Authenticator
+     * @param errors
+     */
+	public void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional, ValidationErrorList errors) throws IntrusionException {
+		try {
+			assertValidHTTPRequestParameterSet(context, request, required, optional);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+	 * Checks that all bytes are valid ASCII characters (between 33 and 126
+	 * inclusive). This implementation does no decoding. http://en.wikipedia.org/wiki/ASCII.
+	 */
+	public boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+     * {@inheritDoc}
+     *
+	 * Checks that all bytes are valid ASCII characters (between 33 and 126
+	 * inclusive). This implementation does no decoding. http://en.wikipedia.org/wiki/ASCII.
+	 */
+	public boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Returns canonicalized and validated printable characters as a byte array. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+     *
+     * @throws IntrusionException
+     */
+	public char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException {
+		if (isEmpty(input)) {
+			if (allowNull) return null;
+   			throw new ValidationException(context + ": Input bytes required", "Input bytes required: HTTP request is null", context );
+		}
+
+		if (input.length > maxLength) {
+			throw new ValidationException(context + ": Input bytes can not exceed " + maxLength + " bytes", "Input exceeds maximum allowed length of " + maxLength + " by " + (input.length-maxLength) + " bytes: context=" + context + ", input=" + new String( input ), context);
+		}
+
+		for (int i = 0; i < input.length; i++) {
+			if (input[i] <= 0x20 || input[i] >= 0x7E ) {
+				throw new ValidationException(context + ": Invalid input bytes: context=" + context, "Invalid non-ASCII input bytes, context=" + context + ", input=" + new String( input ), context);
+			}
+		}
+		return input;
+	}
+
+	/**
+	 * ValidationErrorList variant of getValidPrintable
+     *
+     * @param errors
+     */
+	public char[] getValidPrintable(String context, char[] input,int maxLength, boolean allowNull, ValidationErrorList errors)
+		throws IntrusionException {
+
+		try {
+			return getValidPrintable(context, input, maxLength, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+
+	 /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is valid printable ASCII characters (32-126).
+	 */
+	public boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is valid printable ASCII characters (32-126).
+	 */
+	public boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Returns canonicalized and validated printable characters as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+     *
+     * @throws IntrusionException
+     */
+	public String getValidPrintable(String context, String input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException {
+		try {
+    		String canonical = encoder.canonicalize(input);
+    		return new String( getValidPrintable( context, canonical.toCharArray(), maxLength, allowNull) );
+	    //TODO - changed this to base Exception since we no longer need EncodingException
+    	//TODO - this is a bit lame: we need to re-think this function.
+		} catch (Exception e) {
+	        throw new ValidationException( context + ": Invalid printable input", "Invalid encoding of printable input, context=" + context + ", input=" + input, e, context);
+	    }
+	}
+
+	/**
+	 * ValidationErrorList variant of getValidPrintable
+     *
+     * @param errors
+     */
+	public String getValidPrintable(String context, String input,int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidPrintable(context, input, maxLength, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+
+	/**
+	 * Returns true if input is a valid redirect location.
+	 */
+	public boolean isValidRedirectLocation(String context, String input, boolean allowNull) throws IntrusionException {
+		return ESAPI.validator().isValidInput( context, input, "Redirect", 512, allowNull);
+	}
+
+        /**
+	 * Returns true if input is a valid redirect location.
+	 */
+	public boolean isValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return ESAPI.validator().isValidInput( context, input, "Redirect", 512, allowNull, errors);
+	}
+
+
+	/**
+	 * Returns a canonicalized and validated redirect location as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 */
+	public String getValidRedirectLocation(String context, String input, boolean allowNull) throws ValidationException, IntrusionException {
+		return ESAPI.validator().getValidInput( context, input, "Redirect", 512, allowNull);
+	}
+
+	/**
+	 * ValidationErrorList variant of getValidRedirectLocation
+     *
+     * @param errors
+     */
+	public String getValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidRedirectLocation(context, input, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+	 * This implementation reads until a newline or the specified number of
+	 * characters.
+     *
+     * @param in
+     * @param max
+     */
+	public String safeReadLine(InputStream in, int max) throws ValidationException {
+		if (max <= 0) {
+			throw new ValidationAvailabilityException( "Invalid input", "Invalid readline. Must read a positive number of bytes from the stream");
+		}
+
+		StringBuilder sb = new StringBuilder();
+		int count = 0;
+		int c;
+
+		try {
+			while (true) {
+				c = in.read();
+				if ( c == -1 ) {
+					if (sb.length() == 0) {
+						return null;
+					}
+					break;
+				}
+				if (c == '\n' || c == '\r') {
+					break;
+				}
+				count++;
+				if (count > max) {
+					throw new ValidationAvailabilityException( "Invalid input", "Invalid readLine. Read more than maximum characters allowed (" + max + ")");
+				}
+				sb.append((char) c);
+			}
+			return sb.toString();
+		} catch (IOException e) {
+			throw new ValidationAvailabilityException( "Invalid input", "Invalid readLine. Problem reading from input stream", e);
+		}
+	}
+
+	/**
+	 * Helper function to check if a String is empty
+	 *
+	 * @param input string input value
+	 * @return boolean response if input is empty or not
+	 */
+	private final boolean isEmpty(String input) {
+		return (input==null || input.trim().length() == 0);
+	}
+
+	/**
+	 * Helper function to check if a byte array is empty
+	 *
+	 * @param input string input value
+	 * @return boolean response if input is empty or not
+	 */
+	private final boolean isEmpty(byte[] input) {
+		return (input==null || input.length == 0);
+	}
+
+
+	/**
+	 * Helper function to check if a char array is empty
+	 *
+	 * @param input string input value
+	 * @return boolean response if input is empty or not
+	 */
+	private final boolean isEmpty(char[] input) {
+		return (input==null || input.length == 0);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/FileBasedAuthenticator.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/FileBasedAuthenticator.java.svn-base
new file mode 100644
index 0000000..6b76c5e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/FileBasedAuthenticator.java.svn-base
@@ -0,0 +1,725 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.*;
+import org.owasp.esapi.errors.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Reference implementation of the Authenticator interface. This reference implementation is backed by a simple text
+ * file that contains serialized information about users. Many organizations will want to create their own
+ * implementation of the methods provided in the Authenticator interface backed by their own user repository. This
+ * reference implementation captures information about users in a simple text file format that contains user information
+ * separated by the pipe "|" character. Here's an example of a single line from the users.txt file:
+ * <p/>
+ * <PRE>
+ * <p/>
+ * account id | account name | hashed password | roles | lockout | status | old password hashes | last
+ * hostname | last change | last login | last failed | expiration | failed
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * 1203123710837 | mitch | 44k/NAzQUlrCq9musTGGkcMNmdzEGJ8w8qZTLzpxLuQ= | admin,user | unlocked | enabled |
+ * u10dW4vTo3ZkoM5xP+blayWCz7KdPKyKUojOn9GJobg= | 192.168.1.255 | 1187201000926 | 1187200991568 | 1187200605330 |
+ * 2187200605330 | 1
+ * <p/>
+ * </PRE>
+ *
+ * @author <a href="mailto:jeff.williams at aspectsecurity.com?subject=ESAPI question">Jeff Williams</a> at <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com) <a href="http://www.digital-ritual.com">Digital Ritual Software</a>
+ * @see org.owasp.esapi.Authenticator
+ * @since June 1, 2007
+ */
+public class FileBasedAuthenticator extends AbstractAuthenticator {
+
+    private static volatile Authenticator singletonInstance;
+
+    public static Authenticator getInstance()
+    {
+        if ( singletonInstance == null ) {
+            synchronized ( FileBasedAuthenticator.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new FileBasedAuthenticator();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    /**
+     * The logger.
+     */
+    private final Logger logger = ESAPI.getLogger("Authenticator");
+
+    /**
+     * The file that contains the user db
+     */
+    private File userDB = null;
+
+    /**
+     * How frequently to check the user db for external modifications
+     */
+    private long checkInterval = 60 * 1000;
+
+    /**
+     * The last modified time we saw on the user db.
+     */
+    private long lastModified = 0;
+
+    /**
+     * The last time we checked if the user db had been modified externally
+     */
+    private long lastChecked = 0;
+
+    private static final int MAX_ACCOUNT_NAME_LENGTH = 250;
+
+    /**
+     * Fail safe main program to add or update an account in an emergency.
+     * <p/>
+     * Warning: this method does not perform the level of validation and checks
+     * generally required in ESAPI, and can therefore be used to create a username and password that do not comply
+     * with the username and password strength requirements.
+     * <p/>
+     * Example: Use this to add the alice account with the admin role to the users file:
+     * <PRE>
+     * <p/>
+     * java -Dorg.owasp.esapi.resources="/path/resources" -classpath esapi.jar org.owasp.esapi.Authenticator alice password admin
+     * <p/>
+     * </PRE>
+     *
+     * @param args the arguments (username, password, role)
+     * @throws Exception the exception
+     */
+    public static void main(String[] args) throws Exception {
+        if (args.length != 3) {
+            System.out.println("Usage: Authenticator accountname password role");
+            return;
+        }
+        FileBasedAuthenticator auth = new FileBasedAuthenticator();
+        String accountName = args[0].toLowerCase();
+        String password = args[1];
+        String role = args[2];
+        DefaultUser user = (DefaultUser) auth.getUser(args[0]);
+        if (user == null) {
+            user = new DefaultUser(accountName);
+            String newHash = auth.hashPassword(password, accountName);
+            auth.setHashedPassword(user, newHash);
+            user.addRole(role);
+            user.enable();
+            user.unlock();
+            auth.userMap.put(user.getAccountId(), user);
+            System.out.println("New user created: " + accountName);
+            auth.saveUsers();
+            System.out.println("User account " + user.getAccountName() + " updated");
+        } else {
+            System.err.println("User account " + user.getAccountName() + " already exists!");
+        }
+    }
+
+    /**
+     * Add a hash to a User's hashed password list.  This method is used to store a user's old password hashes
+     * to be sure that any new passwords are not too similar to old passwords.
+     *
+     * @param user the user to associate with the new hash
+     * @param hash the hash to store in the user's password hash list
+     */
+    private void setHashedPassword(User user, String hash) {
+        List<String> hashes = getAllHashedPasswords(user, true);
+        hashes.add(0, hash);
+        if (hashes.size() > ESAPI.securityConfiguration().getMaxOldPasswordHashes()) {
+            hashes.remove(hashes.size() - 1);
+        }
+        logger.info(Logger.SECURITY_SUCCESS, "New hashed password stored for " + user.getAccountName());
+    }
+
+    /**
+     * Return the specified User's current hashed password.
+     *
+     * @param user this User's current hashed password will be returned
+     * @return the specified User's current hashed password
+     */
+    String getHashedPassword(User user) {
+        List hashes = getAllHashedPasswords(user, false);
+        return (String) hashes.get(0);
+    }
+
+    /**
+     * Set the specified User's old password hashes.  This will not set the User's current password hash.
+     *
+     * @param user      the User whose old password hashes will be set
+     * @param oldHashes a list of the User's old password hashes     *
+     */
+    void setOldPasswordHashes(User user, List<String> oldHashes) {
+        List<String> hashes = getAllHashedPasswords(user, true);
+        if (hashes.size() > 1) {
+            hashes.removeAll(hashes.subList(1, hashes.size() - 1));
+        }
+        hashes.addAll(oldHashes);
+    }
+
+    /**
+     * Returns all of the specified User's hashed passwords.  If the User's list of passwords is null,
+     * and create is set to true, an empty password list will be associated with the specified User
+     * and then returned. If the User's password map is null and create is set to false, an exception
+     * will be thrown.
+     *
+     * @param user   the User whose old hashes should be returned
+     * @param create true - if no password list is associated with this user, create one
+     *               false - if no password list is associated with this user, do not create one
+     * @return a List containing all of the specified User's password hashes
+     */
+    List<String> getAllHashedPasswords(User user, boolean create) {
+        List<String> hashes = passwordMap.get(user);
+        if (hashes != null) {
+            return hashes;
+        }
+        if (create) {
+            hashes = new ArrayList<String>();
+            passwordMap.put(user, hashes);
+            return hashes;
+        }
+        throw new RuntimeException("No hashes found for " + user.getAccountName() + ". Is User.hashcode() and equals() implemented correctly?");
+    }
+
+    /**
+     * Get a List of the specified User's old password hashes.  This will not return the User's current
+     * password hash.
+     *
+     * @param user he user whose old password hashes should be returned
+     * @return the specified User's old password hashes
+     */
+    List<String> getOldPasswordHashes(User user) {
+        List<String> hashes = getAllHashedPasswords(user, false);
+        if (hashes.size() > 1) {
+            return Collections.unmodifiableList(hashes.subList(1, hashes.size() - 1));
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * The user map.
+     */
+    private Map<Long, User> userMap = new HashMap<Long, User>();
+
+    // Map<User, List<String>>, where the strings are password hashes, with the current hash in entry 0
+    private Map<User, List<String>> passwordMap = new Hashtable<User, List<String>>();
+
+
+
+    /**
+     *
+     */
+    private FileBasedAuthenticator() {
+    	super();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized User createUser(String accountName, String password1, String password2) throws AuthenticationException {
+        loadUsersIfNecessary();
+        if (accountName == null) {
+            throw new AuthenticationAccountsException("Account creation failed", "Attempt to create user with null accountName");
+        }
+        if (getUser(accountName) != null) {
+            throw new AuthenticationAccountsException("Account creation failed", "Duplicate user creation denied for " + accountName);
+        }
+
+        verifyAccountNameStrength(accountName);
+
+        if (password1 == null) {
+            throw new AuthenticationCredentialsException("Invalid account name", "Attempt to create account " + accountName + " with a null password");
+        }
+        
+        DefaultUser user = new DefaultUser(accountName);
+        
+        verifyPasswordStrength(null, password1, user);
+
+        if (!password1.equals(password2)) {
+            throw new AuthenticationCredentialsException("Passwords do not match", "Passwords for " + accountName + " do not match");
+        }
+
+        try {
+            setHashedPassword(user, hashPassword(password1, accountName));
+        } catch (EncryptionException ee) {
+            throw new AuthenticationException("Internal error", "Error hashing password for " + accountName, ee);
+        }
+        userMap.put(user.getAccountId(), user);
+        logger.info(Logger.SECURITY_SUCCESS, "New user created: " + accountName);
+        saveUsers();
+        return user;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String generateStrongPassword() {
+        return generateStrongPassword("");
+    }
+
+    /**
+     * Generate a strong password that is not similar to the specified old password.
+     *
+     * @param oldPassword the password to be compared to the new password for similarity
+     * @return a new strong password that is dissimilar to the specified old password
+     */
+    private String generateStrongPassword(String oldPassword) {
+        Randomizer r = ESAPI.randomizer();
+        int letters = r.getRandomInteger(4, 6);  // inclusive, exclusive
+        int digits = 7 - letters;
+        String passLetters = r.getRandomString(letters, EncoderConstants.CHAR_PASSWORD_LETTERS);
+        String passDigits = r.getRandomString(digits, EncoderConstants.CHAR_PASSWORD_DIGITS);
+        String passSpecial = r.getRandomString(1, EncoderConstants.CHAR_PASSWORD_SPECIALS);
+        String newPassword = passLetters + passSpecial + passDigits;
+        if (StringUtilities.getLevenshteinDistance(oldPassword, newPassword) > 5) {
+            return newPassword;
+        }
+        return generateStrongPassword(oldPassword);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void changePassword(User user, String currentPassword,
+                               String newPassword, String newPassword2)
+            throws AuthenticationException {
+        String accountName = user.getAccountName();
+        try {
+            String currentHash = getHashedPassword(user);
+            String verifyHash = hashPassword(currentPassword, accountName);
+            if (!currentHash.equals(verifyHash)) {
+                throw new AuthenticationCredentialsException("Password change failed", "Authentication failed for password change on user: " + accountName);
+            }
+            if (newPassword == null || newPassword2 == null || !newPassword.equals(newPassword2)) {
+                throw new AuthenticationCredentialsException("Password change failed", "Passwords do not match for password change on user: " + accountName);
+            }
+            verifyPasswordStrength(currentPassword, newPassword, user);
+            user.setLastPasswordChangeTime(new Date());
+            String newHash = hashPassword(newPassword, accountName);
+            if (getOldPasswordHashes(user).contains(newHash)) {
+                throw new AuthenticationCredentialsException("Password change failed", "Password change matches a recent password for user: " + accountName);
+            }
+            setHashedPassword(user, newHash);
+            logger.info(Logger.SECURITY_SUCCESS, "Password changed for user: " + accountName);
+            // jtm - 11/2/2010 - added to resolve http://code.google.com/p/owasp-esapi-java/issues/detail?id=13
+            saveUsers();
+        } catch (EncryptionException ee) {
+            throw new AuthenticationException("Password change failed", "Encryption exception changing password for " + accountName, ee);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean verifyPassword(User user, String password) {
+        String accountName = user.getAccountName();
+        try {
+            String hash = hashPassword(password, accountName);
+            String currentHash = getHashedPassword(user);
+            if (hash.equals(currentHash)) {
+                user.setLastLoginTime(new Date());
+                ((DefaultUser) user).setFailedLoginCount(0);
+                logger.info(Logger.SECURITY_SUCCESS, "Password verified for " + accountName);
+                return true;
+            }
+        } catch (EncryptionException e) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Encryption error verifying password for " + accountName);
+        }
+        logger.fatal(Logger.SECURITY_FAILURE, "Password verification failed for " + accountName);
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String generateStrongPassword(User user, String oldPassword) {
+        String newPassword = generateStrongPassword(oldPassword);
+        if (newPassword != null) {
+            logger.info(Logger.SECURITY_SUCCESS, "Generated strong password for " + user.getAccountName());
+        }
+        return newPassword;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized User getUser(long accountId) {
+        if (accountId == 0) {
+            return User.ANONYMOUS;
+        }
+        loadUsersIfNecessary();
+        return userMap.get(accountId);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized User getUser(String accountName) {
+        if (accountName == null) {
+            return User.ANONYMOUS;
+        }
+        loadUsersIfNecessary();
+        for (User u : userMap.values()) {
+            if (u.getAccountName().equalsIgnoreCase(accountName)) {
+                return u;
+            }
+        }
+        return null;
+    }
+
+ 
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized Set getUserNames() {
+        loadUsersIfNecessary();
+        HashSet<String> results = new HashSet<String>();
+        for (User u : userMap.values()) {
+            results.add(u.getAccountName());
+        }
+        return results;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws EncryptionException
+     */
+    public String hashPassword(String password, String accountName) throws EncryptionException {
+        String salt = accountName.toLowerCase();
+        return ESAPI.encryptor().hash(password, salt);
+    }
+
+    /**
+     * Load users if they haven't been loaded in a while.
+     */
+    protected void loadUsersIfNecessary() {
+        if (userDB == null) {
+            userDB = ESAPI.securityConfiguration().getResourceFile("users.txt");
+        }
+        if (userDB == null) {
+            userDB = new File(System.getProperty("user.home") + "/.esapi", "users.txt");
+            try {
+                if (!userDB.createNewFile()) throw new IOException("Unable to create the user file");
+                logger.warning(Logger.SECURITY_SUCCESS, "Created " + userDB.getAbsolutePath());
+            } catch (IOException e) {
+                logger.fatal(Logger.SECURITY_FAILURE, "Could not create " + userDB.getAbsolutePath(), e);
+            }
+        }
+
+        // We only check at most every checkInterval milliseconds
+        long now = System.currentTimeMillis();
+        if (now - lastChecked < checkInterval) {
+            return;
+        }
+        lastChecked = now;
+
+        if (lastModified == userDB.lastModified()) {
+            return;
+        }
+        loadUsersImmediately();
+    }
+
+    // file was touched so reload it
+    /**
+     *
+     */
+    protected void loadUsersImmediately() {
+        synchronized (this) {
+            logger.trace(Logger.SECURITY_SUCCESS, "Loading users from " + userDB.getAbsolutePath(), null);
+
+            BufferedReader reader = null;
+            try {
+                HashMap<Long, User> map = new HashMap<Long, User>();
+                reader = new BufferedReader(new FileReader(userDB));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    if (line.length() > 0 && line.charAt(0) != '#') {
+                        DefaultUser user = createUser(line);
+                        if (map.containsKey(new Long(user.getAccountId()))) {
+                            logger.fatal(Logger.SECURITY_FAILURE, "Problem in user file. Skipping duplicate user: " + user, null);
+                        }
+                        map.put(user.getAccountId(), user);
+                    }
+                }
+                userMap = map;
+                this.lastModified = System.currentTimeMillis();
+                logger.trace(Logger.SECURITY_SUCCESS, "User file reloaded: " + map.size(), null);
+            } catch (Exception e) {
+                logger.fatal(Logger.SECURITY_FAILURE, "Failure loading user file: " + userDB.getAbsolutePath(), e);
+            } finally {
+                try {
+                    if (reader != null) {
+                        reader.close();
+                    }
+                } catch (IOException e) {
+                    logger.fatal(Logger.SECURITY_FAILURE, "Failure closing user file: " + userDB.getAbsolutePath(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new user with all attributes from a String.  The format is:
+     * accountId | accountName | password | roles (comma separated) | unlocked | enabled | old password hashes (comma separated) | last host address | last password change time | last long time | last failed login time | expiration time | failed login count
+     * This method verifies the account name and password strength, creates a new CSRF token, then returns the newly created user.
+     *
+     * @param line parameters to set as attributes for the new User.
+     * @return the newly created User
+     * @throws AuthenticationException
+     */
+    private DefaultUser createUser(String line) throws AuthenticationException {
+        String[] parts = line.split(" *\\| *");
+        String accountIdString = parts[0];
+        long accountId = Long.parseLong(accountIdString);
+        String accountName = parts[1];
+
+        verifyAccountNameStrength(accountName);
+        DefaultUser user = new DefaultUser(accountName);
+        user.accountId = accountId;
+
+        String password = parts[2];
+        verifyPasswordStrength(null, password, user);
+        setHashedPassword(user, password);
+
+        String[] roles = parts[3].toLowerCase().split(" *, *");
+        for (String role : roles) {
+            if (!"".equals(role)) {
+                user.addRole(role);
+            }
+        }
+        if (!"unlocked".equalsIgnoreCase(parts[4])) {
+            user.lock();
+        }
+        if ("enabled".equalsIgnoreCase(parts[5])) {
+            user.enable();
+        } else {
+            user.disable();
+        }
+
+        // generate a new csrf token
+        user.resetCSRFToken();
+
+        setOldPasswordHashes(user, Arrays.asList(parts[6].split(" *, *")));
+        user.setLastHostAddress("null".equals(parts[7]) ? null : parts[7]);
+        user.setLastPasswordChangeTime(new Date(Long.parseLong(parts[8])));
+        user.setLastLoginTime(new Date(Long.parseLong(parts[9])));
+        user.setLastFailedLoginTime(new Date(Long.parseLong(parts[10])));
+        user.setExpirationTime(new Date(Long.parseLong(parts[11])));
+        user.setFailedLoginCount(Integer.parseInt(parts[12]));
+        return user;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void removeUser(String accountName) throws AuthenticationException {
+        loadUsersIfNecessary();
+        User user = getUser(accountName);
+        if (user == null) {
+            throw new AuthenticationAccountsException("Remove user failed", "Can't remove invalid accountName " + accountName);
+        }
+        userMap.remove(user.getAccountId());
+        logger.info(Logger.SECURITY_SUCCESS, "Removing user " + user.getAccountName());
+        passwordMap.remove(user);
+        saveUsers();
+    }
+
+    /**
+     * Saves the user database to the file system. In this implementation you must call save to commit any changes to
+     * the user file. Otherwise changes will be lost when the program ends.
+     *
+     * @throws AuthenticationException if the user file could not be written
+     */
+    public synchronized void saveUsers() throws AuthenticationException {
+        PrintWriter writer = null;
+        try {
+            writer = new PrintWriter(new FileWriter(userDB));
+            writer.println("# This is the user file associated with the ESAPI library from http://www.owasp.org");
+            writer.println("# accountId | accountName | hashedPassword | roles | locked | enabled | csrfToken | oldPasswordHashes | lastPasswordChangeTime | lastLoginTime | lastFailedLoginTime | expirationTime | failedLoginCount");
+            writer.println();
+            saveUsers(writer);
+            writer.flush();
+            logger.info(Logger.SECURITY_SUCCESS, "User file written to disk");
+        } catch (IOException e) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Problem saving user file " + userDB.getAbsolutePath(), e);
+            throw new AuthenticationException("Internal Error", "Problem saving user file " + userDB.getAbsolutePath(), e);
+        } finally {
+            if (writer != null) {
+                writer.close();
+                lastModified = userDB.lastModified();
+                lastChecked = lastModified;
+            }
+        }
+    }
+
+    /**
+     * Save users.
+     *
+     * @param writer the print writer to use for saving
+     */
+    protected synchronized void saveUsers(PrintWriter writer) throws AuthenticationCredentialsException {
+        for (Object o : getUserNames()) {
+            String accountName = (String) o;
+            DefaultUser u = (DefaultUser) getUser(accountName);
+            if (u != null && !u.isAnonymous()) {
+                writer.println(save(u));
+            } else {
+                throw new AuthenticationCredentialsException("Problem saving user", "Skipping save of user " + accountName);
+            }
+        }
+    }
+
+    /**
+     * Save.
+     *
+     * @param user the User to save
+     * @return a line containing properly formatted information to save regarding the user
+     */
+    private String save(DefaultUser user) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(user.getAccountId());
+        sb.append(" | ");
+        sb.append(user.getAccountName());
+        sb.append(" | ");
+        sb.append(getHashedPassword(user));
+        sb.append(" | ");
+        sb.append(dump(user.getRoles()));
+        sb.append(" | ");
+        sb.append(user.isLocked() ? "locked" : "unlocked");
+        sb.append(" | ");
+        sb.append(user.isEnabled() ? "enabled" : "disabled");
+        sb.append(" | ");
+        sb.append(dump(getOldPasswordHashes(user)));
+        sb.append(" | ");
+        sb.append(user.getLastHostAddress());
+        sb.append(" | ");
+        sb.append(user.getLastPasswordChangeTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getLastLoginTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getLastFailedLoginTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getExpirationTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getFailedLoginCount());
+        return sb.toString();
+    }
+
+    /**
+     * Dump a collection as a comma-separated list.
+     *
+     * @param c the collection to convert to a comma separated list
+     * @return a comma separated list containing the values in c
+     */
+    private String dump(Collection<String> c) {
+        StringBuilder sb = new StringBuilder();
+        for (String s : c) {
+            sb.append(s).append(",");
+        }
+        if ( c.size() > 0) {
+        	return sb.toString().substring(0, sb.length() - 1);
+        }
+        return "";
+        
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This implementation simply verifies that account names are at least 5 characters long. This helps to defeat a
+     * brute force attack, however the real strength comes from the name length and complexity.
+     *
+     * @param newAccountName
+     */
+    public void verifyAccountNameStrength(String newAccountName) throws AuthenticationException {
+        if (newAccountName == null) {
+            throw new AuthenticationCredentialsException("Invalid account name", "Attempt to create account with a null account name");
+        }
+        if (!ESAPI.validator().isValidInput("verifyAccountNameStrength", newAccountName, "AccountName", MAX_ACCOUNT_NAME_LENGTH, false)) {
+            throw new AuthenticationCredentialsException("Invalid account name", "New account name is not valid: " + newAccountName);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This implementation checks: - for any 3 character substrings of the old password - for use of a length *
+     * character sets > 16 (where character sets are upper, lower, digit, and special
+     * jtm - 11/16/2010 - added check to verify pw != username (fix for http://code.google.com/p/owasp-esapi-java/issues/detail?id=108)
+     */
+    public void verifyPasswordStrength(String oldPassword, String newPassword, User user) throws AuthenticationException {
+        if (newPassword == null) {
+            throw new AuthenticationCredentialsException("Invalid password", "New password cannot be null");
+        }
+
+        // can't change to a password that contains any 3 character substring of old password
+        if (oldPassword != null) {
+            int length = oldPassword.length();
+            for (int i = 0; i < length - 2; i++) {
+                String sub = oldPassword.substring(i, i + 3);
+                if (newPassword.indexOf(sub) > -1) {
+                    throw new AuthenticationCredentialsException("Invalid password", "New password cannot contain pieces of old password");
+                }
+            }
+        }
+
+        // new password must have enough character sets and length
+        int charsets = 0;
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_LOWERS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_UPPERS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_DIGITS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_SPECIALS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+
+        // calculate and verify password strength
+        int strength = newPassword.length() * charsets;
+        if (strength < 16) {
+            throw new AuthenticationCredentialsException("Invalid password", "New password is not long and complex enough");
+        }
+        
+        String accountName = user.getAccountName();
+        
+        //jtm - 11/3/2010 - fix for bug http://code.google.com/p/owasp-esapi-java/issues/detail?id=108
+        if (accountName.equalsIgnoreCase(newPassword)) {
+        	//password can't be account name
+        	throw new AuthenticationCredentialsException("Invalid password", "Password matches account name, irrespective of case");
+        }
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/IntegerAccessReferenceMap.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/IntegerAccessReferenceMap.java.svn-base
new file mode 100644
index 0000000..8bb36d6
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/IntegerAccessReferenceMap.java.svn-base
@@ -0,0 +1,80 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Set;
+
+/**
+ * Reference implementation of the AccessReferenceMap interface. This
+ * implementation generates integers for indirect references.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author Chris Schmidt (chrisisbeef at gmail.com)
+ * @since June 1, 2007
+ * @see org.owasp.esapi.AccessReferenceMap
+ */
+public class IntegerAccessReferenceMap extends AbstractAccessReferenceMap<String> {
+
+	private static final long serialVersionUID = 5311769278372489771L;
+
+	int count = 1;
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap()
+	{
+	}
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap(int initialSize)
+	{
+		super(initialSize);
+	}
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap(Set<Object> directReferences)
+	{
+		super(directReferences.size());
+		update(directReferences);
+	}
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap(Set<Object> directReferences, int initialSize)
+	{
+		super(initialSize);
+		update(directReferences);
+	}
+
+	/**
+	 * TODO Javadoc
+	 * Note: this is final as redefinition by subclasses
+	 * can lead to use before initialization issues as
+	 * {@link #RandomAccessReferenceMap(Set)} and
+	 * {@link #RandomAccessReferenceMap(Set,int)} both call it
+	 * internally.
+	 */
+	protected final synchronized String getUniqueReference() {
+		return "" + count++;  // returns a string version of the counter
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/JavaLogFactory.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/JavaLogFactory.java.svn-base
new file mode 100644
index 0000000..0970818
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/JavaLogFactory.java.svn-base
@@ -0,0 +1,409 @@
+package org.owasp.esapi.reference;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.logging.Level;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.LogFactory;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+
+/**
+ * Reference implementation of the LogFactory and Logger interfaces. This implementation uses the Java logging package, and marks each
+ * log message with the currently logged in user and the word "SECURITY" for security related events. See the 
+ * <a href="JavaLogFactory.JavaLogger.html">JavaLogFactory.JavaLogger</a> Javadocs for the details on the JavaLogger reference implementation.
+ * 
+ * @author Mike Fauzy (mike.fauzy at aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.LogFactory
+ * @see org.owasp.esapi.reference.JavaLogFactory.JavaLogger
+ */
+public class JavaLogFactory implements LogFactory {
+	private static volatile LogFactory singletonInstance;
+
+    public static LogFactory getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( JavaLogFactory.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new JavaLogFactory();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+	private HashMap<Serializable, Logger> loggersMap = new HashMap<Serializable, Logger>();
+	
+	/**
+	* Null argument constructor for this implementation of the LogFactory interface
+	* needed for dynamic configuration.
+	*/
+	public JavaLogFactory() {}
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public Logger getLogger(Class clazz) {
+    	
+    	// If a logger for this class already exists, we return the same one, otherwise we create a new one.
+    	Logger classLogger = (Logger) loggersMap.get(clazz);
+    	
+    	if (classLogger == null) {
+    		classLogger = new JavaLogger(clazz.getName());
+    		loggersMap.put(clazz, classLogger);
+    	}
+		return classLogger;
+    }
+
+    /**
+	* {@inheritDoc}
+	*/
+    public Logger getLogger(String moduleName) {
+    	
+    	// If a logger for this module already exists, we return the same one, otherwise we create a new one.
+    	Logger moduleLogger = (Logger) loggersMap.get(moduleName);
+    	
+    	if (moduleLogger == null) {
+    		moduleLogger = new JavaLogger(moduleName);
+    		loggersMap.put(moduleName, moduleLogger);    		
+    	}
+		return moduleLogger;
+    }
+
+
+    /**
+     *  A custom logging level defined between Level.SEVERE and Level.WARNING in logger.
+     */
+    public static class JavaLoggerLevel extends Level {
+
+        protected static final long serialVersionUID = 1L;
+
+        /**
+    	 * Defines a custom error level below SEVERE but above WARNING since this level isn't defined directly
+    	 * by java.util.Logger already.
+    	 */
+    	public static final Level ERROR_LEVEL = new JavaLoggerLevel( "ERROR", Level.SEVERE.intValue() - 1);
+    	
+    	/**
+    	 * Constructs an instance of a JavaLoggerLevel which essentially provides a mapping between the name of
+    	 * the defined level and its numeric value.
+    	 * 
+    	 * @param name The name of the JavaLoggerLevel
+    	 * @param value The associated numeric value
+    	 */
+		protected JavaLoggerLevel(String name, int value) {
+			super(name, value);
+		}
+    }
+        
+    /**
+     * Reference implementation of the Logger interface.
+     * 
+     * It implements most of the recommendations defined in the Logger interface description. It does not
+     * filter out any sensitive data specific to the current application or organization, such as credit 
+     * cards, social security numbers, etc.  
+     * 
+     * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+     * @since June 1, 2007
+     * @see org.owasp.esapi.LogFactory
+     */
+    private static class JavaLogger implements org.owasp.esapi.Logger {
+
+    	/** The jlogger object used by this class to log everything. */
+        private java.util.logging.Logger jlogger = null;
+
+        /** The module name using this log. */
+        private String moduleName = null;
+
+        /** The application name defined in ESAPI.properties */
+    	private String applicationName=ESAPI.securityConfiguration().getApplicationName();
+
+        /** Log the application name? */
+    	private static boolean logAppName = ESAPI.securityConfiguration().getLogApplicationName();
+
+    	/** Log the server ip? */
+    	private static boolean logServerIP = ESAPI.securityConfiguration().getLogServerIP();
+    	
+        /**
+         * Public constructor should only ever be called via the appropriate LogFactory
+         * 
+         * @param moduleName the module name
+         */
+        private JavaLogger(String moduleName) {
+            this.moduleName = moduleName;
+            this.jlogger = java.util.logging.Logger.getLogger(applicationName + ":" + moduleName);
+        }
+
+        /**
+         * {@inheritDoc}
+         * Note: In this implementation, this change is not persistent,
+         * meaning that if the application is restarted, the log level will revert to the level defined in the 
+         * ESAPI SecurityConfiguration properties file.
+         */
+        public void setLevel(int level)
+        {
+        	try {
+        		jlogger.setLevel(convertESAPILeveltoLoggerLevel( level ));
+        	}
+        	catch (IllegalArgumentException e) {
+       			this.error(Logger.SECURITY_FAILURE, "", e);    		
+        	}
+         }
+        
+        /**
+         * {@inheritDoc}
+         * @see org.owasp.esapi.reference.Log4JLogger#getESAPILevel()
+         */
+        public int getESAPILevel() {
+        	return jlogger.getLevel().intValue();
+        }
+        
+        /**
+         * Converts the ESAPI logging level (a number) into the levels used by Java's logger.
+         * @param level The ESAPI to convert.
+         * @return The Java logging Level that is equivalent.
+         * @throws IllegalArgumentException if the supplied ESAPI level doesn't make a level that is currently defined.
+         */
+        private static Level convertESAPILeveltoLoggerLevel(int level)
+        {
+        	switch (level) {
+        		case Logger.OFF:     return Level.OFF;
+        		case Logger.FATAL:   return Level.SEVERE;
+        		case Logger.ERROR:   return JavaLoggerLevel.ERROR_LEVEL; // This is a custom level.
+        		case Logger.WARNING: return Level.WARNING;
+        		case Logger.INFO:    return Level.INFO;
+        		case Logger.DEBUG:   return Level.FINE;
+        		case Logger.TRACE:   return Level.FINEST;
+        		case Logger.ALL:     return Level.ALL;       		
+        		default: {
+        			throw new IllegalArgumentException("Invalid logging level. Value was: " + level);
+        		}
+        	}
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void trace(EventType type, String message, Throwable throwable) {
+            log(Level.FINEST, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void trace(EventType type, String message) {
+            log(Level.FINEST, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void debug(EventType type, String message, Throwable throwable) {
+            log(Level.FINE, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void debug(EventType type, String message) {
+            log(Level.FINE, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void info(EventType type, String message) {
+            log(Level.INFO, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void info(EventType type, String message, Throwable throwable) {
+            log(Level.INFO, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void warning(EventType type, String message, Throwable throwable) {
+            log(Level.WARNING, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void warning(EventType type, String message) {
+            log(Level.WARNING, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void error(EventType type, String message, Throwable throwable) {
+            log(Level.SEVERE, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void error(EventType type, String message) {
+            log(Level.SEVERE, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void fatal(EventType type, String message, Throwable throwable) {
+            log(Level.SEVERE, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void fatal(EventType type, String message) {
+            log(Level.SEVERE, type, message, null);
+        }
+
+        /**
+         * Log the message after optionally encoding any special characters that might be dangerous when viewed
+         * by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log 
+         * injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging
+         * specific session ID, and the current date/time.
+         * 
+         * It will only log the message if the current logging level is enabled, otherwise it will 
+         * discard the message. 
+         * 
+         * @param level defines the set of recognized logging levels (TRACE, INFO, DEBUG, WARNING, ERROR, FATAL)
+         * @param type the type of the event (SECURITY SUCCESS, SECURITY FAILURE, EVENT SUCCESS, EVENT FAILURE)
+         * @param message the message
+         * @param throwable the throwable
+         */
+        private void log(Level level, EventType type, String message, Throwable throwable) {
+        	
+        	// Check to see if we need to log
+        	if (!jlogger.isLoggable( level )) return;
+
+            // ensure there's something to log
+            if ( message == null ) {
+            	message = "";
+            }
+            
+            // ensure no CRLF injection into logs for forging records
+            String clean = message.replace( '\n', '_' ).replace( '\r', '_' );
+            if ( ESAPI.securityConfiguration().getLogEncodingRequired() ) {
+            	clean = ESAPI.encoder().encodeForHTML(message);
+                if (!message.equals(clean)) {
+                    clean += " (Encoded)";
+                }
+            }
+
+			// log server, port, app name, module name -- server:80/app/module
+			StringBuilder appInfo = new StringBuilder();
+			if ( ESAPI.currentRequest() != null && logServerIP ) {
+				appInfo.append( ESAPI.currentRequest().getLocalAddr() + ":" + ESAPI.currentRequest().getLocalPort() );
+			}
+			if ( logAppName ) {
+				appInfo.append( "/" + applicationName );
+			}
+			appInfo.append( "/"  + moduleName );
+			
+			//get the type text if it exists
+			String typeInfo = "";
+			if (type != null) {
+				typeInfo += type + " ";
+			}
+			
+			// log the message
+			jlogger.log(level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isDebugEnabled() {
+    	    return jlogger.isLoggable(Level.FINE);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isErrorEnabled() {
+    	    return jlogger.isLoggable(JavaLoggerLevel.ERROR_LEVEL);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isFatalEnabled() {
+    	    return jlogger.isLoggable(Level.SEVERE);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isInfoEnabled() {
+    	    return jlogger.isLoggable(Level.INFO);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isTraceEnabled() {
+    	    return jlogger.isLoggable(Level.FINEST);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isWarningEnabled() {
+    	    return jlogger.isLoggable(Level.WARNING);
+        }
+        
+        public String getUserInfo() {
+            // create a random session number for the user to represent the user's 'session', if it doesn't exist already
+            String sid = null;
+            HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+            if ( request != null ) {
+                HttpSession session = request.getSession( false );
+                if ( session != null ) {
+	                sid = (String)session.getAttribute("ESAPI_SESSION");
+	                // if there is no session ID for the user yet, we create one and store it in the user's session
+		            if ( sid == null ) {
+		            	sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000);
+		            	session.setAttribute("ESAPI_SESSION", sid);
+		            }
+                }
+            }
+            
+			// log user information - username:session at ipaddr
+			User user = ESAPI.authenticator().getCurrentUser();            
+			String userInfo = "";
+			//TODO - Make Type Logging configurable
+			if ( user != null) {
+				userInfo += user.getAccountName()+ ":" + sid + "@"+ user.getLastHostAddress();
+			}
+			
+			return userInfo;
+        }
+
+    	/**
+    	 * {@inheritDoc}
+    	 */
+		public void always(EventType type, String message) {
+            always(type, message, null);	
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public void always(EventType type, String message, Throwable throwable) {
+            log(Level.OFF, type, message, throwable);  // Seems backward, but this is what works, not Level.ALL	
+		}
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLogFactory.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLogFactory.java.svn-base
new file mode 100644
index 0000000..cab4d5d
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLogFactory.java.svn-base
@@ -0,0 +1,91 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.HashMap;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.spi.LoggerFactory;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.LogFactory;
+import org.owasp.esapi.Logger;
+
+/**
+ * Reference implementation of the LogFactory interface. This implementation uses the Apache Log4J package, and marks each
+ * log message with the currently logged in user and the word "SECURITY" for security related events. See the 
+ * <a href="JavaLogFactory.JavaLogger.html">JavaLogFactory.JavaLogger</a> Javadocs for the details on the JavaLogger reference implementation.
+ * 
+ * At class initialization time, the file log4j.properties or log4j.xml file will be loaded from the classpath. This configuration file is 
+ * fundamental to make log4j work for you. Please see http://logging.apache.org/log4j/1.2/manual.html for more information.
+ *
+ * Note that <b>you must specify the LogFactory</b> in either log4j.properties:
+ *
+ * <code>
+ *     log4j.loggerFactory=org.owasp.esapi.reference.Log4JLoggerFactory
+ * </code>
+ *
+ * or log4j.xml:
+ * 
+ * <code>
+ *     <loggerFactory class="org.owasp.esapi.reference.Log4JLoggerFactory"/>
+
+ * </code>
+ * 
+ * @author Mike H. Fauzy (mike.fauzy at aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author August Detlefsen (augustd at codemagi dot com) <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.LogFactory
+ * @see org.owasp.esapi.reference.Log4JLogger
+ * @see org.owasp.esapi.reference.Log4JLoggerFactory
+ */
+public class Log4JLogFactory implements LogFactory {
+
+	private static volatile LogFactory singletonInstance;
+
+	//The Log4j logger factory to use
+	LoggerFactory factory = new Log4JLoggerFactory();
+
+    public static LogFactory getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( Log4JLogFactory.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new Log4JLogFactory();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+	
+	protected Log4JLogFactory() {}
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public org.owasp.esapi.Logger getLogger(Class clazz) {
+		return (org.owasp.esapi.Logger)LogManager.getLogger(clazz.getName(), factory);
+    }
+
+    /**
+	* {@inheritDoc}
+	*/
+	public org.owasp.esapi.Logger getLogger(String moduleName) {
+		return (org.owasp.esapi.Logger)LogManager.getLogger(moduleName, factory);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLogger.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLogger.java.svn-base
new file mode 100644
index 0000000..3b4aa95
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLogger.java.svn-base
@@ -0,0 +1,524 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggerFactory;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.User;
+
+/**
+ * Reference implementation of the Logger interface. This implementation extends org.apache.log4j.Logger
+ * in order to take advantage of per-class and per-package configuration options provided by Log4J. 
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLogFactory
+ * @see org.owasp.esapi.reference.Log4JLoggerFactory
+ */
+public class Log4JLogger extends org.apache.log4j.Logger implements org.owasp.esapi.Logger {
+
+	/** The log factory to use in creating new instances. */
+	private static LoggerFactory factory = new Log4JLoggerFactory();
+
+	/** The application name using this log */
+	private static String applicationName = ESAPI.securityConfiguration().getApplicationName();
+
+	/** Log the application name? */
+	private static boolean logAppName = ESAPI.securityConfiguration().getLogApplicationName();
+	
+	/** Log the server ip? */
+	private static boolean logServerIP = ESAPI.securityConfiguration().getLogServerIP();
+
+	public Log4JLogger(String name) {
+		super(name);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getInstance} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static Category getInstance(String name) {
+		return LogManager.getLogger(name, factory);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getInstance} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static Category getInstance(Class clazz) {
+		return LogManager.getLogger(clazz.getName(), factory);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getLogger} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static Logger getLogger(String name) {
+		return LogManager.getLogger(name, factory);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getLogger} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static org.apache.log4j.Logger getLogger(Class clazz) {
+		return LogManager.getLogger(clazz.getName(), factory);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * Note: In this implementation, this change is not persistent,
+	 * meaning that if the application is restarted, the log level will revert to the level defined in the
+	 * ESAPI SecurityConfiguration properties file.
+	 */
+	public void setLevel(int level) {
+		try {
+			super.setLevel(convertESAPILeveltoLoggerLevel(level));
+		} catch (IllegalArgumentException e) {
+			this.error(org.owasp.esapi.Logger.SECURITY_FAILURE, "", e);
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * Explanation: Since this class extends Log4j's Logger class which has a
+	 * {@code getLevel()} method that returns {@code extended by org.apache.log4j.Level},
+	 * we can't simply have a {@code getLevel()} that simply returns an {@code int}.
+	 * Hence we renamed it to {@code getESAPILevel()}.
+	 */
+	public int getESAPILevel() {
+		return super.getLevel().toInt();
+	}
+
+	/**
+	 * Converts the ESAPI logging level (a number) into the levels used by Java's logger.
+	 * @param level The ESAPI to convert.
+	 * @return The Log4J logging Level that is equivalent.
+	 * @throws IllegalArgumentException if the supplied ESAPI level doesn't make a level that is currently defined.
+	 */
+	private static Level convertESAPILeveltoLoggerLevel(int level) {
+		switch (level) {
+		case org.owasp.esapi.Logger.OFF:
+			return Level.OFF;
+		case org.owasp.esapi.Logger.FATAL:
+			return Level.FATAL;
+		case org.owasp.esapi.Logger.ERROR:
+			return Level.ERROR;
+		case org.owasp.esapi.Logger.WARNING:
+			return Level.WARN;
+		case org.owasp.esapi.Logger.INFO:
+			return Level.INFO;
+		case org.owasp.esapi.Logger.DEBUG:
+			return Level.DEBUG; //fine
+		case org.owasp.esapi.Logger.TRACE:
+			return Level.TRACE; //finest
+		case org.owasp.esapi.Logger.ALL:
+			return Level.ALL;
+		default: {
+			throw new IllegalArgumentException("Invalid logging level. Value was: " + level);
+		}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void always(EventType type, String message, Throwable throwable) {
+		log(Level.OFF, type, message, throwable);	// Seems like Level.ALL is what we
+													// would want here, but this is what
+													// works. Level.ALL does not.
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void always(EventType type, String message) {
+		always(type, message, null);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void trace(EventType type, String message, Throwable throwable) {
+		log(Level.TRACE, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void trace(EventType type, String message) {
+		log(Level.TRACE, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void debug(EventType type, String message, Throwable throwable) {
+		log(Level.DEBUG, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void debug(EventType type, String message) {
+		log(Level.DEBUG, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void info(EventType type, String message) {
+		log(Level.INFO, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void info(EventType type, String message, Throwable throwable) {
+		log(Level.INFO, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void warning(EventType type, String message, Throwable throwable) {
+		log(Level.WARN, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void warning(EventType type, String message) {
+		log(Level.WARN, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void error(EventType type, String message, Throwable throwable) {
+		log(Level.ERROR, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void error(EventType type, String message) {
+		log(Level.ERROR, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void fatal(EventType type, String message, Throwable throwable) {
+		log(Level.FATAL, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void fatal(EventType type, String message) {
+		log(Level.FATAL, type, message, null);
+	}
+
+	/**
+	 * Always log the specified message as a {@code SECURITY_AUDIT} event type.
+	 * 
+	 * @param message	The {@code String} representation of the specified message as
+	 * 					logged by calling the object's {@code toString()} method.
+	 */
+	public void always(Object message) {
+		this.always(message, null);
+	}
+
+	/**
+	 * Always log the specified message as a {@code SECURITY_AUDIT} event type, along
+	 * with its associated exception stack trace (if any).
+	 * 
+	 * @param message	The {@code String} representation of the specified message as
+	 * 					logged by calling the object's {@code toString()} method.
+	 */
+	public void always(Object message, Throwable throwable) {
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+		this.always(org.owasp.esapi.Logger.SECURITY_AUDIT, toLog, throwable);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void trace(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.trace(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void trace(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.trace(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void debug(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.debug(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void debug(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.debug(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void info(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.info(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void info(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.info(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void warn(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.warning(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void warn(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.warning(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void error(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.error(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void error(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.error(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fatal(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.fatal(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fatal(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.fatal(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * Log the message after optionally encoding any special characters that might be dangerous when viewed
+	 * by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log
+	 * injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging
+	 * specific session ID, and the current date/time.
+	 *
+	 * It will only log the message if the current logging level is enabled, otherwise it will
+	 * discard the message.
+	 *
+	 * @param level defines the set of recognized logging levels (TRACE, INFO, DEBUG, WARNING, ERROR, FATAL)
+	 * @param type the type of the event (SECURITY SUCCESS, SECURITY FAILURE, EVENT SUCCESS, EVENT FAILURE)
+	 * @param message the message to be logged
+	 * @param throwable the {@code Throwable} from which to generate an exception stack trace.
+	 */
+	private void log(Level level, EventType type, String message, Throwable throwable) {
+
+		// Check to see if we need to log.
+		if (!isEnabledFor(level)) {
+			return;
+		}
+
+		// ensure there's something to log
+		if (message == null) {
+			message = "";
+		}
+
+		// ensure no CRLF injection into logs for forging records
+		String clean = message.replace('\n', '_').replace('\r', '_');
+		if (ESAPI.securityConfiguration().getLogEncodingRequired()) {
+			clean = ESAPI.encoder().encodeForHTML(message);
+			if (!message.equals(clean)) {
+				clean += " (Encoded)";
+			}
+		}
+
+		// log server, port, app name, module name -- server:80/app/module
+		StringBuilder appInfo = new StringBuilder();
+		if (ESAPI.currentRequest() != null && logServerIP) {
+			appInfo.append(ESAPI.currentRequest().getLocalAddr()).append(":").append(ESAPI.currentRequest().getLocalPort());
+		}
+		if (logAppName) {
+			appInfo.append("/").append(applicationName);
+		}
+		appInfo.append("/").append(getName());
+
+		//get the type text if it exists
+		String typeInfo = "";
+		if (type != null) {
+			typeInfo += type + " ";
+		}
+
+		// log the message
+		log(level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isDebugEnabled() {
+		return isEnabledFor(Level.DEBUG);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isErrorEnabled() {
+		return isEnabledFor(Level.ERROR);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isFatalEnabled() {
+		return isEnabledFor(Level.FATAL);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isInfoEnabled() {
+		return isEnabledFor(Level.INFO);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isTraceEnabled() {
+		return isEnabledFor(Level.TRACE);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isWarningEnabled() {
+		return isEnabledFor(Level.WARN);
+	}
+
+	public String getUserInfo() {
+		// create a random session number for the user to represent the user's 'session', if it doesn't exist already
+		String sid = null;
+		HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+		if (request != null) {
+			HttpSession session = request.getSession(false);
+			if (session != null) {
+				sid = (String) session.getAttribute("ESAPI_SESSION");
+				// if there is no session ID for the user yet, we create one and store it in the user's session
+				if (sid == null) {
+					sid = "" + ESAPI.randomizer().getRandomInteger(0, 1000000);
+					session.setAttribute("ESAPI_SESSION", sid);
+				}
+			}
+		}
+
+		// log user information - username:session at ipaddr
+		User user = ESAPI.authenticator().getCurrentUser();
+		String userInfo = "";
+		//TODO - make type logging configurable
+		if (user != null) {
+			userInfo += user.getAccountName() + ":" + sid + "@" + user.getLastHostAddress();
+		}
+
+		return userInfo;
+	}
+	
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLoggerFactory.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLoggerFactory.java.svn-base
new file mode 100644
index 0000000..f5a6713
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/Log4JLoggerFactory.java.svn-base
@@ -0,0 +1,47 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.apache.log4j.spi.LoggerFactory;
+
+/**
+ * Implementation of the LoggerFactory interface. This implementation has been 
+ * overridden to return instances of org.owasp.esapi.reference.Log4JLogger.
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLogFactory
+ * @see org.owasp.esapi.reference.Log4JLogger
+ */
+public class Log4JLoggerFactory implements LoggerFactory {
+
+	/**
+	 * This constructor must be public so it can be accessed from within log4j
+	 */
+	public Log4JLoggerFactory() {}
+
+	/**
+	 * Overridden to return instances of org.owasp.esapi.reference.Log4JLogger.
+	 * 
+	 * @param name The class name to return a logger for.
+	 * @return org.owasp.esapi.reference.Log4JLogger
+	 */
+	public org.apache.log4j.Logger makeNewLoggerInstance(String name) {
+		return new Log4JLogger(name);
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/RandomAccessReferenceMap.java.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/RandomAccessReferenceMap.java.svn-base
new file mode 100644
index 0000000..28c3cb8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/RandomAccessReferenceMap.java.svn-base
@@ -0,0 +1,85 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+
+import java.util.Set;
+
+/**
+ * Reference implementation of the AccessReferenceMap interface. This
+ * implementation generates random 6 character alphanumeric strings for indirect
+ * references. It is possible to use simple integers as indirect references, but
+ * the random string approach provides a certain level of protection from CSRF
+ * attacks, because an attacker would have difficulty guessing the indirect
+ * reference.
+ *
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author Chris Schmidt (chrisisbeef at gmail.com)
+ * @see org.owasp.esapi.AccessReferenceMap
+ * @since June 1, 2007
+ */
+public class RandomAccessReferenceMap extends AbstractAccessReferenceMap<String>
+{
+
+   private static final long serialVersionUID = 8544133840739803001L;
+
+   public RandomAccessReferenceMap(int initialSize)
+   {
+      super(initialSize);
+   }
+
+   /**
+    * This AccessReferenceMap implementation uses short random strings to
+    * create a layer of indirection. Other possible implementations would use
+    * simple integers as indirect references.
+    */
+   public RandomAccessReferenceMap()
+   {
+      // call update to set up the references
+   }
+
+   public RandomAccessReferenceMap(Set<Object> directReferences)
+   {
+      super(directReferences.size());
+      update(directReferences);
+   }
+
+   public RandomAccessReferenceMap(Set<Object> directReferences, int initialSize)
+   {
+      super(initialSize);
+      update(directReferences);
+   }
+
+   /**
+    * {@inheritDoc}
+    * Note: this is final as redefinition by subclasses can lead to use
+    * before initialization issues as
+    * {@link #RandomAccessReferenceMap(Set)} and
+    * {@link #RandomAccessReferenceMap(Set,int)} both call it internally.
+    */
+   protected final synchronized String getUniqueReference()
+   {
+      String candidate;
+      do
+      {
+         candidate = ESAPI.randomizer().getRandomString(6, EncoderConstants.CHAR_ALPHANUMERICS);
+      }
+      while (itod.keySet().contains(candidate));
+      return candidate;
+   }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/reference/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..0e110cb
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/.svn/text-base/package.html.svn-base
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains reference implementations of the ESAPI interfaces. These are intended to
+serve as examples of how your enterprise might implement these functions. The reference implementations
+are high quality and pass all of the ESAPI test cases. Many of the reference implementations are
+likely to be useful in your enterprise without change (Validator, Encoder, Encryptor, etc...).
+Implementing other classes (Authenticator, User, AccessController, Logger, etc...) will likely need to
+be customized for your enterprise, to integrate with your backend systems and policies.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/reference/AbstractAccessReferenceMap.java b/src/main/java/org/owasp/esapi/reference/AbstractAccessReferenceMap.java
new file mode 100644
index 0000000..b8fc30e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/AbstractAccessReferenceMap.java
@@ -0,0 +1,204 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.AccessReferenceMap;
+import org.owasp.esapi.errors.AccessControlException;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Abstract Implementation of the AccessReferenceMap that is backed by ConcurrentHashMaps to
+ * provide a thread-safe implementation of the AccessReferenceMap. Implementations of this
+ * abstract class should implement the #getUniqueReference() method.
+ *
+ * @author  Chris Schmidt (chrisisbeef at gmail.com)
+ * @since   July 21, 2009
+ */
+public abstract class AbstractAccessReferenceMap<K> implements AccessReferenceMap<K>
+{
+   private static final long serialVersionUID = 238742764284682230L;
+
+   /** The Indirect to Direct Map */
+   protected Map<K,Object> itod;
+   /** The Direct to Indirect Map */
+   protected Map<Object,K> dtoi;
+
+   /**
+    * Instantiates a new access reference map. Note that this will create the underlying Maps with an initialSize
+    * of {@link ConcurrentHashMap#DEFAULT_INITIAL_CAPACITY} and that resizing a Map is an expensive process. Consider
+    * using a constructor where the initialSize is passed in to maximize performance of the AccessReferenceMap.
+    *
+    * @see #AbstractAccessReferenceMap(java.util.Set, int)
+    * @see #AbstractAccessReferenceMap(int)
+    */
+   public AbstractAccessReferenceMap() {
+      itod = new ConcurrentHashMap<K, Object>();
+      dtoi = new ConcurrentHashMap<Object,K>();
+   }
+
+   /**
+    * Instantiates a new access reference map with the specified size allotment
+    * to reduce Map resizing overhead.
+    *
+    * @param initialSize
+    *          The initial size of the underlying maps
+    */
+   public AbstractAccessReferenceMap( int initialSize ) {
+      itod = new ConcurrentHashMap<K, Object>(initialSize);
+      dtoi = new ConcurrentHashMap<Object,K>(initialSize);
+   }
+
+   /**
+    * Instantiates a new access reference map with a set of direct references.
+    *
+    * @param directReferences
+    *            the direct references
+    * @deprecated This constructor internally calls the abstract method
+    *	{@link #getUniqueReference()}. Since this is a constructor, any
+    *	subclass that implements getUniqueReference() has not had it's
+    *	own constructor run. This leads to strange bugs because subclass
+    *	internal state is initializaed after calls to getUniqueReference()
+    *	have already happened. If this constructor is desired in a
+    *	subclass, consider running {@link #update(Set)} in the subclass
+    *	constructor instead.
+    */
+   @Deprecated
+   public AbstractAccessReferenceMap( Set<Object> directReferences ) {
+      itod = new ConcurrentHashMap<K, Object>(directReferences.size());
+      dtoi = new ConcurrentHashMap<Object,K>(directReferences.size());
+      update(directReferences);
+   }
+
+   /**
+    * Instantiates a new access reference map with the specified size allotment
+    * and initializes the map with the passed in references. Note that if you pass
+    * in an initialSize that is less than the size of the passed in set, the map will
+    * need to be resized while it is being loaded with the references so it is
+    * best practice to verify that the size being passed in is always larger than
+    * the size of the set that is being passed in.
+    *
+    * @param directReferences
+    *          The references to initialize the access reference map
+    * @param initialSize
+    *          The initial size to set the map to.
+    *
+    * @deprecated This constructor internally calls the abstract method
+    *	{@link #getUniqueReference()}. Since this is a constructor, any
+    *	subclass that implements getUniqueReference() has not had it's
+    *	own constructor run. This leads to strange bugs because subclass
+    *	internal state is initializaed after calls to getUniqueReference()
+    *	have already happened. If this constructor is desired in a
+    *	subclass, consider running {@link #update(Set)} in the subclass
+    *	constructor instead.
+    */
+   @Deprecated
+   public AbstractAccessReferenceMap( Set<Object> directReferences, int initialSize ) {
+      itod = new ConcurrentHashMap<K, Object>(initialSize);
+      dtoi = new ConcurrentHashMap<Object,K>(initialSize);
+      update(directReferences);
+   }
+
+   /**
+    * Returns a Unique Reference Key to be associated with a new directReference being
+    * inserted into the AccessReferenceMap.
+    *
+    * @return Reference Identifier
+    */
+   protected abstract K getUniqueReference();
+
+   /**
+   * {@inheritDoc}
+   */
+   public synchronized Iterator iterator() {
+      TreeSet sorted = new TreeSet(dtoi.keySet());
+      return sorted.iterator();
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> K addDirectReference(T direct) {
+      if ( dtoi.keySet().contains( direct ) ) {
+         return dtoi.get( direct );
+      }
+      K indirect = getUniqueReference();
+      itod.put(indirect, direct);
+      dtoi.put(direct, indirect);
+      return indirect;
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> K removeDirectReference(T direct) throws AccessControlException
+   {
+      K indirect = dtoi.get(direct);
+      if ( indirect != null ) {
+         itod.remove(indirect);
+         dtoi.remove(direct);
+      }
+      return indirect;
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public final synchronized void update(Set directReferences) {
+      Map<Object,K> new_dtoi = new ConcurrentHashMap<Object,K>( directReferences.size() );
+      Map<K,Object> new_itod = new ConcurrentHashMap<K,Object>( directReferences.size() );
+
+      for ( Object o : directReferences ) {
+         K indirect = dtoi.get( o );
+
+         if ( indirect == null ) {
+            indirect = getUniqueReference();
+         }
+         new_dtoi.put( o, indirect );
+         new_itod.put( indirect, o );
+      }
+      dtoi = new_dtoi;
+      itod = new_itod;
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> K getIndirectReference(T directReference) {
+      return dtoi.get(directReference);
+   }
+
+   /**
+   * {@inheritDoc}
+   */
+   public <T> T getDirectReference(K indirectReference) throws AccessControlException {
+      if (itod.containsKey(indirectReference) ) {
+         try
+         {
+            return (T) itod.get(indirectReference);
+         }
+         catch (ClassCastException e)
+         {
+            throw new AccessControlException("Access denied.", "Request for incorrect type reference: " + indirectReference);
+         }
+      }
+      throw new AccessControlException("Access denied", "Request for invalid indirect reference: " + indirectReference);
+   }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java b/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java
new file mode 100644
index 0000000..4876da4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java
@@ -0,0 +1,297 @@
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationCredentialsException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.AuthenticationLoginException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+/**
+ * A partial implementation of the Authenticator interface.
+ * This class should not implement any methods that would be meant
+ * to modify a User object, since that's probably implementation specific.
+ *
+ */
+public abstract class AbstractAuthenticator implements org.owasp.esapi.Authenticator {
+
+	/**
+     * Key for user in session
+     */
+    protected static final String USER = "ESAPIUserSessionKey";
+    
+    private final Logger logger = ESAPI.getLogger("Authenticator");
+    
+    /**
+     * The currentUser ThreadLocal variable is used to make the currentUser available to any call in any part of an
+     * application. Otherwise, each thread would have to pass the User object through the calltree to any methods that
+     * need it. Because we want exceptions and log calls to contain user data, that could be almost anywhere. Therefore,
+     * the ThreadLocal approach simplifies things greatly. <P> As a possible extension, one could create a delegation
+     * framework by adding another ThreadLocal to hold the delegating user identity.
+     */
+    private final ThreadLocalUser currentUser = new ThreadLocalUser();
+
+    private class ThreadLocalUser extends InheritableThreadLocal<User> {
+
+        public User initialValue() {
+            return User.ANONYMOUS;
+        }
+
+        public User getUser() {
+            return super.get();
+        }
+
+        public void setUser(User newUser) {
+            super.set(newUser);
+        }
+    }
+    
+	/**
+	 *
+	 */
+	public AbstractAuthenticator() {
+		super();
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+    public void clearCurrent() {
+        // logger.logWarning(Logger.SECURITY, "************Clearing threadlocals. Thread" + Thread.currentThread().getName() );
+        currentUser.setUser(null);
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(String accountName) {
+        return getUser(accountName) != null;
+    }
+    
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Returns the currently logged user as set by the setCurrentUser() methods. Must not log in this method because the
+     * logger calls getCurrentUser() and this could cause a loop.
+     */
+    public User getCurrentUser() {
+        User user = currentUser.get();
+        if (user == null) {
+            user = User.ANONYMOUS;
+        }
+        return user;
+    }
+    
+    /**
+     * Gets the user from session.
+     *
+     * @return the user from session or null if no user is found in the session
+     */
+    protected User getUserFromSession() {
+        HttpSession session = ESAPI.httpUtilities().getCurrentRequest().getSession(false);
+        if (session == null) return null;
+        return ESAPI.httpUtilities().getSessionAttribute(USER);
+    }
+    
+    /**
+     * Returns the user if a matching remember token is found, or null if the token
+     * is missing, token is corrupt, token is expired, account name does not match
+     * and existing account, or hashed password does not match user's hashed password.
+     *
+     * @return the user if a matching remember token is found, or null if the token
+     *         is missing, token is corrupt, token is expired, account name does not match
+     *         and existing account, or hashed password does not match user's hashed password.
+     */
+    protected DefaultUser getUserFromRememberToken() {
+        try {
+            String token = ESAPI.httpUtilities().getCookie(ESAPI.currentRequest(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
+            if (token == null) return null;
+            
+            // See Google Issue 144 regarding first URLDecode the token and THEN unsealing.
+            // Note that this Google Issue was marked as "WontFix".
+
+            String[] data = ESAPI.encryptor().unseal(token).split("\\|");
+            if (data.length != 2) {
+                logger.warning(Logger.SECURITY_FAILURE, "Found corrupt or expired remember token");
+                ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
+                return null;
+            }
+
+            String username = data[0];
+            String password = data[1];
+            DefaultUser user = (DefaultUser) getUser(username);
+            if (user == null) {
+                logger.warning(Logger.SECURITY_FAILURE, "Found valid remember token but no user matching " + username);
+                return null;
+            }
+
+            logger.info(Logger.SECURITY_SUCCESS, "Logging in user with remember token: " + user.getAccountName());
+            user.loginWithPassword(password);
+            return user;
+        } catch (AuthenticationException ae) {
+            logger.warning(Logger.SECURITY_FAILURE, "Login via remember me cookie failed", ae);
+        } catch (EnterpriseSecurityException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Remember token was missing, corrupt, or expired");
+        }
+        ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
+        return null;
+    }
+    
+    /**
+     * Utility method to extract credentials and verify them.
+     *
+     * @param request The current HTTP request
+     * @return The user that successfully authenticated
+     * @throws AuthenticationException if the submitted credentials are invalid.
+     */
+    private User loginWithUsernameAndPassword(HttpServletRequest request) throws AuthenticationException {
+
+        String username = request.getParameter(ESAPI.securityConfiguration().getUsernameParameterName());
+        String password = request.getParameter(ESAPI.securityConfiguration().getPasswordParameterName());
+
+        // if a logged-in user is requesting to login, log them out first
+        User user = getCurrentUser();
+        if (user != null && !user.isAnonymous()) {
+            logger.warning(Logger.SECURITY_SUCCESS, "User requested relogin. Performing logout then authentication");
+            user.logout();
+        }
+
+        // now authenticate with username and password
+        if (username == null || password == null) {
+            if (username == null) {
+                username = "unspecified user";
+            }
+            throw new AuthenticationCredentialsException("Authentication failed", "Authentication failed for " + username + " because of null username or password");
+        }
+        user = getUser(username);
+        if (user == null) {
+            throw new AuthenticationCredentialsException("Authentication failed", "Authentication failed because user " + username + " doesn't exist");
+        }
+        user.loginWithPassword(password);
+
+        request.setAttribute(user.getCSRFToken(), "authenticated");
+        return user;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public User login() throws AuthenticationException {
+        return login(ESAPI.currentRequest(), ESAPI.currentResponse());
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
+
+        if (request == null || response == null) {
+            throw new AuthenticationCredentialsException("Invalid request", "Request or response objects were null");
+        }
+
+        // if there's a user in the session then use that
+        DefaultUser user = (DefaultUser) getUserFromSession();
+
+        // else if there's a remember token then use that
+        if (user == null) {
+            user = getUserFromRememberToken();
+        }
+
+        // else try to verify credentials - throws exception if login fails
+        if (user == null) {
+            user = (DefaultUser) loginWithUsernameAndPassword(request);
+        }
+
+        // set last host address
+        user.setLastHostAddress(request.getRemoteHost());
+
+        // warn if this authentication request was not POST or non-SSL connection, exposing credentials or session id
+        try {
+            ESAPI.httpUtilities().assertSecureRequest(ESAPI.currentRequest());
+        } catch (AccessControlException e) {
+            throw new AuthenticationException("Attempt to login with an insecure request", e.getLogMessage(), e);
+        }
+
+        // don't let anonymous user log in
+        if (user.isAnonymous()) {
+            user.logout();
+            throw new AuthenticationLoginException("Login failed", "Anonymous user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // don't let disabled users log in
+        if (!user.isEnabled()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Disabled user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // don't let locked users log in
+        if (user.isLocked()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Locked user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // don't let expired users log in
+        if (user.isExpired()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Expired user cannot be set to current user. User: " + user.getAccountName());
+        }
+
+        // check session inactivity timeout
+        if (user.isSessionTimeout()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Session inactivity timeout: " + user.getAccountName());
+        }
+
+        // check session absolute timeout
+        if (user.isSessionAbsoluteTimeout()) {
+            user.logout();
+            user.incrementFailedLoginCount();
+            user.setLastFailedLoginTime(new Date());
+            throw new AuthenticationLoginException("Login failed", "Session absolute timeout: " + user.getAccountName());
+        }
+
+        //set Locale to the user object in the session from request
+        user.setLocale(request.getLocale());
+
+        // create new session for this User
+        HttpSession session = request.getSession();
+        user.addSession(session);
+        session.setAttribute(USER, user);
+        setCurrentUser(user);
+        return user;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void logout() {
+        User user = getCurrentUser();
+        if (user != null && !user.isAnonymous()) {
+            user.logout();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCurrentUser(User user) {
+        currentUser.setUser(user);
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultAccessController.java b/src/main/java/org/owasp/esapi/reference/DefaultAccessController.java
new file mode 100644
index 0000000..3d51338
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultAccessController.java
@@ -0,0 +1,146 @@
+package org.owasp.esapi.reference;
+
+import java.util.Map;
+
+import org.owasp.esapi.AccessControlRule;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.reference.accesscontrol.policyloader.ACRPolicyFileLoader;
+import org.owasp.esapi.reference.accesscontrol.policyloader.PolicyDTO;
+
+public class DefaultAccessController implements AccessController {
+	private Map ruleMap;
+
+    private static volatile AccessController singletonInstance = null;
+
+    public static AccessController getInstance() throws AccessControlException {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultAccessController.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultAccessController();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+	protected final Logger logger = ESAPI.getLogger("DefaultAccessController");
+
+	private DefaultAccessController() throws AccessControlException {
+		ACRPolicyFileLoader policyDescriptor = new ACRPolicyFileLoader();
+		PolicyDTO policyDTO = policyDescriptor.load();		
+		ruleMap = policyDTO.getAccessControlRules();
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+	public boolean isAuthorized(Object key, Object runtimeParameter) {
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied",
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Evaluating Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			return rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			try {
+				//Log the exception by throwing and then catching it.
+				//TODO figure out what which string goes where.		
+				throw new AccessControlException("Access Denied",
+					"An unhandled Exception was " +
+					"caught, so access is denied.",  
+					e);	
+			} catch(AccessControlException ace) {
+				//the exception was just logged. There's nothing left to do.
+			}
+			return false; //fail closed
+		}
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorized(Object key, Object runtimeParameter) throws AccessControlException {
+		boolean isAuthorized;
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied", 
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Asserting Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			isAuthorized = rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			//TODO figure out what which string goes where.		
+			throw new AccessControlException("Access Denied", "An unhandled Exception was " +
+					"caught, so access is denied." +
+					"AccessControlException.",
+					e);
+		}
+		if(!isAuthorized) {
+			throw new AccessControlException("Access Denied", 
+					"Access Denied for key: " + key + 
+					" runtimeParameter: " + runtimeParameter);
+		}
+	}
+	
+    /** {@inheritDoc} */
+	public void assertAuthorizedForData(String action, Object data)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+	/**
+     * {@inheritDoc}
+	 * @deprecated
+	 */
+	public void assertAuthorizedForFile(String filepath)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorizedForFunction(String functionName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorizedForService(String serviceName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+    /** {@inheritDoc} */
+	public void assertAuthorizedForURL(String url)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForData(String action, Object data) {
+		return this.isAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForFile(String filepath) {
+		return this.isAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForFunction(String functionName) {
+		return this.isAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForService(String serviceName) {
+		return this.isAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+    /** {@inheritDoc} */
+	public boolean isAuthorizedForURL(String url) {
+		return this.isAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java
new file mode 100644
index 0000000..4fadcf0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java
@@ -0,0 +1,448 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.codecs.Base64;
+import org.owasp.esapi.codecs.CSSCodec;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.HTMLEntityCodec;
+import org.owasp.esapi.codecs.JavaScriptCodec;
+import org.owasp.esapi.codecs.PercentCodec;
+import org.owasp.esapi.codecs.VBScriptCodec;
+import org.owasp.esapi.codecs.XMLEntityCodec;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.IntrusionException;
+
+
+/**
+ * Reference implementation of the Encoder interface. This implementation takes
+ * a whitelist approach to encoding, meaning that everything not specifically identified in a
+ * list of "immune" characters is encoded.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Encoder
+ */
+public class DefaultEncoder implements Encoder {
+
+    private static volatile Encoder singletonInstance;
+
+    public static Encoder getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultEncoder.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultEncoder();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+	// Codecs
+	private List codecs = new ArrayList();
+	private HTMLEntityCodec htmlCodec = new HTMLEntityCodec();
+	private XMLEntityCodec xmlCodec = new XMLEntityCodec();
+	private PercentCodec percentCodec = new PercentCodec();
+	private JavaScriptCodec javaScriptCodec = new JavaScriptCodec();
+	private VBScriptCodec vbScriptCodec = new VBScriptCodec();
+	private CSSCodec cssCodec = new CSSCodec();
+
+	private final Logger logger = ESAPI.getLogger("Encoder");
+	
+	/**
+	 *  Character sets that define characters (in addition to alphanumerics) that are
+	 * immune from encoding in various formats
+	 */
+	private final static char[]     IMMUNE_HTML = { ',', '.', '-', '_', ' ' };
+	private final static char[] IMMUNE_HTMLATTR = { ',', '.', '-', '_' };
+	private final static char[] IMMUNE_CSS = {};
+	private final static char[] IMMUNE_JAVASCRIPT = { ',', '.', '_' };
+	private final static char[] IMMUNE_VBSCRIPT = { ',', '.', '_' };
+	private final static char[] IMMUNE_XML = { ',', '.', '-', '_', ' ' };
+	private final static char[] IMMUNE_SQL = { ' ' };
+	private final static char[] IMMUNE_OS = { '-' };
+	private final static char[] IMMUNE_XMLATTR = { ',', '.', '-', '_' };
+	private final static char[] IMMUNE_XPATH = { ',', '.', '-', '_', ' ' };
+	
+	
+	/**
+	 * Instantiates a new DefaultEncoder
+	 */
+	private DefaultEncoder() {
+		codecs.add( htmlCodec );
+		codecs.add( percentCodec );
+		codecs.add( javaScriptCodec );
+	}
+	
+	public DefaultEncoder( List<String> codecNames ) {
+		for ( String clazz : codecNames ) {
+			try {
+				if ( clazz.indexOf( '.' ) == -1 ) clazz = "org.owasp.esapi.codecs." + clazz;
+				codecs.add( Class.forName( clazz ).newInstance() );
+			} catch ( Exception e ) {
+				logger.warning( Logger.EVENT_FAILURE, "Codec " + clazz + " listed in ESAPI.properties not on classpath" );
+			}
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String canonicalize( String input ) {
+		if ( input == null ) {
+			return null;
+		}
+
+        // Issue 231 - These are reverse boolean logic in the Encoder interface, so we need to invert these values - CS
+		return canonicalize(input, 
+							!ESAPI.securityConfiguration().getAllowMultipleEncoding(),
+							!ESAPI.securityConfiguration().getAllowMixedEncoding() );
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String canonicalize( String input, boolean strict) {
+		return canonicalize(input, strict, strict);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String canonicalize( String input, boolean restrictMultiple, boolean restrictMixed ) {
+		if ( input == null ) {
+			return null;
+		}
+		
+        String working = input;
+        Codec codecFound = null;
+        int mixedCount = 1;
+        int foundCount = 0;
+        boolean clean = false;
+        while( !clean ) {
+            clean = true;
+            
+            // try each codec and keep track of which ones work
+            Iterator i = codecs.iterator();
+            while ( i.hasNext() ) {
+                Codec codec = (Codec)i.next();
+                String old = working;
+                working = codec.decode( working );
+                if ( !old.equals( working ) ) {
+                    if ( codecFound != null && codecFound != codec ) {
+                        mixedCount++;
+                    }
+                    codecFound = codec;
+                    if ( clean ) {
+                        foundCount++;
+                    }
+                    clean = false;
+                }
+            }
+        }
+        
+        // do strict tests and handle if any mixed, multiple, nested encoding were found
+        if ( foundCount >= 2 && mixedCount > 1 ) {
+            if ( restrictMultiple || restrictMixed ) {
+                throw new IntrusionException( "Input validation failure", "Multiple ("+ foundCount +"x) and mixed encoding ("+ mixedCount +"x) detected in " + input );
+            } else {
+                logger.warning( Logger.SECURITY_FAILURE, "Multiple ("+ foundCount +"x) and mixed encoding ("+ mixedCount +"x) detected in " + input );
+            }
+        }
+        else if ( foundCount >= 2 ) {
+            if ( restrictMultiple ) {
+                throw new IntrusionException( "Input validation failure", "Multiple ("+ foundCount +"x) encoding detected in " + input );
+            } else {
+                logger.warning( Logger.SECURITY_FAILURE, "Multiple ("+ foundCount +"x) encoding detected in " + input );
+            }
+        }
+        else if ( mixedCount > 1 ) {
+            if ( restrictMixed ) {
+                throw new IntrusionException( "Input validation failure", "Mixed encoding ("+ mixedCount +"x) detected in " + input );
+            } else {
+                logger.warning( Logger.SECURITY_FAILURE, "Mixed encoding ("+ mixedCount +"x) detected in " + input );
+            }
+        }
+        return working;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForHTML(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return htmlCodec.encode( IMMUNE_HTML, input);	    
+	 }
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String decodeForHTML(String input) {
+		
+		if( input == null ) {
+	    	return null;
+	    }
+	    return htmlCodec.decode( input);	 
+    }
+	 
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForHTMLAttribute(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return htmlCodec.encode( IMMUNE_HTMLATTR, input);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForCSS(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return cssCodec.encode( IMMUNE_CSS, input);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForJavaScript(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return javaScriptCodec.encode(IMMUNE_JAVASCRIPT, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForVBScript(String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return vbScriptCodec.encode(IMMUNE_VBSCRIPT, input);	    
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForSQL(Codec codec, String input) {
+	    if( input == null ) {
+	    	return null;
+	    }
+	    return codec.encode(IMMUNE_SQL, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForOS(Codec codec, String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return codec.encode( IMMUNE_OS, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForLDAP(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+		// TODO: replace with LDAP codec
+	    StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			switch (c) {
+			case '\\':
+				sb.append("\\5c");
+				break;
+			case '*':
+				sb.append("\\2a");
+				break;
+			case '(':
+				sb.append("\\28");
+				break;
+			case ')':
+				sb.append("\\29");
+				break;
+			case '\0':
+				sb.append("\\00");
+				break;
+			default:
+				sb.append(c);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForDN(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+		// TODO: replace with DN codec
+	    StringBuilder sb = new StringBuilder();
+		if ((input.length() > 0) && ((input.charAt(0) == ' ') || (input.charAt(0) == '#'))) {
+			sb.append('\\'); // add the leading backslash if needed
+		}
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			switch (c) {
+			case '\\':
+				sb.append("\\\\");
+				break;
+			case ',':
+				sb.append("\\,");
+				break;
+			case '+':
+				sb.append("\\+");
+				break;
+			case '"':
+				sb.append("\\\"");
+				break;
+			case '<':
+				sb.append("\\<");
+				break;
+			case '>':
+				sb.append("\\>");
+				break;
+			case ';':
+				sb.append("\\;");
+				break;
+			default:
+				sb.append(c);
+			}
+		}
+		// add the trailing backslash if needed
+		if ((input.length() > 1) && (input.charAt(input.length() - 1) == ' ')) {
+			sb.insert(sb.length() - 1, '\\');
+		}
+		return sb.toString();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForXPath(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return htmlCodec.encode( IMMUNE_XPATH, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForXML(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return xmlCodec.encode( IMMUNE_XML, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForXMLAttribute(String input) {
+	    if( input == null ) {
+	    	return null;	
+	    }
+	    return xmlCodec.encode( IMMUNE_XMLATTR, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForURL(String input) throws EncodingException {
+		if ( input == null ) {
+			return null;
+		}
+		try {
+			return URLEncoder.encode(input, ESAPI.securityConfiguration().getCharacterEncoding());
+		} catch (UnsupportedEncodingException ex) {
+			throw new EncodingException("Encoding failure", "Character encoding not supported", ex);
+		} catch (Exception e) {
+			throw new EncodingException("Encoding failure", "Problem URL encoding input", e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String decodeFromURL(String input) throws EncodingException {
+		if ( input == null ) {
+			return null;
+		}
+		String canonical = canonicalize(input);
+		try {
+			return URLDecoder.decode(canonical, ESAPI.securityConfiguration().getCharacterEncoding());
+		} catch (UnsupportedEncodingException ex) {
+			throw new EncodingException("Decoding failed", "Character encoding not supported", ex);
+		} catch (Exception e) {
+			throw new EncodingException("Decoding failed", "Problem URL decoding input", e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encodeForBase64(byte[] input, boolean wrap) {
+		if ( input == null ) {
+			return null;
+		}
+		int options = 0;
+		if ( !wrap ) {
+			options |= Base64.DONT_BREAK_LINES;
+		}
+		return Base64.encodeBytes(input, options);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public byte[] decodeFromBase64(String input) throws IOException {
+		if ( input == null ) {
+			return null;
+		}
+		return Base64.decode( input );
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java b/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java
new file mode 100644
index 0000000..9f49765
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java
@@ -0,0 +1,234 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Map;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.ExecuteResult;
+import org.owasp.esapi.Executor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.UnixCodec;
+import org.owasp.esapi.codecs.WindowsCodec;
+import org.owasp.esapi.errors.ExecutorException;
+
+/**
+ * Reference implementation of the Executor interface. This implementation is very restrictive. Commands must exactly
+ * equal the canonical path to an executable on the system. 
+ * 
+ * <p>Valid characters for parameters are codec dependent, but will usually only include alphanumeric, forward-slash, and dash.</p>
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Executor
+ */
+public class DefaultExecutor implements org.owasp.esapi.Executor {
+    private static volatile Executor singletonInstance;
+
+    public static Executor getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultExecutor.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultExecutor();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    /** The logger. */
+    private final Logger logger = ESAPI.getLogger("Executor");
+    private Codec codec = null;
+    //private final int MAX_SYSTEM_COMMAND_LENGTH = 2500;
+    
+
+    /**
+     * Instantiate a new Executor
+     */
+    private DefaultExecutor() {
+		if ( System.getProperty("os.name").indexOf("Windows") != -1 ) {
+			logger.warning( Logger.SECURITY_SUCCESS, "Using WindowsCodec for Executor. If this is not running on Windows this could allow injection" );
+			codec = new WindowsCodec();
+		} else {
+			logger.warning( Logger.SECURITY_SUCCESS, "Using UnixCodec for Executor. If this is not running on Unix this could allow injection" );
+			codec = new UnixCodec();
+		}
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ExecuteResult executeSystemCommand(File executable, List params) throws ExecutorException {
+    	File workdir = ESAPI.securityConfiguration().getWorkingDirectory();
+    	boolean logParams = false;
+    	boolean redirectErrorStream = false;
+    	return executeSystemCommand( executable, params, workdir, codec, logParams, redirectErrorStream );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * The reference implementation sets the work directory, escapes the parameters as per the Codec in use,
+     * and then executes the command without using concatenation. The exact, absolute, canonical path of each
+     * executable must be listed as an approved executable in the ESAPI properties. The executable must also
+     * exist on the disk. All failures will be logged, along with parameters if specified. Set the logParams to false if
+     * you are going to invoke this interface with confidential information.
+     */
+    public ExecuteResult executeSystemCommand(File executable, List params, File workdir, Codec codec, boolean logParams, boolean redirectErrorStream ) throws ExecutorException {
+        try {
+            // executable must exist
+            if (!executable.exists()) {
+                throw new ExecutorException("Execution failure", "No such executable: " + executable);
+            }
+            
+            // executable must use canonical path
+            if ( !executable.isAbsolute() ) {
+                throw new ExecutorException("Execution failure", "Attempt to invoke an executable using a non-absolute path: " + executable);
+            }
+            
+            // executable must use canonical path
+            if ( !executable.getPath().equals( executable.getCanonicalPath() ) ) {
+            	throw new ExecutorException("Execution failure", "Attempt to invoke an executable using a non-canonical path: " + executable);
+        	}
+            
+            // exact, absolute, canonical path to executable must be listed in ESAPI configuration 
+            List approved = ESAPI.securityConfiguration().getAllowedExecutables();
+            if (!approved.contains(executable.getPath())) {
+                throw new ExecutorException("Execution failure", "Attempt to invoke executable that is not listed as an approved executable in ESAPI configuration: " + executable.getPath() + " not listed in " + approved );
+            }
+
+            // escape any special characters in the parameters
+            for ( int i = 0; i < params.size(); i++ ) {
+            	String param = (String)params.get(i);
+            	params.set( i, ESAPI.encoder().encodeForOS(codec, param));
+            }
+            
+            // working directory must exist
+            if (!workdir.exists()) {
+                throw new ExecutorException("Execution failure", "No such working directory for running executable: " + workdir.getPath());
+            }
+            
+            // set the command into the list and create command array
+            params.add(0, executable.getCanonicalPath());
+
+            // Legacy - this is how to implement in Java 1.4
+            // String[] command = (String[])params.toArray( new String[0] );
+            // Process process = Runtime.getRuntime().exec(command, new String[0], workdir);
+            
+            // The following is host to implement in Java 1.5+
+            ProcessBuilder pb = new ProcessBuilder(params);
+            Map env = pb.environment();
+            env.clear();  // Security check - clear environment variables!
+            pb.directory(workdir);
+            pb.redirectErrorStream(redirectErrorStream);
+
+            if ( logParams ) {
+            	logger.warning(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " " + params + " in " + workdir);
+            } else {
+            	logger.warning(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " [sensitive parameters obscured] in " + workdir);
+            }
+
+            final StringBuilder outputBuffer = new StringBuilder();
+            final StringBuilder errorsBuffer = new StringBuilder();
+            final Process process = pb.start();
+            try {
+                ReadThread errorReader;
+                if (!redirectErrorStream) {
+                	errorReader = new ReadThread(process.getErrorStream(), errorsBuffer);
+                	errorReader.start();
+                } else {
+                	errorReader = null;
+                }
+            	readStream( process.getInputStream(), outputBuffer );
+            	if (errorReader != null) {
+            		errorReader.join();
+            		if (errorReader.exception != null) {
+            			throw errorReader.exception;
+            		}
+            	}
+            	process.waitFor();
+            } catch (Throwable e) {
+            	process.destroy();
+            	throw new ExecutorException("Execution failure", "Exception thrown during execution of system command: " + e.getMessage(), e);
+            }
+
+            String output = outputBuffer.toString();
+            String errors = errorsBuffer.toString();
+            int exitValue = process.exitValue();
+            if ( errors != null && errors.length() > 0 ) {
+            	String logErrors = errors;
+            	final int MAX_LEN = 256;
+            	if (logErrors.length() > MAX_LEN) {
+            		logErrors = logErrors.substring(0, MAX_LEN) + "(truncated at "+MAX_LEN+" characters)";
+            	}
+            	logger.warning( Logger.SECURITY_SUCCESS, "Error during system command: " + logErrors );
+            }
+            if ( exitValue != 0 ) {
+            	logger.warning( Logger.EVENT_FAILURE, "System command exited with non-zero status: " + exitValue );
+            }
+
+            logger.warning(Logger.SECURITY_SUCCESS, "System command complete");
+            return new ExecuteResult(exitValue, output, errors);
+        } catch (IOException e) {
+            throw new ExecutorException("Execution failure", "Exception thrown during execution of system command: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * readStream reads lines from an input stream and returns all of them in a single string
+     * 
+     * @param is
+     * 			input stream to read from
+     * @throws IOException
+     */
+    private static void readStream( InputStream is, StringBuilder sb ) throws IOException {
+	    InputStreamReader isr = new InputStreamReader(is);
+	    BufferedReader br = new BufferedReader(isr);
+	    String line;
+	    while ((line = br.readLine()) != null) {
+	        sb.append(line).append('\n');
+	    }
+    }
+    
+    private static class ReadThread extends Thread {
+    	volatile IOException exception;
+    	private final InputStream stream;
+    	private final StringBuilder buffer;
+
+    	ReadThread(InputStream stream, StringBuilder buffer) {
+    		this.stream = stream;
+    		this.buffer = buffer;
+    	}
+
+    	@Override
+		public void run() {
+    		try {
+    			readStream(stream, buffer);
+    		} catch (IOException e) {
+    			exception = e;
+    		}
+    	}
+
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java
new file mode 100644
index 0000000..dc99d76
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java
@@ -0,0 +1,1022 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.ProgressListener;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.owasp.esapi.*;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.*;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Reference implementation of the HTTPUtilities interface. This implementation
+ * uses the Apache Commons FileUploader library, which in turn uses the Apache
+ * Commons IO library.
+ * <P>
+ * To simplify the interface, some methods use the current request and response that
+ * are tracked by ThreadLocal variables in the Authenticator. This means that you
+ * must have called ESAPI.authenticator().setCurrentHTTP(request, response) before
+ * calling these methods.
+ * <P>
+ * Typically, this is done by calling the Authenticator.login() method, which
+ * calls setCurrentHTTP() automatically. However if you want to use these methods
+ * in another application, you should explicitly call setCurrentHTTP() in your
+ * own code. In either case, you *must* call ESAPI.clearCurrent() to clear threadlocal
+ * variables before the thread is reused. The advantages of having identity everywhere
+ * outweigh the disadvantages of this approach.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.HTTPUtilities
+ */
+public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities {
+    private static volatile HTTPUtilities instance = null;
+
+    public static HTTPUtilities getInstance() {
+        if ( instance == null ) {
+            synchronized ( DefaultHTTPUtilities.class ) {
+                if ( instance == null ) {
+                    instance = new DefaultHTTPUtilities();
+                }
+            }
+        }
+        return instance;
+    }
+
+	/**
+     * Defines the ThreadLocalRequest to store the current request for this thread.
+     */
+    private class ThreadLocalRequest extends InheritableThreadLocal<HttpServletRequest> {
+
+        public HttpServletRequest getRequest() {
+            return super.get();
+        }
+
+        public HttpServletRequest initialValue() {
+        	return null;
+        }
+
+        public void setRequest(HttpServletRequest newRequest) {
+            super.set(newRequest);
+        }
+    }
+
+	/**
+     * Defines the ThreadLocalResponse to store the current response for this thread.
+     */
+    private class ThreadLocalResponse extends InheritableThreadLocal<HttpServletResponse> {
+
+        public HttpServletResponse getResponse() {
+            return super.get();
+        }
+
+        public HttpServletResponse initialValue() {
+        	return null;
+        }
+
+        public void setResponse(HttpServletResponse newResponse) {
+            super.set(newResponse);
+        }
+    }
+
+    /** The logger. */
+	private final Logger logger = ESAPI.getLogger("HTTPUtilities");
+
+    /** The max bytes. */
+	static final int maxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize();
+
+
+    /*
+     * The currentRequest ThreadLocal variable is used to make the currentRequest available to any call in any part of an
+     * application. This enables API's for actions that require the request to be much simpler. For example, the logout()
+     * method in the Authenticator class requires the currentRequest to get the session in order to invalidate it.
+     */
+    private ThreadLocalRequest currentRequest = new ThreadLocalRequest();
+
+	/*
+     * The currentResponse ThreadLocal variable is used to make the currentResponse available to any call in any part of an
+     * application. This enables API's for actions that require the response to be much simpler. For example, the logout()
+     * method in the Authenticator class requires the currentResponse to kill the Session ID cookie.
+     */
+    private ThreadLocalResponse currentResponse = new ThreadLocalResponse();
+
+
+
+	/**
+     * No arg constructor.
+     */
+    public DefaultHTTPUtilities() {
+	}
+
+
+	/**
+	 * {@inheritDoc}
+     * This implementation uses a custom "set-cookie" header rather than Java's
+     * cookie interface which doesn't allow the use of HttpOnly. Configure the
+     * HttpOnly and Secure settings in ESAPI.properties.
+	 */
+	public void addCookie( Cookie cookie ) {
+		addCookie( getCurrentResponse(), cookie );
+    }
+
+    /**
+	 * {@inheritDoc}
+     * This implementation uses a custom "set-cookie" header rather than Java's
+     * cookie interface which doesn't allow the use of HttpOnly. Configure the
+     * HttpOnly and Secure settings in ESAPI.properties.
+	 */
+    public void addCookie(HttpServletResponse response, Cookie cookie) {
+        String name = cookie.getName();
+        String value = cookie.getValue();
+        int maxAge = cookie.getMaxAge();
+        String domain = cookie.getDomain();
+        String path = cookie.getPath();
+        boolean secure = cookie.getSecure();
+
+        // validate the name and value
+        ValidationErrorList errors = new ValidationErrorList();
+        String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors);
+        String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", 5000, false, errors);
+
+        // if there are no errors, then set the cookie either with a header or normally
+        if (errors.size() == 0) {
+        	if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) {
+	            String header = createCookieHeader(cookieName, cookieValue, maxAge, domain, path, secure);
+	            addHeader(response, "Set-Cookie", header);
+        	} else {
+                // Issue 23 - If the ESAPI Configuration is set to force secure cookies, force the secure flag on the cookie before setting it
+                cookie.setSecure( secure || ESAPI.securityConfiguration().getForceSecureCookies() );
+        		response.addCookie(cookie);
+        	}
+            return;
+        }
+        logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (skip mode). Skipping cookie and continuing.");
+    }
+
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String addCSRFToken(String href) {
+		User user = ESAPI.authenticator().getCurrentUser();
+		if (user.isAnonymous()) {
+			return href;
+		}
+
+		// if there are already parameters append with &, otherwise append with ?
+		String token = CSRF_TOKEN_NAME + "=" + user.getCSRFToken();
+		return href.indexOf( '?') != -1 ? href + "&" + token : href + "?" + token;
+	}
+
+    /**
+	 * {@inheritDoc}
+     */
+    public void addHeader(String name, String value) {
+    	addHeader( getCurrentResponse(), name, value );
+    }
+
+    /**
+	 * {@inheritDoc}
+     */
+    public void addHeader(HttpServletResponse response, String name, String value) {
+        try {
+            String strippedName = StringUtilities.replaceLinearWhiteSpace(name);
+            String strippedValue = StringUtilities.replaceLinearWhiteSpace(value);
+            String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", 500, false);
+            response.addHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e);
+        }
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertSecureChannel() throws AccessControlException {
+    	assertSecureChannel( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * This implementation ignores the built-in isSecure() method
+	 * and uses the URL to determine if the request was transmitted over SSL.
+	 * This is because SSL may have been terminated somewhere outside the
+	 * container.
+	 */
+	public void assertSecureChannel(HttpServletRequest request) throws AccessControlException {
+	    if ( request == null ) {
+	    	throw new AccessControlException( "Insecure request received", "HTTP request was null" );
+	    }
+	    StringBuffer sb = request.getRequestURL();
+	    if ( sb == null ) {
+	    	throw new AccessControlException( "Insecure request received", "HTTP request URL was null" );
+	    }
+	    String url = sb.toString();
+	    if ( !url.startsWith( "https" ) ) {
+	    	throw new AccessControlException( "Insecure request received", "HTTP request did not use SSL" );
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertSecureRequest() throws AccessControlException {
+    	assertSecureRequest( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     */
+	public void assertSecureRequest(HttpServletRequest request) throws AccessControlException {
+		assertSecureChannel( request );
+		String receivedMethod = request.getMethod();
+		String requiredMethod = "POST";
+		if ( !receivedMethod.equals( requiredMethod ) ) {
+			throw new AccessControlException( "Insecure request received", "Received request using " + receivedMethod + " when only " + requiredMethod + " is allowed" );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public HttpSession changeSessionIdentifier() throws AuthenticationException {
+    	return changeSessionIdentifier( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     */
+	public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException {
+
+		// get the current session
+		HttpSession oldSession = request.getSession();
+
+		// make a copy of the session content
+		Map<String,Object> temp = new ConcurrentHashMap<String,Object>();
+		Enumeration e = oldSession.getAttributeNames();
+		while (e != null && e.hasMoreElements()) {
+			String name = (String) e.nextElement();
+			Object value = oldSession.getAttribute(name);
+			temp.put(name, value);
+		}
+
+		// kill the old session and create a new one
+		oldSession.invalidate();
+		HttpSession newSession = request.getSession();
+		User user = ESAPI.authenticator().getCurrentUser();
+		user.addSession( newSession );
+		user.removeSession( oldSession );
+
+		// copy back the session content
+      for (Map.Entry<String, Object> stringObjectEntry : temp.entrySet())
+      {
+         newSession.setAttribute(stringObjectEntry.getKey(), stringObjectEntry.getValue());
+		}
+		return newSession;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void clearCurrent() {
+		currentRequest.set(null);
+		currentResponse.set(null);
+	}
+
+	private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure) {
+        // create the special cookie header instead of creating a Java cookie
+        // Set-Cookie:<name>=<value>[; <name>=<value>][; expires=<date>][;
+        // domain=<domain_name>][; path=<some_path>][; secure][;HttpOnly]
+        String header = name + "=" + value;
+        header += "; Max-Age=" + maxAge;
+        if (domain != null) {
+            header += "; Domain=" + domain;
+        }
+        if (path != null) {
+            header += "; Path=" + path;
+        }
+        if ( secure || ESAPI.securityConfiguration().getForceSecureCookies() ) {
+            header += "; Secure";
+        }
+        if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) {
+            header += "; HttpOnly";
+        }
+        return header;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String decryptHiddenField(String encrypted) {
+    	try {
+    		return decryptString(encrypted);
+    	} catch( EncryptionException e ) {
+    		throw new IntrusionException("Invalid request","Tampering detected. Hidden field data did not decrypt properly.", e);
+    	}
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Map<String,String> decryptQueryString(String encrypted) throws EncryptionException {
+        String plaintext = decryptString(encrypted);
+		return queryToMap(plaintext);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Map<String,String> decryptStateFromCookie() throws EncryptionException {
+		return decryptStateFromCookie( getCurrentRequest() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param request
+     */
+    public Map<String,String> decryptStateFromCookie(HttpServletRequest request) throws EncryptionException {
+    	try {
+    		String encrypted = getCookie( request, ESAPI_STATE );
+    		if ( encrypted == null ) return new HashMap<String,String>();
+    		String plaintext = decryptString(encrypted);
+    		return queryToMap( plaintext );
+    	} catch( ValidationException e ) {
+        	return null;
+    	}
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encryptHiddenField(String value) throws EncryptionException {
+    	return encryptString(value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String encryptQueryString(String query) throws EncryptionException {
+	    return encryptString(query);
+	}
+
+	/**
+	 * {@inheritDoc}
+     */
+    public void encryptStateInCookie(HttpServletResponse response, Map<String,String> cleartext) throws EncryptionException {
+    	StringBuilder sb = new StringBuilder();
+    	Iterator i = cleartext.entrySet().iterator();
+    	while ( i.hasNext() ) {
+    		try {
+	    		Map.Entry entry = (Map.Entry)i.next();
+	    		
+	    		    // What do these need to be URL encoded? They are encrypted!
+	    		String name = ESAPI.encoder().encodeForURL( entry.getKey().toString() );
+	    		String value = ESAPI.encoder().encodeForURL( entry.getValue().toString() );
+             sb.append(name).append("=").append(value);
+	    		if ( i.hasNext() ) sb.append( "&" );
+    		} catch( EncodingException e ) {
+    			logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry", e );
+    		}
+    	}
+
+		String encrypted = encryptString(sb.toString());
+
+		if ( encrypted.length() > (MAX_COOKIE_LEN ) ) {
+			logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry");
+			throw new EncryptionException("Encryption failure", "Encrypted cookie state of " + encrypted.length() + " longer than allowed " + MAX_COOKIE_LEN );
+		}
+
+    	Cookie cookie = new Cookie( ESAPI_STATE, encrypted );
+    	addCookie( response, cookie );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void encryptStateInCookie( Map<String,String> cleartext ) throws EncryptionException {
+		encryptStateInCookie( getCurrentResponse(), cleartext );
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getCookie( HttpServletRequest request, String name ) throws ValidationException {
+        Cookie c = getFirstCookie( request, name );
+        if ( c == null ) return null;
+		String value = c.getValue();
+		return ESAPI.validator().getValidInput("HTTP cookie value: " + value, value, "HTTPCookieValue", 1000, false);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public String getCookie( String name ) throws ValidationException {
+    	return getCookie( getCurrentRequest(), name );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getCSRFToken() {
+		User user = ESAPI.authenticator().getCurrentUser();
+		if (user == null) return null;
+		return user.getCSRFToken();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public HttpServletRequest getCurrentRequest() {
+    	return currentRequest.getRequest();
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public HttpServletResponse getCurrentResponse() {
+        return currentResponse.getResponse();
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public List<File> getFileUploads() throws ValidationException {
+    	return getFileUploads( getCurrentRequest(), ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions() );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public List<File> getFileUploads(HttpServletRequest request) throws ValidationException {
+    	return getFileUploads(request, ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions());
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public List<File> getFileUploads(HttpServletRequest request, File finalDir ) throws ValidationException {
+    	return getFileUploads(request, finalDir, ESAPI.securityConfiguration().getAllowedFileExtensions());
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public List<File> getFileUploads(HttpServletRequest request, File finalDir, List allowedExtensions) throws ValidationException {
+        File tempDir = ESAPI.securityConfiguration().getUploadTempDirectory();
+		if ( !tempDir.exists() ) {
+		    if ( !tempDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create temp directory: " + tempDir.getAbsolutePath() );
+		}
+
+		if( finalDir != null){
+			if ( !finalDir.exists() ) {
+				if ( !finalDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + finalDir.getAbsolutePath() );
+			}
+		}
+		else {
+			if ( !ESAPI.securityConfiguration().getUploadDirectory().exists()) {
+				if ( !ESAPI.securityConfiguration().getUploadDirectory().mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + ESAPI.securityConfiguration().getUploadDirectory().getAbsolutePath() );
+			}
+			finalDir = ESAPI.securityConfiguration().getUploadDirectory();
+		}
+
+		List<File> newFiles = new ArrayList<File>();
+		try {
+			final HttpSession session = request.getSession(false);
+			if (!ServletFileUpload.isMultipartContent(request)) {
+				throw new ValidationUploadException("Upload failed", "Not a multipart request");
+			}
+
+			// this factory will store ALL files in the temp directory,
+			// regardless of size
+			DiskFileItemFactory factory = new DiskFileItemFactory(0, tempDir);
+			ServletFileUpload upload = new ServletFileUpload(factory);
+			upload.setSizeMax(maxBytes);
+
+			// Create a progress listener
+			ProgressListener progressListener = new ProgressListener() {
+				private long megaBytes = -1;
+				private long progress = 0;
+
+				public void update(long pBytesRead, long pContentLength, int pItems) {
+					if (pItems == 0)
+						return;
+					long mBytes = pBytesRead / 1000000;
+					if (megaBytes == mBytes)
+						return;
+					megaBytes = mBytes;
+					progress = (long) (((double) pBytesRead / (double) pContentLength) * 100);
+					if ( session != null ) {
+					    session.setAttribute("progress", Long.toString(progress));
+					}
+					// logger.logSuccess(Logger.SECURITY, "   Item " + pItems + " (" + progress + "% of " + pContentLength + " bytes]");
+				}
+			};
+			upload.setProgressListener(progressListener);
+
+			List<FileItem> items = upload.parseRequest(request);
+         for (FileItem item : items)
+         {
+            if (!item.isFormField() && item.getName() != null && !(item.getName().equals("")))
+            {
+					String[] fparts = item.getName().split("[\\/\\\\]");
+					String filename = fparts[fparts.length - 1];
+
+               if (!ESAPI.validator().isValidFileName("upload", filename, allowedExtensions, false))
+               {
+						throw new ValidationUploadException("Upload only simple filenames with the following extensions " + allowedExtensions, "Upload failed isValidFileName check");
+					}
+
+					logger.info(Logger.SECURITY_SUCCESS, "File upload requested: " + filename);
+					File f = new File(finalDir, filename);
+               if (f.exists())
+               {
+						String[] parts = filename.split("\\/.");
+						String extension = "";
+                  if (parts.length > 1)
+                  {
+							extension = parts[parts.length - 1];
+						}
+						String filenm = filename.substring(0, filename.length() - extension.length());
+						f = File.createTempFile(filenm, "." + extension, finalDir);
+					}
+					item.write(f);
+               newFiles.add(f);
+					// delete temporary file
+					item.delete();
+					logger.fatal(Logger.SECURITY_SUCCESS, "File successfully uploaded: " + f);
+               if (session != null)
+               {
+					    session.setAttribute("progress", Long.toString(0));
+					}
+				}
+			}
+		} catch (Exception e) {
+			if (e instanceof ValidationUploadException) {
+				throw (ValidationException)e;
+			}
+			throw new ValidationUploadException("Upload failure", "Problem during upload:" + e.getMessage(), e);
+		}
+		return Collections.synchronizedList(newFiles);
+	}
+
+
+
+	/**
+     * Utility to return the first cookie matching the provided name.
+     * @param request
+     * @param name
+     */
+	private Cookie getFirstCookie(HttpServletRequest request, String name) {
+		Cookie[] cookies = request.getCookies();
+		if (cookies != null) {
+         for (Cookie cookie : cookies)
+         {
+            if (cookie.getName().equals(name))
+            {
+					return cookie;
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getHeader( HttpServletRequest request, String name ) throws ValidationException {
+        String value = request.getHeader(name);
+        return ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, false);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public String getHeader( String name ) throws ValidationException {
+    	return getHeader( getCurrentRequest(), name );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public String getParameter( HttpServletRequest request, String name ) throws ValidationException {
+	    String value = request.getParameter(name);
+	    return ESAPI.validator().getValidInput("HTTP parameter value: " + value, value, "HTTPParameterValue", 2000, true);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public String getParameter( String name ) throws ValidationException {
+    	return getParameter( getCurrentRequest(), name );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void killAllCookies() {
+    	killAllCookies( getCurrentRequest(), getCurrentResponse() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param request
+     * @param response
+     */
+	public void killAllCookies(HttpServletRequest request, HttpServletResponse response) {
+		Cookie[] cookies = request.getCookies();
+		if (cookies != null) {
+         for (Cookie cookie : cookies)
+         {
+				killCookie(request, response, cookie.getName());
+			}
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param request
+     * @param response
+     * @param name
+     */
+	public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) {
+		String path = "//";
+		String domain="";
+		Cookie cookie = getFirstCookie(request, name);
+		if ( cookie != null ) {
+			path = cookie.getPath();
+			domain = cookie.getDomain();
+		}
+		Cookie deleter = new Cookie( name, "deleted" );
+		deleter.setMaxAge( 0 );
+		if ( domain != null ) deleter.setDomain( domain );
+		if ( path != null ) deleter.setPath( path );
+		response.addCookie( deleter );
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void killCookie( String name ) {
+    	killCookie( getCurrentRequest(), getCurrentResponse(), name );
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void logHTTPRequest() {
+    	logHTTPRequest( getCurrentRequest(), logger, null );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void logHTTPRequest(HttpServletRequest request, Logger logger) {
+    	logHTTPRequest( request, logger, null );
+    }
+
+		/**
+		 * Formats an HTTP request into a log suitable string. This implementation logs the remote host IP address (or
+		 * hostname if available), the request method (GET/POST), the URL, and all the querystring and form parameters. All
+		 * the parameters are presented as though they were in the URL even if they were in a form. Any parameters that
+		 * match items in the parameterNamesToObfuscate are shown as eight asterisks.
+		 *
+		 *
+		 * @param request
+		 */
+		public void logHTTPRequest(HttpServletRequest request, Logger logger, List parameterNamesToObfuscate) {
+			StringBuilder params = new StringBuilder();
+		    Iterator i = request.getParameterMap().keySet().iterator();
+		    while (i.hasNext()) {
+		        String key = (String) i.next();
+		        String[] value = (String[]) request.getParameterMap().get(key);
+		        for (int j = 0; j < value.length; j++) {
+                 params.append(key).append("=");
+		            if (parameterNamesToObfuscate != null && parameterNamesToObfuscate.contains(key)) {
+		                params.append("********");
+		            } else {
+		                params.append(value[j]);
+		            }
+		            if (j < value.length - 1) {
+		                params.append("&");
+		            }
+		        }
+		        if (i.hasNext())
+		            params.append("&");
+		    }
+		    Cookie[] cookies = request.getCookies();
+		    if ( cookies != null ) {
+             for (Cookie cooky : cookies)
+             {
+                if (!cooky.getName().equals(ESAPI.securityConfiguration().getHttpSessionIdName())) 
+                {
+                   params.append("+").append(cooky.getName()).append("=").append(cooky.getValue());
+		                    }
+		            }
+		    }
+		    String msg = request.getMethod() + " " + request.getRequestURL() + (params.length() > 0 ? "?" + params : "");
+		    logger.info(Logger.SECURITY_SUCCESS, msg);
+		}
+
+	private Map<String,String> queryToMap(String query) {
+		TreeMap<String,String> map = new TreeMap<String,String>();
+		String[] parts = query.split("&");
+      for (String part : parts)
+      {
+         try
+         {
+            String[] nvpair = part.split("=");
+				String name = ESAPI.encoder().decodeFromURL(nvpair[0]);
+				String value = ESAPI.encoder().decodeFromURL(nvpair[1]);
+            map.put(name, value);
+         }
+         catch (EncodingException e)
+         {
+				// skip the nvpair with the encoding problem - note this is already logged.
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * This implementation simply checks to make sure that the forward location starts with "WEB-INF" and
+	 * is intended for use in frameworks that forward to JSP files inside the WEB-INF folder.
+	 */
+	public void sendForward(HttpServletRequest request, HttpServletResponse response, String location) throws AccessControlException,ServletException,IOException {
+		if (!location.startsWith("WEB-INF")) {
+			throw new AccessControlException("Forward failed", "Bad forward location: " + location);
+		}
+		RequestDispatcher dispatcher = request.getRequestDispatcher(location);
+		dispatcher.forward( request, response );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void sendForward( String location )  throws AccessControlException,ServletException,IOException {
+    	sendForward( getCurrentRequest(), getCurrentResponse(), location);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * This implementation checks against the list of safe redirect locations defined in ESAPI.properties.
+     *
+     * @param response
+     */
+    public void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException {
+        if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location);
+            throw new IOException("Redirect failed");
+        }
+        response.sendRedirect(location);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void sendRedirect( String location )  throws AccessControlException,IOException {
+    	sendRedirect( getCurrentResponse(), location);
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void setContentType() {
+    	setContentType( getCurrentResponse() );
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setContentType(HttpServletResponse response) {
+		response.setContentType((ESAPI.securityConfiguration()).getResponseContentType());
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setCurrentHTTP(HttpServletRequest request, HttpServletResponse response) {
+     	currentRequest.setRequest(request);
+        currentResponse.setResponse(response);
+    }
+
+    /**
+	 * {@inheritDoc}
+     */
+    public void setHeader(HttpServletResponse response, String name, String value) {
+        try {
+            String strippedName = StringUtilities.replaceLinearWhiteSpace(name);
+            String strippedValue = StringUtilities.replaceLinearWhiteSpace(value);
+            String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false);
+            String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", 500, false);
+            response.setHeader(safeName, safeValue);
+        } catch (ValidationException e) {
+            logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e);
+        }
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void setHeader( String name, String value ) {
+    	setHeader( getCurrentResponse(), name, value );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setNoCacheHeaders() {
+    	setNoCacheHeaders( getCurrentResponse() );
+    }
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @param response
+     */
+	public void setNoCacheHeaders(HttpServletResponse response) {
+		// HTTP 1.1
+		response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
+
+		// HTTP 1.0
+		response.setHeader("Pragma","no-cache");
+		response.setDateHeader("Expires", -1);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * Save the user's remember me data in an encrypted cookie and send it to the user.
+	 * Any old remember me cookie is destroyed first. Setting this cookie will keep the user
+	 * logged in until the maxAge passes, the password is changed, or the cookie is deleted.
+	 * If the cookie exists for the current user, it will automatically be used by ESAPI to
+	 * log the user in, if the data is valid and not expired.
+     *
+     * @param request
+     * @param response
+     */
+	public String setRememberToken( HttpServletRequest request, HttpServletResponse response, String password, int maxAge, String domain, String path ) {
+		User user = ESAPI.authenticator().getCurrentUser();
+		try {
+			killCookie(request, response, REMEMBER_TOKEN_COOKIE_NAME );
+			// seal already contains random data
+			String clearToken = user.getAccountName() + "|" + password;
+			long expiry = ESAPI.encryptor().getRelativeTimeStamp(maxAge * 1000);
+			String cryptToken = ESAPI.encryptor().seal(clearToken, expiry);
+
+            // Do NOT URLEncode cryptToken before creating cookie. See Google Issue # 144,
+			// which was marked as "WontFix".
+
+			Cookie cookie = new Cookie( REMEMBER_TOKEN_COOKIE_NAME, cryptToken );
+			cookie.setMaxAge( maxAge );
+			cookie.setDomain( domain );
+			cookie.setPath( path );
+			response.addCookie( cookie );
+			logger.info(Logger.SECURITY_SUCCESS, "Enabled remember me token for " + user.getAccountName() );
+			return cryptToken;
+		} catch( IntegrityException e ) {
+			logger.warning(Logger.SECURITY_FAILURE, "Attempt to set remember me token failed for " + user.getAccountName(), e );
+			return null;
+		}
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String setRememberToken( String password, int maxAge, String domain, String path ) {
+    	return setRememberToken( getCurrentRequest(), getCurrentResponse(), password, maxAge, domain, path );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public void verifyCSRFToken() throws IntrusionException {
+    	verifyCSRFToken( getCurrentRequest() );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 *
+	 * This implementation uses the CSRF_TOKEN_NAME parameter for the token.
+     *
+     * @param request
+     */
+	public void verifyCSRFToken(HttpServletRequest request) throws IntrusionException {
+		User user = ESAPI.authenticator().getCurrentUser();
+
+		// check if user authenticated with this request - no CSRF protection required
+		if( request.getAttribute(user.getCSRFToken()) != null ) {
+			return;
+		}
+		String token = request.getParameter(CSRF_TOKEN_NAME);
+		if ( !user.getCSRFToken().equals( token ) ) {
+			throw new IntrusionException("Authentication failed", "Possibly forged HTTP request without proper CSRF token detected");
+		}
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getSessionAttribute( String key ) {
+        final HttpSession session = ESAPI.currentRequest().getSession(false);
+        if ( session != null )
+            return (T) session.getAttribute(key);
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getSessionAttribute(HttpSession session, String key)
+    {
+        return (T) session.getAttribute(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getRequestAttribute(String key)
+    {
+        return (T)  ESAPI.currentRequest().getAttribute(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T getRequestAttribute(HttpServletRequest request, String key)
+    {
+        return (T) request.getAttribute( key );
+    }
+    
+    /////////////////////
+    
+    /* Helper method to encrypt using new Encryptor encryption methods and
+     * return the serialized ciphertext as a hex-encoded string.
+     */
+    private String encryptString(String plaintext) throws EncryptionException {
+        PlainText pt = new PlainText(plaintext);
+        CipherText ct = ESAPI.encryptor().encrypt(pt);
+        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+        return Hex.encode(serializedCiphertext, false);
+    }
+    
+    /* Helper method to decrypt a hex-encode serialized ciphertext string and
+     * to decrypt it using the new Encryptor decryption methods.
+     */
+    private String decryptString(String ciphertext) throws EncryptionException {
+        byte[] serializedCiphertext = Hex.decode(ciphertext);
+        CipherText restoredCipherText =
+            CipherText.fromPortableSerializedBytes(serializedCiphertext);
+        PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
+        return plaintext.toString();
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultIntrusionDetector.java b/src/main/java/org/owasp/esapi/reference/DefaultIntrusionDetector.java
new file mode 100644
index 0000000..2a71e14
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultIntrusionDetector.java
@@ -0,0 +1,193 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.SecurityConfiguration.Threshold;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.IntrusionException;
+
+/**
+ * Reference implementation of the IntrusionDetector interface. This
+ * implementation monitors EnterpriseSecurityExceptions to see if any user
+ * exceeds a configurable threshold in a configurable time period. For example,
+ * it can monitor to see if a user exceeds 10 input validation issues in a 1
+ * minute period. Or if there are more than 3 authentication problems in a 10
+ * second period. More complex implementations are certainly possible, such as
+ * one that establishes a baseline of expected behavior, and then detects
+ * deviations from that baseline. This implementation stores state in the
+ * user's session, so that it will be properly cleaned up when the session is
+ * terminated. State is not otherwise persisted, so attacks that span sessions
+ * will not be detectable.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.IntrusionDetector
+ */
+public class DefaultIntrusionDetector implements org.owasp.esapi.IntrusionDetector {
+
+	/** The logger. */
+	private final Logger logger = ESAPI.getLogger("IntrusionDetector");
+
+    public DefaultIntrusionDetector() {
+	}
+	
+	/**
+	 * {@inheritDoc}
+     *
+     * @param e
+     */
+	public void addException(Exception e) {
+		if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+		
+        if ( e instanceof EnterpriseSecurityException ) {
+            logger.warning( Logger.SECURITY_FAILURE, ((EnterpriseSecurityException)e).getLogMessage(), e );
+        } else {
+            logger.warning( Logger.SECURITY_FAILURE, e.getMessage(), e );
+        }
+
+        // add the exception to the current user, which may trigger a detector 
+		User user = ESAPI.authenticator().getCurrentUser();
+        String eventName = e.getClass().getName();
+
+        if ( e instanceof IntrusionException) {
+            return;
+        }
+        
+        // add the exception to the user's store, handle IntrusionException if thrown
+		try {
+			addSecurityEvent(user, eventName);
+		} catch( IntrusionException ex ) {
+            Threshold quota = ESAPI.securityConfiguration().getQuota(eventName);
+            Iterator i = quota.actions.iterator();
+            while ( i.hasNext() ) {
+                String action = (String)i.next();
+                String message = "User exceeded quota of " + quota.count + " per "+ quota.interval +" seconds for event " + eventName + ". Taking actions " + quota.actions;
+                takeSecurityAction( action, message );
+            }
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void addEvent(String eventName, String logMessage) throws IntrusionException {
+    	if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+    	
+        logger.warning( Logger.SECURITY_FAILURE, "Security event " + eventName + " received : " + logMessage );
+
+        // add the event to the current user, which may trigger a detector 
+        User user = ESAPI.authenticator().getCurrentUser();
+        try {
+            addSecurityEvent(user, "event." + eventName);
+        } catch( IntrusionException ex ) {
+            Threshold quota = ESAPI.securityConfiguration().getQuota("event." + eventName);
+            Iterator i = quota.actions.iterator();
+            while ( i.hasNext() ) {
+                String action = (String)i.next();
+                String message = "User exceeded quota of " + quota.count + " per "+ quota.interval +" seconds for event " + eventName + ". Taking actions " + quota.actions;
+                takeSecurityAction( action, message );
+            }
+        }
+    }
+
+    /**
+     * Take a specified security action.  In this implementation, acceptable
+     * actions are: log, disable, logout.
+     * 
+     * @param action
+     * 		the action to take (log, disable, logout)
+     * @param message
+     * 		the message to log if the action is "log"
+     */
+    private void takeSecurityAction( String action, String message ) {
+    	if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+    	
+        if ( action.equals( "log" ) ) {
+            logger.fatal( Logger.SECURITY_FAILURE, "INTRUSION - " + message );
+        }
+        User user = ESAPI.authenticator().getCurrentUser();
+        if (user == User.ANONYMOUS)
+        	return;
+        if ( action.equals( "disable" ) ) {
+            user.disable();
+        }
+        if ( action.equals( "logout" ) ) {
+            user.logout();
+        }
+    }
+
+	 /**
+	 * Adds a security event to the user.  These events are used to check that the user has not
+	 * reached the security thresholds set in the properties file.
+	 * 
+	 * @param user
+	 * 			The user that caused the event.
+	 * @param eventName
+	 * 			The name of the event that occurred.
+	 */
+	private void addSecurityEvent(User user, String eventName) {
+		if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+		
+		if ( user.isAnonymous() ) return;
+		
+		HashMap eventMap = user.getEventMap();
+		
+		// if there is a threshold, then track this event
+		Threshold threshold = ESAPI.securityConfiguration().getQuota( eventName );
+		if ( threshold != null ) {
+			Event event = (Event)eventMap.get( eventName );
+			if ( event == null ) {
+				event = new Event( eventName );
+				eventMap.put( eventName, event );
+			}
+			// increment
+			event.increment(threshold.count, threshold.interval);
+		}
+	}
+
+    private static class Event {
+        public String key;
+        public Stack times = new Stack();
+        //public long count = 0;
+        public Event( String key ) {
+            this.key = key;
+        }
+        public void increment(int count, long interval) throws IntrusionException {
+        	if (ESAPI.securityConfiguration().getDisableIntrusionDetection()) return;
+        	
+            Date now = new Date();
+            times.add( 0, now );
+            while ( times.size() > count ) times.remove( times.size()-1 );
+            if ( times.size() == count ) {
+                Date past = (Date)times.get( count-1 );
+                long plong = past.getTime();
+                long nlong = now.getTime(); 
+                if ( nlong - plong < interval * 1000 ) {
+                    throw new IntrusionException( "Threshold exceeded", "Exceeded threshold for " + key );
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultRandomizer.java b/src/main/java/org/owasp/esapi/reference/DefaultRandomizer.java
new file mode 100644
index 0000000..3272a69
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultRandomizer.java
@@ -0,0 +1,134 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.UUID;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.Randomizer;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Reference implementation of the Randomizer interface. This implementation builds on the JCE provider to provide a
+ * cryptographically strong source of entropy. The specific algorithm used is configurable in ESAPI.properties.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Randomizer
+ */
+public class DefaultRandomizer implements org.owasp.esapi.Randomizer {
+    private static volatile Randomizer singletonInstance;
+
+    public static Randomizer getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( DefaultRandomizer.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new DefaultRandomizer();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    /** The sr. */
+    private SecureRandom secureRandom = null;
+
+    /** The logger. */
+    private final Logger logger = ESAPI.getLogger("Randomizer");
+
+    private DefaultRandomizer() {
+        String algorithm = ESAPI.securityConfiguration().getRandomAlgorithm();
+        try {
+            secureRandom = SecureRandom.getInstance(algorithm);
+        } catch (NoSuchAlgorithmException e) {
+            // Can't throw an exception from the constructor, but this will get
+            // it logged and tracked
+            new EncryptionException("Error creating randomizer", "Can't find random algorithm " + algorithm, e);
+        }
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomString(int length, char[] characterSet) {
+    	StringBuilder sb = new StringBuilder();
+        for (int loop = 0; loop < length; loop++) {
+            int index = secureRandom.nextInt(characterSet.length);
+            sb.append(characterSet[index]);
+        }
+        String nonce = sb.toString();
+        return nonce;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getRandomBoolean() {
+        return secureRandom.nextBoolean();
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getRandomInteger(int min, int max) {
+        return secureRandom.nextInt(max - min) + min;
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public long getRandomLong() {
+        return secureRandom.nextLong();    
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public float getRandomReal(float min, float max) {
+        float factor = max - min;
+        return secureRandom.nextFloat() * factor + min;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomFilename(String extension) {
+        String fn = getRandomString(12, EncoderConstants.CHAR_ALPHANUMERICS) + "." + extension;
+        logger.debug(Logger.SECURITY_SUCCESS, "Generated new random filename: " + fn );
+        return fn;
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomGUID() throws EncryptionException {
+    	return UUID.randomUUID().toString();
+    }
+    	
+    /**
+     * {@inheritDoc}
+     */
+    public byte[] getRandomBytes(int n) {
+    	byte[] result = new byte[ n ];
+    	secureRandom.nextBytes(result);
+    	return result;
+    }
+    	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java
new file mode 100644
index 0000000..c2e3821
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java
@@ -0,0 +1,1224 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.SecurityConfiguration;
+import org.owasp.esapi.errors.ConfigurationException;
+
+/**
+ * The reference {@code SecurityConfiguration} manages all the settings used by the ESAPI in a single place. In this reference
+ * implementation, resources can be put in several locations, which are searched in the following order:
+ * <p>
+ * 1) Inside a directory set with a call to SecurityConfiguration.setResourceDirectory( "C:\temp\resources" ).
+ * <p>
+ * 2) Inside the System.getProperty( "org.owasp.esapi.resources" ) directory.
+ * You can set this on the java command line as follows (for example):
+ * <pre>
+ * 		java -Dorg.owasp.esapi.resources="C:\temp\resources"
+ * </pre>
+ * You may have to add this to the start-up script that starts your web server. For example, for Tomcat,
+ * in the "catalina" script that starts Tomcat, you can set the JAVA_OPTS variable to the {@code -D} string above.
+ * <p>
+ * 3) Inside the {@code System.getProperty( "user.home" ) + "/.esapi"} directory (supported for backward compatibility) or
+ * inside the {@code System.getProperty( "user.home" ) + "/esapi"} directory.
+ * <p>
+ * 4) The first ".esapi" or "esapi" directory on the classpath. (The former for backward compatibility.)
+ * <p>
+ * Once the Configuration is initialized with a resource directory, you can edit it to set things like master
+ * keys and passwords, logging locations, error thresholds, and allowed file extensions.
+ * <p>
+ * WARNING: Do not forget to update ESAPI.properties to change the master key and other security critical settings.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim .at. manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @author Kevin Wall (kevin.w.wall .at. gmail.com)
+ */
+
+public class DefaultSecurityConfiguration implements SecurityConfiguration {
+    private static volatile SecurityConfiguration instance = null;
+
+    public static SecurityConfiguration getInstance() {
+        if ( instance == null ) {
+            synchronized (DefaultSecurityConfiguration.class) {
+                if ( instance == null ) {
+                    instance = new DefaultSecurityConfiguration();
+                }
+            }
+        }
+        return instance;
+    }
+    
+    private Properties properties = null;
+    private String cipherXformFromESAPIProp = null;	// New in ESAPI 2.0
+    private String cipherXformCurrent = null;		// New in ESAPI 2.0
+
+	/** The name of the ESAPI property file */
+	public static final String RESOURCE_FILE = "ESAPI.properties";
+	
+    public static final String REMEMBER_TOKEN_DURATION = "Authenticator.RememberTokenDuration";
+    public static final String IDLE_TIMEOUT_DURATION = "Authenticator.IdleTimeoutDuration";
+    public static final String ABSOLUTE_TIMEOUT_DURATION = "Authenticator.AbsoluteTimeoutDuration";
+    public static final String ALLOWED_LOGIN_ATTEMPTS = "Authenticator.AllowedLoginAttempts";
+    public static final String USERNAME_PARAMETER_NAME = "Authenticator.UsernameParameterName";
+    public static final String PASSWORD_PARAMETER_NAME = "Authenticator.PasswordParameterName";
+    public static final String MAX_OLD_PASSWORD_HASHES = "Authenticator.MaxOldPasswordHashes";
+
+    public static final String ALLOW_MULTIPLE_ENCODING = "Encoder.AllowMultipleEncoding";
+    public static final String ALLOW_MIXED_ENCODING	= "Encoder.AllowMixedEncoding";
+    public static final String CANONICALIZATION_CODECS = "Encoder.DefaultCodecList";
+
+    public static final String DISABLE_INTRUSION_DETECTION  = "IntrusionDetector.Disable";
+    
+    public static final String MASTER_KEY = "Encryptor.MasterKey";
+    public static final String MASTER_SALT = "Encryptor.MasterSalt";
+    public static final String KEY_LENGTH = "Encryptor.EncryptionKeyLength";
+    public static final String ENCRYPTION_ALGORITHM = "Encryptor.EncryptionAlgorithm";
+    public static final String HASH_ALGORITHM = "Encryptor.HashAlgorithm";
+    public static final String HASH_ITERATIONS = "Encryptor.HashIterations";
+    public static final String CHARACTER_ENCODING = "Encryptor.CharacterEncoding";
+    public static final String RANDOM_ALGORITHM = "Encryptor.RandomAlgorithm";
+    public static final String DIGITAL_SIGNATURE_ALGORITHM = "Encryptor.DigitalSignatureAlgorithm";
+    public static final String DIGITAL_SIGNATURE_KEY_LENGTH = "Encryptor.DigitalSignatureKeyLength";
+    			// ==================================//
+    			//		New in ESAPI Java 2.0		 //
+    			// ================================= //
+    public static final String PREFERRED_JCE_PROVIDER = "Encryptor.PreferredJCEProvider";
+    public static final String CIPHER_TRANSFORMATION_IMPLEMENTATION = "Encryptor.CipherTransformation";
+    public static final String CIPHERTEXT_USE_MAC = "Encryptor.CipherText.useMAC";
+    public static final String PLAINTEXT_OVERWRITE = "Encryptor.PlainText.overwrite";
+    public static final String IV_TYPE = "Encryptor.ChooseIVMethod";
+    public static final String FIXED_IV = "Encryptor.fixedIV";
+    public static final String COMBINED_CIPHER_MODES = "Encryptor.cipher_modes.combined_modes";
+    public static final String ADDITIONAL_ALLOWED_CIPHER_MODES = "Encryptor.cipher_modes.additional_allowed";
+    public static final String KDF_PRF_ALG = "Encryptor.KDF.PRF";
+	public static final String PRINT_PROPERTIES_WHEN_LOADED = "ESAPI.printProperties";
+
+    public static final String WORKING_DIRECTORY = "Executor.WorkingDirectory";
+    public static final String APPROVED_EXECUTABLES = "Executor.ApprovedExecutables";
+
+    public static final String FORCE_HTTPONLYSESSION = "HttpUtilities.ForceHttpOnlySession";
+    public static final String FORCE_SECURESESSION = "HttpUtilities.SecureSession";
+    public static final String FORCE_HTTPONLYCOOKIES = "HttpUtilities.ForceHttpOnlyCookies";
+    public static final String FORCE_SECURECOOKIES = "HttpUtilities.ForceSecureCookies";
+	public static final String MAX_HTTP_HEADER_SIZE = "HttpUtilities.MaxHeaderSize";
+    public static final String UPLOAD_DIRECTORY = "HttpUtilities.UploadDir";
+    public static final String UPLOAD_TEMP_DIRECTORY = "HttpUtilities.UploadTempDir";
+    public static final String APPROVED_UPLOAD_EXTENSIONS = "HttpUtilities.ApprovedUploadExtensions";
+    public static final String MAX_UPLOAD_FILE_BYTES = "HttpUtilities.MaxUploadFileBytes";
+    public static final String RESPONSE_CONTENT_TYPE = "HttpUtilities.ResponseContentType";
+    public static final String HTTP_SESSION_ID_NAME = "HttpUtilities.HttpSessionIdName";
+
+    public static final String APPLICATION_NAME = "Logger.ApplicationName";
+    public static final String LOG_LEVEL = "Logger.LogLevel";
+    public static final String LOG_FILE_NAME = "Logger.LogFileName";
+    public static final String MAX_LOG_FILE_SIZE = "Logger.MaxLogFileSize";
+    public static final String LOG_ENCODING_REQUIRED = "Logger.LogEncodingRequired";
+    public static final String LOG_APPLICATION_NAME = "Logger.LogApplicationName";
+    public static final String LOG_SERVER_IP = "Logger.LogServerIP";
+    public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile";
+    public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates";
+
+
+
+    /**
+	 * The default max log file size is set to 10,000,000 bytes (10 Meg). If the current log file exceeds the current
+	 * max log file size, the logger will move the old log data into another log file. There currently is a max of
+	 * 1000 log files of the same name. If that is exceeded it will presumably start discarding the oldest logs.
+	 */
+	public static final int DEFAULT_MAX_LOG_FILE_SIZE = 10000000;
+	
+    protected final int MAX_REDIRECT_LOCATION = 1000;
+    
+    /**
+     * @deprecated	It is not clear whether this is intended to be the max file name length for the basename(1) of
+     *				a file or the max full path name length of a canonical full path name. Since it is not used anywhere
+     *				in the ESAPI code it is being deprecated and scheduled to be removed in release 2.1.
+     */
+    protected final int MAX_FILE_NAME_LENGTH = 1000;	// DISCUSS: Is this for given directory or refer to canonicalized full path name?
+    													// Too long if the former! (Usually 255 is limit there.) Hard to tell since not used
+    													// here in this class and it's protected, so not sure what it's intent is. It's not
+    													// used anywhere in the ESAPI code base. I am going to deprecate it because of this. -kww
+
+    /*
+     * Implementation Keys
+     */
+    public static final String LOG_IMPLEMENTATION = "ESAPI.Logger";
+    public static final String AUTHENTICATION_IMPLEMENTATION = "ESAPI.Authenticator";
+    public static final String ENCODER_IMPLEMENTATION = "ESAPI.Encoder";
+    public static final String ACCESS_CONTROL_IMPLEMENTATION = "ESAPI.AccessControl";
+    public static final String ENCRYPTION_IMPLEMENTATION = "ESAPI.Encryptor";
+    public static final String INTRUSION_DETECTION_IMPLEMENTATION = "ESAPI.IntrusionDetector";
+    public static final String RANDOMIZER_IMPLEMENTATION = "ESAPI.Randomizer";
+	public static final String EXECUTOR_IMPLEMENTATION = "ESAPI.Executor";
+	public static final String VALIDATOR_IMPLEMENTATION = "ESAPI.Validator";
+	public static final String HTTP_UTILITIES_IMPLEMENTATION = "ESAPI.HTTPUtilities";
+
+    /*
+     * Default Implementations
+     */
+    public static final String DEFAULT_LOG_IMPLEMENTATION = "org.owasp.esapi.reference.JavaLogFactory";
+    public static final String DEFAULT_AUTHENTICATION_IMPLEMENTATION = "org.owasp.esapi.reference.FileBasedAuthenticator";
+    public static final String DEFAULT_ENCODER_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultEncoder";
+    public static final String DEFAULT_ACCESS_CONTROL_IMPLEMENTATION = "org.owasp.esapi.reference.accesscontrol.DefaultAccessController";
+    public static final String DEFAULT_ENCRYPTION_IMPLEMENTATION = "org.owasp.esapi.reference.crypto.JavaEncryptor";
+    public static final String DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultIntrusionDetector";
+    public static final String DEFAULT_RANDOMIZER_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultRandomizer";
+    public static final String DEFAULT_EXECUTOR_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultExecutor";
+    public static final String DEFAULT_HTTP_UTILITIES_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultHTTPUtilities";
+    public static final String DEFAULT_VALIDATOR_IMPLEMENTATION = "org.owasp.esapi.reference.DefaultValidator";
+
+    private static final Map<String, Pattern> patternCache = new HashMap<String, Pattern>();
+
+    /*
+     * Absolute path to the user.home. No longer includes the ESAPI portion as it used to.
+     */
+    private static final String userHome = System.getProperty("user.home" );
+    /*
+     * Absolute path to the customDirectory
+     */	// DISCUSS: Implicit assumption here that there is no SecurityManager installed enforcing the
+        //			prevention of reading system properties. Otherwise this will fail with SecurityException.
+    private static String customDirectory = System.getProperty("org.owasp.esapi.resources");
+    /*
+     * Relative path to the resourceDirectory. Relative to the classpath.
+     * Specifically, ClassLoader.getResource(resourceDirectory + filename) will
+     * be used to load the file.
+     */
+    private String resourceDirectory = ".esapi";	// For backward compatibility (vs. "esapi")
+
+//    private static long lastModified = -1;
+
+    /**
+     * Instantiates a new configuration.
+     */
+    public DefaultSecurityConfiguration() {
+    	// load security configuration
+    	try {
+        	loadConfiguration();
+        	this.setCipherXProperties();
+        } catch( IOException e ) {
+	        logSpecial("Failed to load security configuration", e );
+	        throw new ConfigurationException("Failed to load security configuration", e);
+        }
+    }
+    
+    /**
+     * Instantiates a new configuration with the supplied properties.
+     * 
+     * Warning - if the setResourceDirectory() method is invoked the properties will
+     * be re-loaded, replacing the supplied properties.
+     * 
+     * @param properties
+     */
+    public DefaultSecurityConfiguration(Properties properties) {
+    	super();
+    	this.properties = properties; 
+    	this.setCipherXProperties();
+    }
+    
+    private void setCipherXProperties() {
+		// TODO: FUTURE: Replace by future CryptoControls class???
+		// See SecurityConfiguration.setCipherTransformation() for
+		// explanation of this.
+        // (Propose this in 2.1 via future email to ESAPI-DEV list.)
+		cipherXformFromESAPIProp =
+			getESAPIProperty(CIPHER_TRANSFORMATION_IMPLEMENTATION,
+							 "AES/CBC/PKCS5Padding");
+		cipherXformCurrent = cipherXformFromESAPIProp;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getApplicationName() {
+    	return getESAPIProperty(APPLICATION_NAME, "DefaultName");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getLogImplementation() {
+    	return getESAPIProperty(LOG_IMPLEMENTATION, DEFAULT_LOG_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getAuthenticationImplementation() {
+    	return getESAPIProperty(AUTHENTICATION_IMPLEMENTATION, DEFAULT_AUTHENTICATION_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getEncoderImplementation() {
+    	return getESAPIProperty(ENCODER_IMPLEMENTATION, DEFAULT_ENCODER_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getAccessControlImplementation() {
+    	return getESAPIProperty(ACCESS_CONTROL_IMPLEMENTATION, DEFAULT_ACCESS_CONTROL_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getEncryptionImplementation() {
+    	return getESAPIProperty(ENCRYPTION_IMPLEMENTATION, DEFAULT_ENCRYPTION_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getIntrusionDetectionImplementation() {
+    	return getESAPIProperty(INTRUSION_DETECTION_IMPLEMENTATION, DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomizerImplementation() {
+    	return getESAPIProperty(RANDOMIZER_IMPLEMENTATION, DEFAULT_RANDOMIZER_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getExecutorImplementation() {
+    	return getESAPIProperty(EXECUTOR_IMPLEMENTATION, DEFAULT_EXECUTOR_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getHTTPUtilitiesImplementation() {
+    	return getESAPIProperty(HTTP_UTILITIES_IMPLEMENTATION, DEFAULT_HTTP_UTILITIES_IMPLEMENTATION);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getValidationImplementation() {
+    	return getESAPIProperty(VALIDATOR_IMPLEMENTATION, DEFAULT_VALIDATOR_IMPLEMENTATION);
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public byte[] getMasterKey() {
+    	byte[] key = getESAPIPropertyEncoded( MASTER_KEY, null );
+    	if ( key == null || key.length == 0 ) {
+    		throw new ConfigurationException("Property '" + MASTER_KEY +
+    							"' missing or empty in ESAPI.properties file.");
+    }
+    	return key;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setResourceDirectory( String dir ) {
+    	resourceDirectory = dir;
+        logSpecial( "Reset resource directory to: " + dir, null );
+
+        // reload configuration if necessary
+    	try {
+    		this.loadConfiguration();
+    	} catch( IOException e ) {
+	        logSpecial("Failed to load security configuration from " + dir, e);
+    	}
+    }
+
+    public int getEncryptionKeyLength() {
+    	return getESAPIProperty(KEY_LENGTH, 128 );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public byte[] getMasterSalt() {
+    	byte[] salt = getESAPIPropertyEncoded( MASTER_SALT, null );
+    	if ( salt == null || salt.length == 0 ) {
+    		throw new ConfigurationException("Property '" + MASTER_SALT +
+    							"' missing or empty in ESAPI.properties file.");
+    }
+    	return salt;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public List<String> getAllowedExecutables() {
+    	String def = "";
+        String[] exList = getESAPIProperty(APPROVED_EXECUTABLES,def).split(",");
+        return Arrays.asList(exList);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public List<String> getAllowedFileExtensions() {
+    	String def = ".zip,.pdf,.tar,.gz,.xls,.properties,.txt,.xml";
+        String[] extList = getESAPIProperty(APPROVED_UPLOAD_EXTENSIONS,def).split(",");
+        return Arrays.asList(extList);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getAllowedFileUploadSize() {
+        return getESAPIProperty(MAX_UPLOAD_FILE_BYTES, 5000000);
+    }
+
+
+    private Properties loadPropertiesFromStream( InputStream is, String name ) throws IOException {
+    	Properties config = new Properties();
+        try {
+	        config.load(is);
+	        logSpecial("Loaded '" + name + "' properties file", null);
+        } finally {
+            if ( is != null ) try { is.close(); } catch( Exception e ) {}
+        }
+        return config;
+    }
+
+	/**
+	 * Load configuration. Never prints properties.
+	 * 
+	 * @throws java.io.IOException
+	 *             if the file is inaccessible
+	 */
+	protected void loadConfiguration() throws IOException {
+		
+		try {
+		    //first attempt file IO loading of properties
+			logSpecial("Attempting to load " + RESOURCE_FILE + " via file I/O.");
+			properties = loadPropertiesFromStream(getResourceStream(RESOURCE_FILE), RESOURCE_FILE);
+			
+		} catch (Exception iae) {
+		    //if file I/O loading fails, attempt classpath based loading next
+		    logSpecial("Loading " + RESOURCE_FILE + " via file I/O failed. Exception was: " + iae);
+			logSpecial("Attempting to load " + RESOURCE_FILE + " via the classpath.");
+			try {
+				properties = loadConfigurationFromClasspath(RESOURCE_FILE);
+			} catch (Exception e) {				
+				logSpecial(RESOURCE_FILE + " could not be loaded by any means. Fail.", e);
+				throw new ConfigurationException(RESOURCE_FILE + " could not be loaded by any means. Fail.", e);
+			}			
+		}
+		
+		// if properties loaded properly above, get validation properties and merge them into the main properties
+		if (properties != null) {
+			
+			String validationPropFileName = getESAPIProperty(VALIDATION_PROPERTIES, "validation.properties");
+			Properties validationProperties = null;
+
+			//clear any cached validation patterns so they can be reloaded from validation.properties
+			patternCache.clear();
+			
+			try {
+			    //first attempt file IO loading of properties
+				logSpecial("Attempting to load " + validationPropFileName + " via file I/O.");
+				validationProperties = loadPropertiesFromStream(getResourceStream(validationPropFileName), validationPropFileName);
+				
+			} catch (Exception iae) {
+			    //if file I/O loading fails, attempt classpath based loading next
+			    logSpecial("Loading " + validationPropFileName + " via file I/O failed.");
+				logSpecial("Attempting to load " + validationPropFileName + " via the classpath.");		
+				try {
+					validationProperties = loadConfigurationFromClasspath(validationPropFileName);
+				} catch (Exception e) {				
+					logSpecial(validationPropFileName + " could not be loaded by any means. fail.", e);
+				}			
+			}
+			
+			if (validationProperties != null) {
+		    	Iterator<?> i = validationProperties.keySet().iterator();
+		    	while( i.hasNext() ) {
+		    		String key = (String)i.next();
+		    		String value = validationProperties.getProperty(key);
+		    		properties.put( key, value);
+		    	}
+			}
+			
+	        if ( shouldPrintProperties() ) {
+	    	
+	    	//FIXME - make this chunk configurable
+	    	/*
+	        logSpecial("  ========Master Configuration========", null);
+	        //logSpecial( "  ResourceDirectory: " + DefaultSecurityConfiguration.resourceDirectory );
+	        Iterator j = new TreeSet( properties.keySet() ).iterator();
+	        while (j.hasNext()) {
+	            String key = (String)j.next();
+	            // print out properties, but not sensitive ones like MasterKey and MasterSalt
+	            if ( !key.contains( "Master" ) ) {
+	            		logSpecial("  |   " + key + "=" + properties.get(key), null);
+	        	}
+	        }
+	        */
+	        	
+	        }
+		}
+	}	
+	
+	/**
+	 * @param filename
+	 * @return An {@code InputStream} associated with the specified file name as
+	 *         a resource stream.
+	 * @throws IOException
+	 *             If the file cannot be found or opened for reading.
+	 */
+	public InputStream getResourceStream(String filename) throws IOException {
+		if (filename == null) {
+			return null;
+		}
+
+		try {
+			File f = getResourceFile(filename);
+			if (f != null && f.exists()) {
+				return new FileInputStream(f);
+			}
+		} catch (Exception e) {
+		}
+
+		throw new FileNotFoundException();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public File getResourceFile(String filename) {
+		logSpecial("Attempting to load " + filename + " as resource file via file I/O.");
+
+		if (filename == null) {
+			logSpecial("Failed to load properties via FileIO. Filename is null.");
+			return null; // not found.
+		}
+
+		File f = null;
+
+		// first, allow command line overrides. -Dorg.owasp.esapi.resources
+		// directory
+		f = new File(customDirectory, filename);
+		if (customDirectory != null && f.canRead()) {
+			logSpecial("Found in 'org.owasp.esapi.resources' directory: " + f.getAbsolutePath());
+			return f;
+		} else {
+			logSpecial("Not found in 'org.owasp.esapi.resources' directory or file not readable: " + f.getAbsolutePath());
+		}
+
+		// if not found, then try the programmatically set resource directory
+		// (this defaults to SystemResource directory/RESOURCE_FILE
+		URL fileUrl = ClassLoader.getSystemResource(resourceDirectory + "/" + filename);
+        if ( fileUrl == null ) {
+            fileUrl = ClassLoader.getSystemResource("esapi/" + filename);
+        }
+
+		if (fileUrl != null) {
+			String fileLocation = fileUrl.getFile();
+			f = new File(fileLocation);
+			if (f.exists()) {
+				logSpecial("Found in SystemResource Directory/resourceDirectory: " + f.getAbsolutePath());
+				return f;
+			} else {
+				logSpecial("Not found in SystemResource Directory/resourceDirectory (this should never happen): " + f.getAbsolutePath());
+			}
+		} else {
+			logSpecial("Not found in SystemResource Directory/resourceDirectory: " + resourceDirectory + File.separator + filename);
+		}
+
+		// If not found, then try immediately under user's home directory first in
+		//		userHome + "/.esapi"		and secondly under
+		//		userHome + "/esapi"
+		// We look in that order because of backward compatibility issues.
+		String homeDir = userHome;
+		if ( homeDir == null ) {
+			homeDir = "";	// Without this,	homeDir + "/.esapi"	would produce
+							// the string		"null/.esapi"		which surely is not intended.
+		}
+		// First look under ".esapi" (for reasons of backward compatibility).
+		f = new File(homeDir + "/.esapi", filename);
+		if ( f.canRead() ) {
+			logSpecial("[Compatibility] Found in 'user.home' directory: " + f.getAbsolutePath());
+			return f;
+		} else {
+			// Didn't find it under old directory ".esapi" so now look under the "esapi" directory.
+			f = new File(homeDir + "/esapi", filename);
+			if ( f.canRead() ) {
+				logSpecial("Found in 'user.home' directory: " + f.getAbsolutePath());
+				return f;
+			} else {
+				logSpecial("Not found in 'user.home' (" + homeDir + ") directory: " + f.getAbsolutePath());
+			}
+		}
+
+		// return null if not found
+		return null;
+	}
+	
+    /**
+     * Used to load ESAPI.properties from a variety of different classpath locations.
+     *
+     * @param fileName The properties file filename.
+     */
+	private Properties loadConfigurationFromClasspath(String fileName) throws IllegalArgumentException {
+		Properties result = null;
+		InputStream in = null;
+
+		ClassLoader[] loaders = new ClassLoader[] {
+				Thread.currentThread().getContextClassLoader(),
+				ClassLoader.getSystemClassLoader(),
+				getClass().getClassLoader() 
+		};
+		String[] classLoaderNames = {
+				"current thread context class loader",
+				"system class loader",
+				"class loader for DefaultSecurityConfiguration class"
+		};
+
+		ClassLoader currentLoader = null;
+		for (int i = 0; i < loaders.length; i++) {
+			if (loaders[i] != null) {
+				currentLoader = loaders[i];
+				try {
+					// try root
+					String currentClasspathSearchLocation = "/ (root)";
+					in = loaders[i].getResourceAsStream(fileName);
+					
+					// try resourceDirectory folder
+					if (in == null) {
+						currentClasspathSearchLocation = resourceDirectory + "/";
+						in = currentLoader.getResourceAsStream(resourceDirectory + "/" + fileName);
+					}
+
+					// try .esapi folder. Look here first for backward compatibility.
+					if (in == null) {
+						currentClasspathSearchLocation = ".esapi/";
+						in = currentLoader.getResourceAsStream(".esapi/" + fileName);
+					} 
+					
+					// try esapi folder (new directory)
+					if (in == null) {
+						currentClasspathSearchLocation = "esapi/";
+						in = currentLoader.getResourceAsStream("esapi/" + fileName);
+					} 
+					
+					// try resources folder
+					if (in == null) {
+						currentClasspathSearchLocation = "resources/";
+						in = currentLoader.getResourceAsStream("resources/" + fileName);
+					}
+		
+					// now load the properties
+					if (in != null) {
+						result = new Properties();
+						result.load(in); // Can throw IOException
+						logSpecial("SUCCESSFULLY LOADED " + fileName + " via the CLASSPATH from '" +
+								currentClasspathSearchLocation + "' using " + classLoaderNames[i] + "!");
+						break;	// Outta here since we've found and loaded it.
+					}
+				} catch (Exception e) {
+					result = null;
+		
+				} finally {
+					try {
+						in.close();
+					} catch (Exception e) {
+					}
+				}
+			}
+		}
+
+		if (result == null) {
+			// CHECKME: This is odd...why not ConfigurationException?
+		    throw new IllegalArgumentException("Failed to load " + RESOURCE_FILE + " as a classloader resource.");
+		}
+
+		return result;
+	}
+
+    /**
+     * Used to log errors to the console during the loading of the properties file itself. Can't use
+     * standard logging in this case, since the Logger may not be initialized yet. Output is sent to
+     * {@code PrintStream} {@code System.out}.
+     *
+     * @param message The message to send to the console.
+     * @param e The error that occurred. (This value printed via {@code e.toString()}.)
+     */
+    private void logSpecial(String message, Throwable e) {
+    	StringBuffer msg = new StringBuffer(message);
+    	if (e != null) {
+    		msg.append(" Exception was: ").append( e.toString() );
+    	}
+		System.out.println( msg.toString() );
+		// if ( e != null) e.printStackTrace();		// TODO ??? Do we want this?
+    }
+
+    /**
+     * Used to log errors to the console during the loading of the properties file itself. Can't use
+     * standard logging in this case, since the Logger may not be initialized yet. Output is sent to
+     * {@code PrintStream} {@code System.out}.
+     *
+     * @param message The message to send to the console.
+     */
+    private void logSpecial(String message) {
+		System.out.println(message);
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getPasswordParameterName() {
+        return getESAPIProperty(PASSWORD_PARAMETER_NAME, "password");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getUsernameParameterName() {
+        return getESAPIProperty(USERNAME_PARAMETER_NAME, "username");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getEncryptionAlgorithm() {
+        return getESAPIProperty(ENCRYPTION_ALGORITHM, "AES");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getCipherTransformation() {
+    	assert cipherXformCurrent != null : "Current cipher transformation is null";
+    	return cipherXformCurrent;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String setCipherTransformation(String cipherXform) {
+    	String previous = getCipherTransformation();
+    	if ( cipherXform == null ) {
+    		// Special case... means set it to original value from ESAPI.properties
+    		cipherXformCurrent = cipherXformFromESAPIProp;
+    	} else {
+    		assert ! cipherXform.trim().equals("") :
+    			"Cipher transformation cannot be just white space or empty string";
+    		cipherXformCurrent = cipherXform;	// Note: No other sanity checks!!!
+    	}
+    	return previous;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean useMACforCipherText() {
+    	return getESAPIProperty(CIPHERTEXT_USE_MAC, true);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean overwritePlainText() {
+    	return getESAPIProperty(PLAINTEXT_OVERWRITE, true);
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getIVType() {
+    	String value = getESAPIProperty(IV_TYPE, "random");
+    	if ( value.equalsIgnoreCase("fixed") || value.equalsIgnoreCase("random") ) {
+    		return value;
+    	} else if ( value.equalsIgnoreCase("specified") ) {
+    		// This is planned for future implementation where setting
+    		// Encryptor.ChooseIVMethod=specified   will require setting some
+    		// other TBD property that will specify an implementation class that
+    		// will generate appropriate IVs. The intent of this would be to use
+    		// such a class with various feedback modes where it is imperative
+    		// that for a given key, any particular IV is *NEVER* reused. For
+    		// now, we will assume that generating a random IV is usually going
+    		// to be sufficient to prevent this.
+    		throw new ConfigurationException("'" + IV_TYPE + "=specified' is not yet implemented. Use 'fixed' or 'random'");
+    	} else {
+    		// TODO: Once 'specified' is legal, adjust exception msg, below.
+    		// DISCUSS: Could just log this and then silently return "random" instead.
+    		throw new ConfigurationException(value + " is illegal value for " + IV_TYPE +
+    										 ". Use 'random' (preferred) or 'fixed'.");
+    	}
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getFixedIV() {
+    	if ( getIVType().equalsIgnoreCase("fixed") ) {
+    		String ivAsHex = getESAPIProperty(FIXED_IV, ""); // No default
+    		if ( ivAsHex == null || ivAsHex.trim().equals("") ) {
+    			throw new ConfigurationException("Fixed IV requires property " +
+    						FIXED_IV + " to be set, but it is not.");
+    		}
+    		return ivAsHex;		// We do no further checks here as we have no context.
+    	} else {
+    		// DISCUSS: Should we just log a warning here and return null instead?
+    		//			If so, may cause NullPointException somewhere later.
+    		throw new ConfigurationException("IV type not 'fixed' (set to '" +
+    										 getIVType() + "'), so no fixed IV applicable.");
+    	}
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getHashAlgorithm() {
+        return getESAPIProperty(HASH_ALGORITHM, "SHA-512");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getHashIterations() {
+    	return getESAPIProperty(HASH_ITERATIONS, 1024);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+	public String getKDFPseudoRandomFunction() {
+		return getESAPIProperty(KDF_PRF_ALG, "HmacSHA256");  // NSA recommended SHA2 or better.
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getCharacterEncoding() {
+        return getESAPIProperty(CHARACTER_ENCODING, "UTF-8");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public boolean getAllowMultipleEncoding() {
+		return getESAPIProperty( ALLOW_MULTIPLE_ENCODING, false );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public boolean getAllowMixedEncoding() {
+		return getESAPIProperty( ALLOW_MIXED_ENCODING, false );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public List<String> getDefaultCanonicalizationCodecs() {
+		List<String> def = new ArrayList<String>();
+		def.add( "org.owasp.esapi.codecs.HTMLEntityCodec" );
+		def.add( "org.owasp.esapi.codecs.PercentCodec" );
+		def.add( "org.owasp.esapi.codecs.JavaScriptCodec" );
+		return getESAPIProperty( CANONICALIZATION_CODECS, def );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getDigitalSignatureAlgorithm() {
+        return getESAPIProperty(DIGITAL_SIGNATURE_ALGORITHM, "SHAwithDSA");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getDigitalSignatureKeyLength() {
+        return getESAPIProperty(DIGITAL_SIGNATURE_KEY_LENGTH, 1024);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getRandomAlgorithm() {
+        return getESAPIProperty(RANDOM_ALGORITHM, "SHA1PRNG");
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getAllowedLoginAttempts() {
+        return getESAPIProperty(ALLOWED_LOGIN_ATTEMPTS, 5);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getMaxOldPasswordHashes() {
+        return getESAPIProperty(MAX_OLD_PASSWORD_HASHES, 12);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public File getUploadDirectory() {
+    	String dir = getESAPIProperty( UPLOAD_DIRECTORY, "UploadDir");
+    	return new File( dir );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public File getUploadTempDirectory() {
+    	String dir = getESAPIProperty(UPLOAD_TEMP_DIRECTORY,
+            System.getProperty("java.io.tmpdir","UploadTempDir"));
+    	return new File( dir );
+    }
+    
+    /**
+	 * {@inheritDoc}
+	 */
+	public boolean getDisableIntrusionDetection() {
+    	String value = properties.getProperty( DISABLE_INTRUSION_DETECTION );
+    	if ("true".equalsIgnoreCase(value)) return true;
+    	return false;	// Default result
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public Threshold getQuota(String eventName) {
+        int count = getESAPIProperty("IntrusionDetector." + eventName + ".count", 0);
+        int interval =  getESAPIProperty("IntrusionDetector." + eventName + ".interval", 0);
+        List<String> actions = new ArrayList<String>();
+        String actionString = getESAPIProperty("IntrusionDetector." + eventName + ".actions", "");
+        if (actionString != null) {
+            String[] actionList = actionString.split(",");
+            actions = Arrays.asList(actionList);
+        }
+        if ( count > 0 && interval > 0 && actions.size() > 0 ) {
+        	return new Threshold(eventName, count, interval, actions);
+        }
+        return null;
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public int getLogLevel() {
+        String level = getESAPIProperty(LOG_LEVEL, "WARNING" );
+
+        if (level.equalsIgnoreCase("OFF"))
+            return Logger.OFF;
+        if (level.equalsIgnoreCase("FATAL"))
+            return Logger.FATAL;
+        if (level.equalsIgnoreCase("ERROR"))
+            return Logger.ERROR ;
+        if (level.equalsIgnoreCase("WARNING"))
+            return Logger.WARNING;
+        if (level.equalsIgnoreCase("INFO"))
+            return Logger.INFO;
+        if (level.equalsIgnoreCase("DEBUG"))
+            return Logger.DEBUG;
+        if (level.equalsIgnoreCase("TRACE"))
+            return Logger.TRACE;
+        if (level.equalsIgnoreCase("ALL"))
+            return Logger.ALL;
+
+		// This error is NOT logged the normal way because the logger constructor calls getLogLevel() and if this error occurred it would cause
+		// an infinite loop.
+        logSpecial("The LOG-LEVEL property in the ESAPI properties file has the unrecognized value: " + level + ". Using default: WARNING", null);
+        return Logger.WARNING;  // Note: The default logging level is WARNING.
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public String getLogFileName() {
+    	return getESAPIProperty( LOG_FILE_NAME, "ESAPI_logging_file" );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public int getMaxLogFileSize() {
+    	return getESAPIProperty( MAX_LOG_FILE_SIZE, DEFAULT_MAX_LOG_FILE_SIZE );
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getLogEncodingRequired() {
+    	return getESAPIProperty( LOG_ENCODING_REQUIRED, false );
+	}
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getLogApplicationName() {
+    	return getESAPIProperty( LOG_APPLICATION_NAME, true );
+	}
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getLogServerIP() {
+    	return getESAPIProperty( LOG_SERVER_IP, true );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceHttpOnlySession() {
+    	return getESAPIProperty( FORCE_HTTPONLYSESSION, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceSecureSession() {
+    	return getESAPIProperty( FORCE_SECURESESSION, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceHttpOnlyCookies() {
+    	return getESAPIProperty( FORCE_HTTPONLYCOOKIES, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public boolean getForceSecureCookies() {
+    	return getESAPIProperty( FORCE_SECURECOOKIES, true );
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public int getMaxHttpHeaderSize() {
+        return getESAPIProperty( MAX_HTTP_HEADER_SIZE, 4096 );
+	}
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public String getResponseContentType() {
+        return getESAPIProperty( RESPONSE_CONTENT_TYPE, "text/html; charset=UTF-8" );
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getHttpSessionIdName() {
+        return getESAPIProperty( HTTP_SESSION_ID_NAME, "JSESSIONID" );
+    }
+	
+	/**
+	 * {@inheritDoc}
+	 */
+    public long getRememberTokenDuration() {
+        int days = getESAPIProperty( REMEMBER_TOKEN_DURATION, 14 );
+        return (long) (1000 * 60 * 60 * 24 * days);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public int getSessionIdleTimeoutLength() {
+        int minutes = getESAPIProperty( IDLE_TIMEOUT_DURATION, 20 );
+        return 1000 * 60 * minutes;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public int getSessionAbsoluteTimeoutLength() {
+        int minutes = getESAPIProperty(ABSOLUTE_TIMEOUT_DURATION, 20 );
+        return 1000 * 60 * minutes;
+	}
+
+   /**
+    * getValidationPattern returns a single pattern based upon key
+    *
+    *  @param key
+    *  			validation pattern name you'd like
+    *  @return
+    *  			if key exists, the associated validation pattern, null otherwise
+	*/
+    public Pattern getValidationPattern( String key ) {
+    	String value = getESAPIProperty( "Validator." + key, "" );
+    	// check cache
+    	Pattern p = patternCache.get( value );
+    	if ( p != null ) return p;
+
+    	// compile a new pattern
+    	if ( value == null || value.equals( "" ) ) return null;
+    	try {
+    		Pattern q = Pattern.compile(value);
+    		patternCache.put( value, q );
+    		return q;
+    	} catch ( PatternSyntaxException e ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not a valid regex in ESAPI.properties. Returning null", null );
+    		return null;
+    	}
+    }
+
+    /**
+     * getWorkingDirectory returns the default directory where processes will be executed
+     * by the Executor.
+     */
+	public File getWorkingDirectory() {
+		String dir = getESAPIProperty( WORKING_DIRECTORY, System.getProperty( "user.dir") );
+		if ( dir != null ) {
+			return new File( dir );
+		}
+		return null;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getPreferredJCEProvider() {
+	    return properties.getProperty(PREFERRED_JCE_PROVIDER); // No default!
+	}  
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public List<String> getCombinedCipherModes()
+	{
+	    List<String> empty = new ArrayList<String>();     // Default is empty list
+	    return getESAPIProperty(COMBINED_CIPHER_MODES, empty);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public List<String> getAdditionalAllowedCipherModes()
+	{
+	    List<String> empty = new ArrayList<String>();     // Default is empty list
+	    return getESAPIProperty(ADDITIONAL_ALLOWED_CIPHER_MODES, empty);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean getLenientDatesAccepted() {
+		return getESAPIProperty( ACCEPT_LENIENT_DATES, false);
+	}
+
+	protected String getESAPIProperty( String key, String def ) {
+		String value = properties.getProperty(key);
+		if ( value == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+		return value;
+	}
+
+	protected boolean getESAPIProperty( String key, boolean def ) {
+		String property = properties.getProperty(key);
+		if ( property == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+		if ( property.equalsIgnoreCase("true") || property.equalsIgnoreCase("yes" ) ) {
+			return true;
+		}
+		if ( property.equalsIgnoreCase("false") || property.equalsIgnoreCase( "no" ) ) {
+			return false;
+		}
+		logSpecial( "SecurityConfiguration for " + key + " not either \"true\" or \"false\" in ESAPI.properties. Using default: " + def, null );
+		return def;
+	}
+
+	protected byte[] getESAPIPropertyEncoded( String key, byte[] def ) {
+		String property = properties.getProperty(key);
+		if ( property == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+        try {
+            return ESAPI.encoder().decodeFromBase64(property);
+        } catch( IOException e ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not properly Base64 encoded in ESAPI.properties. Using default: " + def, null );
+            return null;
+        }
+	}
+
+	protected int getESAPIProperty( String key, int def ) {
+		String property = properties.getProperty(key);
+		if ( property == null ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+    		return def;
+		}
+		try {
+            return Integer.parseInt( property );
+		} catch( NumberFormatException e ) {
+    		logSpecial( "SecurityConfiguration for " + key + " not an integer in ESAPI.properties. Using default: " + def, null );
+			return def;
+		}
+	}
+
+	/**
+     * Returns a {@code List} representing the parsed, comma-separated property.
+     * 
+	 * @param key  The specified property name
+	 * @param def  A default value for the property name to return if the property
+	 *             is not set.
+	 * @return A list of strings.
+	 */
+	protected List<String> getESAPIProperty( String key, List<String> def ) {
+	    String property = properties.getProperty( key );
+	    if ( property == null ) {
+	        logSpecial( "SecurityConfiguration for " + key + " not found in ESAPI.properties. Using default: " + def, null );
+	        return def;
+	    }
+	    String[] parts = property.split(",");
+	    return Arrays.asList( parts );
+	}
+
+	protected boolean shouldPrintProperties() {
+        return getESAPIProperty(PRINT_PROPERTIES_WHEN_LOADED, false);
+	}
+
+    protected Properties getESAPIProperties() {
+        return properties;
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultUser.java b/src/main/java/org/owasp/esapi/reference/DefaultUser.java
new file mode 100644
index 0000000..6b24f1a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultUser.java
@@ -0,0 +1,614 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.*;
+
+import javax.servlet.http.HttpSession;
+import java.io.Serializable;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Collections;
+import java.util.HashMap;
+/**
+ * Reference implementation of the User interface. This implementation is serialized into a flat file in a simple format.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com) <a href="http://www.digital-ritual.com">Digital Ritual Software</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.User
+ */
+public class DefaultUser implements User, Serializable {
+
+	/** The Constant serialVersionUID. */
+	private static final long serialVersionUID = 1L;
+
+	/** The idle timeout length specified in the ESAPI config file. */
+	private static final int IDLE_TIMEOUT_LENGTH = ESAPI.securityConfiguration().getSessionIdleTimeoutLength();
+	
+	/** The absolute timeout length specified in the ESAPI config file. */
+	private static final int ABSOLUTE_TIMEOUT_LENGTH = ESAPI.securityConfiguration().getSessionAbsoluteTimeoutLength();
+	
+	/** The logger used by the class. */
+	private transient final Logger logger = ESAPI.getLogger("DefaultUser");
+    
+	/** This user's account id. */
+	long accountId = 0;
+
+	/** This user's account name. */
+	private String accountName = "";
+
+	/** This user's screen name (account name alias). */
+	private String screenName = "";
+
+	/** This user's CSRF token. */
+	private String csrfToken = resetCSRFToken();
+
+	/** This user's assigned roles. */
+	private Set<String> roles = new HashSet<String>();
+
+	/** Whether this user's account is locked. */
+	private boolean locked = false;
+
+	/** Whether this user is logged in. */
+	private boolean loggedIn = true;
+
+    /** Whether this user's account is enabled. */
+	private boolean enabled = false;
+
+    /** The last host address used by this user. */
+    private String lastHostAddress;
+
+	/** The last password change time for this user. */
+	private Date lastPasswordChangeTime = new Date(0);
+
+	/** The last login time for this user. */
+	private Date lastLoginTime = new Date(0);
+
+	/** The last failed login time for this user. */
+	private Date lastFailedLoginTime = new Date(0);
+	
+	/** The expiration date/time for this user's account. */
+	private Date expirationTime = new Date(Long.MAX_VALUE);
+
+	/** The sessions this user is associated with */
+	private transient Set<HttpSession> sessions = new HashSet<HttpSession>();
+	
+	/** The event map for this User */ 
+	private transient HashMap eventMap = new HashMap();
+	
+	/* A flag to indicate that the password must be changed before the account can be used. */
+	// private boolean requiresPasswordChange = true;
+	
+	/** The failed login count for this user's account. */
+	private int failedLoginCount = 0;
+	
+	/** This user's Locale. */
+	private Locale locale;
+    
+    private static final int MAX_ROLE_LENGTH = 250;
+    
+	/**
+	 * Instantiates a new user.
+	 *
+	 * @param accountName
+	 * 		The name of this user's account.
+	 */
+	public DefaultUser(String accountName) {
+		this.accountName = accountName.toLowerCase();
+		while( true ) {
+			long id = Math.abs( ESAPI.randomizer().getRandomLong() );
+			if ( ESAPI.authenticator().getUser( id ) == null && id != 0 ) {
+				this.accountId = id;
+				break;
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void addRole(String role) throws AuthenticationException {
+		String roleName = role.toLowerCase();
+		if ( ESAPI.validator().isValidInput("addRole", roleName, "RoleName", MAX_ROLE_LENGTH, false) ) {
+			roles.add(roleName);
+			logger.info(Logger.SECURITY_SUCCESS, "Role " + roleName + " added to " + getAccountName() );
+		} else {
+			throw new AuthenticationAccountsException( "Add role failed", "Attempt to add invalid role " + roleName + " to " + getAccountName() );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void addRoles(Set<String> newRoles) throws AuthenticationException {
+        for (String newRole : newRoles)
+        {
+            addRole(newRole);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void changePassword(String oldPassword, String newPassword1, String newPassword2) throws AuthenticationException, EncryptionException {
+		ESAPI.authenticator().changePassword(this, oldPassword, newPassword1, newPassword2);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void disable() {
+		enabled = false;
+		logger.info( Logger.SECURITY_SUCCESS, "Account disabled: " + getAccountName() );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void enable() {
+		this.enabled = true;
+		logger.info( Logger.SECURITY_SUCCESS, "Account enabled: " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public long getAccountId() {
+        return accountId;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getAccountName() {
+		return accountName;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getCSRFToken() {
+		return csrfToken;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getExpirationTime() {
+		return (Date)expirationTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public int getFailedLoginCount() {
+		return failedLoginCount;
+	}
+	
+	/**
+	 * Set the failed login count
+	 * 
+	 * @param count
+	 * 			the number of failed logins
+	 */
+	void setFailedLoginCount(int count) {
+		failedLoginCount = count;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getLastFailedLoginTime() {
+		return (Date)lastFailedLoginTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getLastHostAddress() {
+		if ( lastHostAddress == null ) {
+			return "unknown";
+		}
+        return lastHostAddress;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getLastLoginTime() {
+		return (Date)lastLoginTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getLastPasswordChangeTime() {
+		return (Date)lastPasswordChangeTime.clone();
+	}
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @return
+     */
+	public String getName() {
+		return this.getAccountName();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public Set<String> getRoles() {
+		return Collections.unmodifiableSet(roles);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getScreenName() {
+		return screenName;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    public void addSession( HttpSession s ) {
+        sessions.add( s );
+    }
+    
+	/**
+	 * {@inheritDoc}
+	 */
+    public void removeSession( HttpSession s ) {
+        sessions.remove( s );
+    }
+    
+	/**
+	 * {@inheritDoc}
+     *
+     * @return
+     */
+	public Set getSessions() {
+	    return sessions;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void incrementFailedLoginCount() {
+		failedLoginCount++;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isAnonymous() {
+		// User cannot be anonymous, since we have a special User.ANONYMOUS instance
+		// for the anonymous user
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isEnabled() {
+		return enabled;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isExpired() {
+		return getExpirationTime().before( new Date() );
+
+		// If expiration should happen automatically or based on lastPasswordChangeTime?
+		//		long from = lastPasswordChangeTime.getTime();
+		//		long to = new Date().getTime();
+		//		double difference = to - from;
+		//		long days = Math.round((difference / (1000 * 60 * 60 * 24)));
+		//		return days > 60;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isInRole(String role) {
+		return roles.contains(role.toLowerCase());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isLocked() {
+		return locked;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isLoggedIn() {
+		return loggedIn;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isSessionAbsoluteTimeout() {
+		HttpSession session = ESAPI.httpUtilities().getCurrentRequest().getSession(false);
+		if ( session == null ) return true;
+		Date deadline = new Date( session.getCreationTime() + ABSOLUTE_TIMEOUT_LENGTH);
+		Date now = new Date();
+		return now.after(deadline);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isSessionTimeout() {
+		HttpSession session = ESAPI.httpUtilities().getCurrentRequest().getSession(false);
+		if ( session == null ) return true;
+		Date deadline = new Date( session.getLastAccessedTime() + IDLE_TIMEOUT_LENGTH);
+		Date now = new Date();
+		return now.after(deadline);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void lock() {
+		this.locked = true;
+		logger.info(Logger.SECURITY_SUCCESS, "Account locked: " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void loginWithPassword(String password) throws AuthenticationException {
+		if ( password == null || password.equals("") ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException( "Login failed", "Missing password: " + accountName  );
+		}
+		
+		// don't let disabled users log in
+		if ( !isEnabled() ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException("Login failed", "Disabled user attempt to login: " + accountName );
+		}
+		
+		// don't let locked users log in
+		if ( isLocked() ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException("Login failed", "Locked user attempt to login: " + accountName );
+		}
+		
+		// don't let expired users log in
+		if ( isExpired() ) {
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			throw new AuthenticationLoginException("Login failed", "Expired user attempt to login: " + accountName );
+		}
+		
+		logout();
+
+		if ( verifyPassword( password ) ) {
+			loggedIn = true;
+			ESAPI.httpUtilities().changeSessionIdentifier( ESAPI.currentRequest() );
+			ESAPI.authenticator().setCurrentUser(this);
+			setLastLoginTime(new Date());
+            setLastHostAddress( ESAPI.httpUtilities().getCurrentRequest().getRemoteAddr() );
+			logger.trace(Logger.SECURITY_SUCCESS, "User logged in: " + accountName );
+		} else {
+			loggedIn = false;
+			setLastFailedLoginTime(new Date());
+			incrementFailedLoginCount();
+			if (getFailedLoginCount() >= ESAPI.securityConfiguration().getAllowedLoginAttempts()) {
+				lock();
+			}
+			throw new AuthenticationLoginException("Login failed", "Incorrect password provided for " + getAccountName() );
+		}
+	}
+ 
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void logout() {
+		ESAPI.httpUtilities().killCookie( ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME );
+		
+		HttpSession session = ESAPI.currentRequest().getSession(false);
+		if (session != null) {
+            removeSession(session);
+			session.invalidate();
+		}
+		ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), ESAPI.securityConfiguration().getHttpSessionIdName());
+		loggedIn = false;
+		logger.info(Logger.SECURITY_SUCCESS, "Logout successful" );
+		ESAPI.authenticator().setCurrentUser(User.ANONYMOUS);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void removeRole(String role) {
+		roles.remove(role.toLowerCase());
+		logger.trace(Logger.SECURITY_SUCCESS, "Role " + role + " removed from " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * In this implementation, we have chosen to use a random token that is
+	 * stored in the User object. Note that it is possible to avoid the use of
+	 * server side state by using either the hash of the users's session id or
+	 * an encrypted token that includes a timestamp and the user's IP address.
+	 * user's IP address. A relatively short 8 character string has been chosen
+	 * because this token will appear in all links and forms.
+	 * 
+	 * @return the string
+	 */
+	public String resetCSRFToken() {
+		// user.csrfToken = ESAPI.encryptor().hash( session.getId(),user.name );
+		// user.csrfToken = ESAPI.encryptor().encrypt( address + ":" + ESAPI.encryptor().getTimeStamp();
+		csrfToken = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		return csrfToken;
+	}
+
+	/**
+	 * Sets the account id for this user's account.
+	 */
+	private void setAccountId(long accountId) {
+		this.accountId = accountId;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setAccountName(String accountName) {
+		String old = getAccountName();
+		this.accountName = accountName.toLowerCase();
+		if (old != null) {
+			if ( old.equals( "" ) ) {
+				old = "[nothing]";
+			}
+			logger.info(Logger.SECURITY_SUCCESS, "Account name changed from " + old + " to " + getAccountName() );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setExpirationTime(Date expirationTime) {
+		this.expirationTime = new Date( expirationTime.getTime() );
+		logger.info(Logger.SECURITY_SUCCESS, "Account expiration time set to " + expirationTime + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastFailedLoginTime(Date lastFailedLoginTime) {
+		this.lastFailedLoginTime = lastFailedLoginTime;
+		logger.info(Logger.SECURITY_SUCCESS, "Set last failed login time to " + lastFailedLoginTime + " for " + getAccountName() );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastHostAddress(String remoteHost) throws AuthenticationHostException
+    {
+		if ( lastHostAddress != null && !lastHostAddress.equals(remoteHost)) {
+        	// returning remote address not remote hostname to prevent DNS lookup
+			throw new AuthenticationHostException("Host change", "User session just jumped from " + lastHostAddress + " to " + remoteHost );
+		}
+		lastHostAddress = remoteHost;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastLoginTime(Date lastLoginTime) {
+		this.lastLoginTime = lastLoginTime;
+		logger.info(Logger.SECURITY_SUCCESS, "Set last successful login time to " + lastLoginTime + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setLastPasswordChangeTime(Date lastPasswordChangeTime) {
+		this.lastPasswordChangeTime = lastPasswordChangeTime;
+		logger.info(Logger.SECURITY_SUCCESS, "Set last password change time to " + lastPasswordChangeTime + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setRoles(Set<String> roles) throws AuthenticationException {
+		this.roles = new HashSet<String>();
+		addRoles(roles);
+		logger.info(Logger.SECURITY_SUCCESS, "Adding roles " + roles + " to " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setScreenName(String screenName) {
+		this.screenName = screenName;
+		logger.info(Logger.SECURITY_SUCCESS, "ScreenName changed to " + screenName + " for " + getAccountName() );
+	}
+
+	/**
+	 * {@inheritDoc}
+     *
+     * @return
+     */
+	public String toString() {
+		return "USER:" + accountName;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void unlock() {
+		this.locked = false;
+		this.failedLoginCount = 0;
+		logger.info( Logger.SECURITY_SUCCESS, "Account unlocked: " + getAccountName() );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean verifyPassword(String password) {
+		return ESAPI.authenticator().verifyPassword(this, password);
+	}
+    
+    /**
+     * Override clone and make final to prevent duplicate user objects.
+     * @return 
+     * @throws java.lang.CloneNotSupportedException
+     */
+    public final Object clone() throws java.lang.CloneNotSupportedException {
+    	  throw new java.lang.CloneNotSupportedException();
+    }
+	/**
+	 * @return the locale
+	 */
+	public Locale getLocale() {
+		return locale;
+	}
+
+	/**
+	 * @param locale the locale to set
+	 */
+	public void setLocale(Locale locale) {
+		this.locale = locale;
+	}
+    
+    public HashMap getEventMap() {
+    	return eventMap;
+    }
+    
+}
diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java
new file mode 100644
index 0000000..85c3343
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java
@@ -0,0 +1,1194 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ *
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.ValidationRule;
+import org.owasp.esapi.Validator;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationAvailabilityException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.reference.validation.CreditCardValidationRule;
+import org.owasp.esapi.reference.validation.DateValidationRule;
+import org.owasp.esapi.reference.validation.HTMLValidationRule;
+import org.owasp.esapi.reference.validation.IntegerValidationRule;
+import org.owasp.esapi.reference.validation.NumberValidationRule;
+import org.owasp.esapi.reference.validation.StringValidationRule;
+
+/**
+ * Reference implementation of the Validator interface. This implementation
+ * relies on the ESAPI Encoder, Java Pattern (regex), Date,
+ * and several other classes to provide basic validation functions. This library
+ * has a heavy emphasis on whitelist validation and canonicalization.
+ *
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ *
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class DefaultValidator implements org.owasp.esapi.Validator {
+    private static volatile Validator instance = null;
+
+    public static Validator getInstance() {
+        if ( instance == null ) {
+            synchronized ( Validator.class ) {
+                if ( instance == null ) {
+                    instance = new DefaultValidator();
+                }
+            }
+        }
+        return instance;
+    }
+
+	/** A map of validation rules */
+	private Map<String, ValidationRule> rules = new HashMap<String, ValidationRule>();
+
+	/** The encoder to use for canonicalization */
+	private Encoder encoder = null;
+
+	/** The encoder to use for file system */
+	private static Validator fileValidator = null;
+
+	/** Initialize file validator with an appropriate set of codecs */
+	static {
+		List<String> list = new ArrayList<String>();
+		list.add( "HTMLEntityCodec" );
+		list.add( "PercentCodec" );
+		Encoder fileEncoder = new DefaultEncoder( list );
+		fileValidator = new DefaultValidator( fileEncoder );
+	}
+
+
+	/**
+	 * Default constructor uses the ESAPI standard encoder for canonicalization.
+	 */
+	public DefaultValidator() {
+	    this.encoder = ESAPI.encoder();
+	}
+
+	/**
+	 * Construct a new DefaultValidator that will use the specified
+	 * Encoder for canonicalization.
+     *
+     * @param encoder
+     */
+	public DefaultValidator( Encoder encoder ) {
+	    this.encoder = encoder;
+	}
+
+
+	/**
+	 * Add a validation rule to the registry using the "type name" of the rule as the key.
+	 */
+	public void addRule( ValidationRule rule ) {
+		rules.put( rule.getTypeName(), rule );
+	}
+
+	/**
+	 * Get a validation rule from the registry with the "type name" of the rule as the key.
+	 */
+	public ValidationRule getRule( String name ) {
+		return rules.get( name );
+	}
+
+
+	/**
+	 * Returns true if data received from browser is valid. Double encoding is treated as an attack. The
+	 * default encoder supports html encoding, URL encoding, and javascript escaping. Input is canonicalized
+	 * by default before validation.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum post-canonicalized String length allowed.
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @return The canonicalized user input.
+	 * @throws IntrusionException
+	 */
+	public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws IntrusionException  {
+		return isValidInput(context, input, type, maxLength, allowNull, true);
+	}
+
+        public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException  {
+		return isValidInput(context, input, type, maxLength, allowNull, true, errors);
+	}
+
+	public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws IntrusionException  {
+		try {
+			getValidInput( context, input, type, maxLength, allowNull, canonicalize);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errors) throws IntrusionException  {
+		try {
+			getValidInput( context, input, type, maxLength, allowNull, canonicalize);
+			return true;
+		} catch( ValidationException e ) {
+			errors.addError( context, e );
+			return false;
+		}
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version.
+	 * Double encoding is treated as an attack. The default encoder supports
+	 * html encoding, URL encoding, and javascript escaping. Input is
+	 * canonicalized by default before validation.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name which maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum post-canonicalized String length allowed.
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @return The canonicalized user input.
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws ValidationException {
+		return getValidInput(context, input, type, maxLength, allowNull, true);
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version. Only
+	 * URL encoding is supported. Double encoding is treated as an attack.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name which maps to the actual regular expression in the ESAPI validation configuration file
+	 * @param maxLength The maximum String length allowed. If input is canonicalized per the canonicalize argument, then maxLength must be verified after canonicalization
+     * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param canonicalize If canonicalize is true then input will be canonicalized before validation
+	 * @return The user input, may be canonicalized if canonicalize argument is true
+	 * @throws ValidationException
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws ValidationException {
+		StringValidationRule rvr = new StringValidationRule( type, encoder );
+		Pattern p = ESAPI.securityConfiguration().getValidationPattern( type );
+		if ( p != null ) {
+			rvr.addWhitelistPattern( p );
+		} else {
+            // Issue 232 - Specify requested type in exception message - CS
+			throw new IllegalArgumentException("The selected type [" + type + "] was not set via the ESAPI validation configuration");
+		}
+		rvr.setMaximumLength(maxLength);
+		rvr.setAllowNull(allowNull);
+		rvr.setValidateInputAndCanonical(canonicalize);
+		return rvr.getValid(context, input);
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version. Only
+	 * URL encoding is supported. Double encoding is treated as an attack. Input
+	 * is canonicalized by default before validation.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum String length allowed. If input is canonicalized per the canonicalize argument, then maxLength must be verified after canonicalization
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param errors If ValidationException is thrown, then add to error list instead of throwing out to caller
+	 * @return The canonicalized user input.
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return getValidInput(context, input, type, maxLength, allowNull, true, errors);
+	}
+
+	/**
+	 * Validates data received from the browser and returns a safe version. Only
+	 * URL encoding is supported. Double encoding is treated as an attack.
+	 *
+	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
+	 * @param input The actual user input data to validate.
+	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
+	 * @param maxLength The maximum post-canonicalized String length allowed
+	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
+	 * @param canonicalize If canonicalize is true then input will be canonicalized before validation
+	 * @param errors If ValidationException is thrown, then add to error list instead of throwing out to caller
+	 * @return The user input, may be canonicalized if canonicalize argument is true
+	 * @throws IntrusionException
+	 */
+	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidInput(context,  input,  type,  maxLength,  allowNull, canonicalize);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDate(String context, String input, DateFormat format, boolean allowNull) throws IntrusionException {
+		try {
+			getValidDate( context, input, format, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidDate( context, input, format, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getValidDate(String context, String input, DateFormat format, boolean allowNull) throws ValidationException, IntrusionException {
+		DateValidationRule dvr = new DateValidationRule( "SimpleDate", encoder, format);
+		dvr.setAllowNull(allowNull);
+		return dvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Date getValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidDate(context, input, format, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return null
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws IntrusionException {
+		try {
+			getValidSafeHTML( context, input, maxLength, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidSafeHTML( context, input, maxLength, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * This implementation relies on the OWASP AntiSamy project.
+	 */
+	public String getValidSafeHTML( String context, String input, int maxLength, boolean allowNull ) throws ValidationException, IntrusionException {
+		HTMLValidationRule hvr = new HTMLValidationRule( "safehtml", encoder );
+		hvr.setMaximumLength(maxLength);
+		hvr.setAllowNull(allowNull);
+		hvr.setValidateInputAndCanonical(false);
+		return hvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidSafeHTML(context, input, maxLength, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidCreditCard(String context, String input, boolean allowNull) throws IntrusionException {
+		try {
+			getValidCreditCard( context, input, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidCreditCard( context, input, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidCreditCard(String context, String input, boolean allowNull) throws ValidationException, IntrusionException {
+		CreditCardValidationRule ccvr = new CreditCardValidationRule( "creditcard", encoder );
+		ccvr.setAllowNull(allowNull);
+		return ccvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidCreditCard(context, input, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+	 */
+	public boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws IntrusionException {
+		try {
+			getValidDirectoryPath( context, input, parent, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+	 */
+	public boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidDirectoryPath( context, input, parent, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws ValidationException, IntrusionException {
+		try {
+			if (isEmpty(input)) {
+				if (allowNull) return null;
+       			throw new ValidationException( context + ": Input directory path required", "Input directory path required: context=" + context + ", input=" + input, context );
+			}
+
+			File dir = new File( input );
+
+			// check dir exists and parent exists and dir is inside parent
+			if ( !dir.exists() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, does not exist: context=" + context + ", input=" + input );
+			}
+			if ( !dir.isDirectory() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, not a directory: context=" + context + ", input=" + input );
+			}
+			if ( !parent.exists() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, specified parent does not exist: context=" + context + ", input=" + input + ", parent=" + parent );
+			}
+			if ( !parent.isDirectory() ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, specified parent is not a directory: context=" + context + ", input=" + input + ", parent=" + parent );
+			}
+			if ( !dir.getCanonicalPath().startsWith(parent.getCanonicalPath() ) ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory, not inside specified parent: context=" + context + ", input=" + input + ", parent=" + parent );
+			}
+
+			// check canonical form matches input
+			String canonicalPath = dir.getCanonicalPath();
+			String canonical = fileValidator.getValidInput( context, canonicalPath, "DirectoryName", 255, false);
+			if ( !canonical.equals( input ) ) {
+				throw new ValidationException( context + ": Invalid directory name", "Invalid directory name does not match the canonical path: context=" + context + ", input=" + input + ", canonical=" + canonical, context );
+			}
+			return canonical;
+		} catch (Exception e) {
+			throw new ValidationException( context + ": Invalid directory name", "Failure to validate directory path: context=" + context + ", input=" + input, e, context );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+
+		try {
+			return getValidDirectoryPath(context, input, parent, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, boolean allowNull) throws IntrusionException {
+		return isValidFileName( context, input, ESAPI.securityConfiguration().getAllowedFileExtensions(), allowNull );
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return isValidFileName( context, input, ESAPI.securityConfiguration().getAllowedFileExtensions(), allowNull, errors );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws IntrusionException {
+		try {
+			getValidFileName( context, input, allowedExtensions, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidFileName( context, input, allowedExtensions, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException {
+		if ((allowedExtensions == null) || (allowedExtensions.isEmpty())) {
+			throw new ValidationException( "Internal Error", "getValidFileName called with an empty or null list of allowed Extensions, therefore no files can be uploaded" );
+		}
+
+		String canonical = "";
+		// detect path manipulation
+		try {
+			if (isEmpty(input)) {
+				if (allowNull) return null;
+	   			throw new ValidationException( context + ": Input file name required", "Input required: context=" + context + ", input=" + input, context );
+			}
+
+			// do basic validation
+	        canonical = new File(input).getCanonicalFile().getName();
+	        getValidInput( context, input, "FileName", 255, true );
+
+			File f = new File(canonical);
+			String c = f.getCanonicalPath();
+			String cpath = c.substring(c.lastIndexOf(File.separator) + 1);
+
+
+			// the path is valid if the input matches the canonical path
+			if (!input.equals(cpath)) {
+				throw new ValidationException( context + ": Invalid file name", "Invalid directory name does not match the canonical path: context=" + context + ", input=" + input + ", canonical=" + canonical, context );
+			}
+
+		} catch (IOException e) {
+			throw new ValidationException( context + ": Invalid file name", "Invalid file name does not exist: context=" + context + ", canonical=" + canonical, e, context );
+		}
+
+		// verify extensions
+		Iterator<String> i = allowedExtensions.iterator();
+		while (i.hasNext()) {
+			String ext = i.next();
+			if (input.toLowerCase().endsWith(ext.toLowerCase())) {
+				return canonical;
+			}
+		}
+		throw new ValidationException( context + ": Invalid file name does not have valid extension ( "+allowedExtensions+")", "Invalid file name does not have valid extension ( "+allowedExtensions+"): context=" + context+", input=" + input, context );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValidFileName(String context, String input, List<String> allowedParameters, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidFileName(context, input, allowedParameters, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return "";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws IntrusionException {
+		try {
+			getValidNumber(context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidNumber(context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws ValidationException, IntrusionException {
+		Double minDoubleValue = new Double(minValue);
+		Double maxDoubleValue = new Double(maxValue);
+		return getValidDouble(context, input, minDoubleValue.doubleValue(), maxDoubleValue.doubleValue(), allowNull);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidNumber(context, input, minValue, maxValue, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws IntrusionException {
+        try {
+            getValidDouble( context, input, minValue, maxValue, allowNull );
+            return true;
+        } catch( Exception e ) {
+            return false;
+        }
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+        try {
+            getValidDouble( context, input, minValue, maxValue, allowNull );
+            return true;
+        } catch( ValidationException e ) {
+            errors.addError(context, e);
+            return false;
+        }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws ValidationException, IntrusionException {
+		NumberValidationRule nvr = new NumberValidationRule( "number", encoder, minValue, maxValue );
+		nvr.setAllowNull(allowNull);
+		return nvr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidDouble(context, input, minValue, maxValue, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+
+		return new Double(Double.NaN);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws IntrusionException {
+		try {
+			getValidInteger( context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidInteger( context, input, minValue, maxValue, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws ValidationException, IntrusionException {
+		IntegerValidationRule ivr = new IntegerValidationRule( "number", encoder, minValue, maxValue );
+		ivr.setAllowNull(allowNull);
+		return ivr.getValid(context, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidInteger(context, input, minValue, maxValue, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws IntrusionException {
+		try {
+			getValidFileContent( context, input, maxBytes, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 */
+	public boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidFileContent( context, input, maxBytes, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException {
+		if (isEmpty(input)) {
+			if (allowNull) return null;
+   			throw new ValidationException( context + ": Input required", "Input required: context=" + context + ", input=" + input, context );
+		}
+
+		long esapiMaxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize();
+		if (input.length > esapiMaxBytes ) throw new ValidationException( context + ": Invalid file content can not exceed " + esapiMaxBytes + " bytes", "Exceeded ESAPI max length", context );
+		if (input.length > maxBytes ) throw new ValidationException( context + ": Invalid file content can not exceed " + maxBytes + " bytes", "Exceeded maxBytes ( " + input.length + ")", context );
+
+		return input;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidFileContent(context, input, maxBytes, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// return empty byte array on error
+		return new byte[0];
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+     */
+	public boolean isValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull) throws IntrusionException {
+		return( isValidFileName( context, filename, allowNull ) &&
+				isValidDirectoryPath( context, directorypath, parent, allowNull ) &&
+				isValidFileContent( context, content, maxBytes, allowNull ) );
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * <p><b>Note:</b> On platforms that support symlinks, this function will fail canonicalization if directorypath
+	 * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real
+	 * path (/private/etc), not the symlink (/etc).</p>
+     */
+	public boolean isValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return( isValidFileName( context, filename, allowNull, errors ) &&
+				isValidDirectoryPath( context, directorypath, parent, allowNull, errors ) &&
+				isValidFileContent( context, content, maxBytes, allowNull, errors ) );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException {
+		getValidFileName( context, filename, allowedExtensions, allowNull );
+		getValidDirectoryPath( context, directorypath, parent, allowNull );
+		getValidFileContent( context, content, maxBytes, allowNull );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errors)
+		throws IntrusionException {
+		try {
+			assertValidFileUpload(context, filepath, filename, parent, content, maxBytes, allowedExtensions, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+	}
+
+	 /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is a valid list item.
+	 */
+	public boolean isValidListItem(String context, String input, List<String> list) {
+		try {
+			getValidListItem( context, input, list);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is a valid list item.
+	 */
+	public boolean isValidListItem(String context, String input, List<String> list, ValidationErrorList errors) {
+		try {
+			getValidListItem( context, input, list);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Returns the list item that exactly matches the canonicalized input. Invalid or non-matching input
+	 * will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 */
+	public String getValidListItem(String context, String input, List<String> list) throws ValidationException, IntrusionException {
+		if (list.contains(input)) return input;
+		throw new ValidationException( context + ": Invalid list item", "Invalid list item: context=" + context + ", input=" + input, context );
+	}
+
+
+	/**
+	 * ValidationErrorList variant of getValidListItem
+     *
+     * @param errors
+     */
+	public String getValidListItem(String context, String input, List<String> list, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidListItem(context, input, list);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+	 /**
+	 * {@inheritDoc}
+     */
+	public boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> requiredNames, Set<String> optionalNames) {
+		try {
+			assertValidHTTPRequestParameterSet( context, request, requiredNames, optionalNames);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+         /**
+	 * {@inheritDoc}
+     */
+	public boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> requiredNames, Set<String> optionalNames, ValidationErrorList errors) {
+		try {
+			assertValidHTTPRequestParameterSet( context, request, requiredNames, optionalNames);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Validates that the parameters in the current request contain all required parameters and only optional ones in
+	 * addition. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 *
+	 * Uses current HTTPRequest
+	 */
+	public void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional) throws ValidationException, IntrusionException {
+		Set<String> actualNames = request.getParameterMap().keySet();
+
+		// verify ALL required parameters are present
+		Set<String> missing = new HashSet<String>(required);
+		missing.removeAll(actualNames);
+		if (missing.size() > 0) {
+			throw new ValidationException( context + ": Invalid HTTP request missing parameters", "Invalid HTTP request missing parameters " + missing + ": context=" + context, context );
+		}
+
+		// verify ONLY optional + required parameters are present
+		Set<String> extra = new HashSet<String>(actualNames);
+		extra.removeAll(required);
+		extra.removeAll(optional);
+		if (extra.size() > 0) {
+			throw new ValidationException( context + ": Invalid HTTP request extra parameters " + extra, "Invalid HTTP request extra parameters " + extra + ": context=" + context, context );
+		}
+	}
+
+	/**
+	 * ValidationErrorList variant of assertIsValidHTTPRequestParameterSet
+     *
+	 * Uses current HTTPRequest saved in ESAPI Authenticator
+     * @param errors
+     */
+	public void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set<String> required, Set<String> optional, ValidationErrorList errors) throws IntrusionException {
+		try {
+			assertValidHTTPRequestParameterSet(context, request, required, optional);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+	 * Checks that all bytes are valid ASCII characters (between 33 and 126
+	 * inclusive). This implementation does no decoding. http://en.wikipedia.org/wiki/ASCII.
+	 */
+	public boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+     * {@inheritDoc}
+     *
+	 * Checks that all bytes are valid ASCII characters (between 33 and 126
+	 * inclusive). This implementation does no decoding. http://en.wikipedia.org/wiki/ASCII.
+	 */
+	public boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Returns canonicalized and validated printable characters as a byte array. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+     *
+     * @throws IntrusionException
+     */
+	public char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException {
+		if (isEmpty(input)) {
+			if (allowNull) return null;
+   			throw new ValidationException(context + ": Input bytes required", "Input bytes required: HTTP request is null", context );
+		}
+
+		if (input.length > maxLength) {
+			throw new ValidationException(context + ": Input bytes can not exceed " + maxLength + " bytes", "Input exceeds maximum allowed length of " + maxLength + " by " + (input.length-maxLength) + " bytes: context=" + context + ", input=" + new String( input ), context);
+		}
+
+		for (int i = 0; i < input.length; i++) {
+			if (input[i] <= 0x20 || input[i] >= 0x7E ) {
+				throw new ValidationException(context + ": Invalid input bytes: context=" + context, "Invalid non-ASCII input bytes, context=" + context + ", input=" + new String( input ), context);
+			}
+		}
+		return input;
+	}
+
+	/**
+	 * ValidationErrorList variant of getValidPrintable
+     *
+     * @param errors
+     */
+	public char[] getValidPrintable(String context, char[] input,int maxLength, boolean allowNull, ValidationErrorList errors)
+		throws IntrusionException {
+
+		try {
+			return getValidPrintable(context, input, maxLength, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+
+	 /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is valid printable ASCII characters (32-126).
+	 */
+	public boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( Exception e ) {
+			return false;
+		}
+	}
+
+        /**
+	 * {@inheritDoc}
+	 *
+	 * Returns true if input is valid printable ASCII characters (32-126).
+	 */
+	public boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			getValidPrintable( context, input, maxLength, allowNull);
+			return true;
+		} catch( ValidationException e ) {
+            errors.addError(context, e);
+			return false;
+		}
+	}
+
+	/**
+	 * Returns canonicalized and validated printable characters as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+     *
+     * @throws IntrusionException
+     */
+	public String getValidPrintable(String context, String input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException {
+		try {
+    		String canonical = encoder.canonicalize(input);
+    		return new String( getValidPrintable( context, canonical.toCharArray(), maxLength, allowNull) );
+	    //TODO - changed this to base Exception since we no longer need EncodingException
+    	//TODO - this is a bit lame: we need to re-think this function.
+		} catch (Exception e) {
+	        throw new ValidationException( context + ": Invalid printable input", "Invalid encoding of printable input, context=" + context + ", input=" + input, e, context);
+	    }
+	}
+
+	/**
+	 * ValidationErrorList variant of getValidPrintable
+     *
+     * @param errors
+     */
+	public String getValidPrintable(String context, String input,int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidPrintable(context, input, maxLength, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+
+	/**
+	 * Returns true if input is a valid redirect location.
+	 */
+	public boolean isValidRedirectLocation(String context, String input, boolean allowNull) throws IntrusionException {
+		return ESAPI.validator().isValidInput( context, input, "Redirect", 512, allowNull);
+	}
+
+        /**
+	 * Returns true if input is a valid redirect location.
+	 */
+	public boolean isValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		return ESAPI.validator().isValidInput( context, input, "Redirect", 512, allowNull, errors);
+	}
+
+
+	/**
+	 * Returns a canonicalized and validated redirect location as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack
+	 * will generate a descriptive IntrusionException.
+	 */
+	public String getValidRedirectLocation(String context, String input, boolean allowNull) throws ValidationException, IntrusionException {
+		return ESAPI.validator().getValidInput( context, input, "Redirect", 512, allowNull);
+	}
+
+	/**
+	 * ValidationErrorList variant of getValidRedirectLocation
+     *
+     * @param errors
+     */
+	public String getValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
+		try {
+			return getValidRedirectLocation(context, input, allowNull);
+		} catch (ValidationException e) {
+			errors.addError(context, e);
+		}
+		// error has been added to list, so return original input
+		return input;
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+	 * This implementation reads until a newline or the specified number of
+	 * characters.
+     *
+     * @param in
+     * @param max
+     */
+	public String safeReadLine(InputStream in, int max) throws ValidationException {
+		if (max <= 0) {
+			throw new ValidationAvailabilityException( "Invalid input", "Invalid readline. Must read a positive number of bytes from the stream");
+		}
+
+		StringBuilder sb = new StringBuilder();
+		int count = 0;
+		int c;
+
+		try {
+			while (true) {
+				c = in.read();
+				if ( c == -1 ) {
+					if (sb.length() == 0) {
+						return null;
+					}
+					break;
+				}
+				if (c == '\n' || c == '\r') {
+					break;
+				}
+				count++;
+				if (count > max) {
+					throw new ValidationAvailabilityException( "Invalid input", "Invalid readLine. Read more than maximum characters allowed (" + max + ")");
+				}
+				sb.append((char) c);
+			}
+			return sb.toString();
+		} catch (IOException e) {
+			throw new ValidationAvailabilityException( "Invalid input", "Invalid readLine. Problem reading from input stream", e);
+		}
+	}
+
+	/**
+	 * Helper function to check if a String is empty
+	 *
+	 * @param input string input value
+	 * @return boolean response if input is empty or not
+	 */
+	private final boolean isEmpty(String input) {
+		return (input==null || input.trim().length() == 0);
+	}
+
+	/**
+	 * Helper function to check if a byte array is empty
+	 *
+	 * @param input string input value
+	 * @return boolean response if input is empty or not
+	 */
+	private final boolean isEmpty(byte[] input) {
+		return (input==null || input.length == 0);
+	}
+
+
+	/**
+	 * Helper function to check if a char array is empty
+	 *
+	 * @param input string input value
+	 * @return boolean response if input is empty or not
+	 */
+	private final boolean isEmpty(char[] input) {
+		return (input==null || input.length == 0);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java b/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java
new file mode 100644
index 0000000..6b76c5e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java
@@ -0,0 +1,725 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.*;
+import org.owasp.esapi.errors.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Reference implementation of the Authenticator interface. This reference implementation is backed by a simple text
+ * file that contains serialized information about users. Many organizations will want to create their own
+ * implementation of the methods provided in the Authenticator interface backed by their own user repository. This
+ * reference implementation captures information about users in a simple text file format that contains user information
+ * separated by the pipe "|" character. Here's an example of a single line from the users.txt file:
+ * <p/>
+ * <PRE>
+ * <p/>
+ * account id | account name | hashed password | roles | lockout | status | old password hashes | last
+ * hostname | last change | last login | last failed | expiration | failed
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ * 1203123710837 | mitch | 44k/NAzQUlrCq9musTGGkcMNmdzEGJ8w8qZTLzpxLuQ= | admin,user | unlocked | enabled |
+ * u10dW4vTo3ZkoM5xP+blayWCz7KdPKyKUojOn9GJobg= | 192.168.1.255 | 1187201000926 | 1187200991568 | 1187200605330 |
+ * 2187200605330 | 1
+ * <p/>
+ * </PRE>
+ *
+ * @author <a href="mailto:jeff.williams at aspectsecurity.com?subject=ESAPI question">Jeff Williams</a> at <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com) <a href="http://www.digital-ritual.com">Digital Ritual Software</a>
+ * @see org.owasp.esapi.Authenticator
+ * @since June 1, 2007
+ */
+public class FileBasedAuthenticator extends AbstractAuthenticator {
+
+    private static volatile Authenticator singletonInstance;
+
+    public static Authenticator getInstance()
+    {
+        if ( singletonInstance == null ) {
+            synchronized ( FileBasedAuthenticator.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new FileBasedAuthenticator();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    /**
+     * The logger.
+     */
+    private final Logger logger = ESAPI.getLogger("Authenticator");
+
+    /**
+     * The file that contains the user db
+     */
+    private File userDB = null;
+
+    /**
+     * How frequently to check the user db for external modifications
+     */
+    private long checkInterval = 60 * 1000;
+
+    /**
+     * The last modified time we saw on the user db.
+     */
+    private long lastModified = 0;
+
+    /**
+     * The last time we checked if the user db had been modified externally
+     */
+    private long lastChecked = 0;
+
+    private static final int MAX_ACCOUNT_NAME_LENGTH = 250;
+
+    /**
+     * Fail safe main program to add or update an account in an emergency.
+     * <p/>
+     * Warning: this method does not perform the level of validation and checks
+     * generally required in ESAPI, and can therefore be used to create a username and password that do not comply
+     * with the username and password strength requirements.
+     * <p/>
+     * Example: Use this to add the alice account with the admin role to the users file:
+     * <PRE>
+     * <p/>
+     * java -Dorg.owasp.esapi.resources="/path/resources" -classpath esapi.jar org.owasp.esapi.Authenticator alice password admin
+     * <p/>
+     * </PRE>
+     *
+     * @param args the arguments (username, password, role)
+     * @throws Exception the exception
+     */
+    public static void main(String[] args) throws Exception {
+        if (args.length != 3) {
+            System.out.println("Usage: Authenticator accountname password role");
+            return;
+        }
+        FileBasedAuthenticator auth = new FileBasedAuthenticator();
+        String accountName = args[0].toLowerCase();
+        String password = args[1];
+        String role = args[2];
+        DefaultUser user = (DefaultUser) auth.getUser(args[0]);
+        if (user == null) {
+            user = new DefaultUser(accountName);
+            String newHash = auth.hashPassword(password, accountName);
+            auth.setHashedPassword(user, newHash);
+            user.addRole(role);
+            user.enable();
+            user.unlock();
+            auth.userMap.put(user.getAccountId(), user);
+            System.out.println("New user created: " + accountName);
+            auth.saveUsers();
+            System.out.println("User account " + user.getAccountName() + " updated");
+        } else {
+            System.err.println("User account " + user.getAccountName() + " already exists!");
+        }
+    }
+
+    /**
+     * Add a hash to a User's hashed password list.  This method is used to store a user's old password hashes
+     * to be sure that any new passwords are not too similar to old passwords.
+     *
+     * @param user the user to associate with the new hash
+     * @param hash the hash to store in the user's password hash list
+     */
+    private void setHashedPassword(User user, String hash) {
+        List<String> hashes = getAllHashedPasswords(user, true);
+        hashes.add(0, hash);
+        if (hashes.size() > ESAPI.securityConfiguration().getMaxOldPasswordHashes()) {
+            hashes.remove(hashes.size() - 1);
+        }
+        logger.info(Logger.SECURITY_SUCCESS, "New hashed password stored for " + user.getAccountName());
+    }
+
+    /**
+     * Return the specified User's current hashed password.
+     *
+     * @param user this User's current hashed password will be returned
+     * @return the specified User's current hashed password
+     */
+    String getHashedPassword(User user) {
+        List hashes = getAllHashedPasswords(user, false);
+        return (String) hashes.get(0);
+    }
+
+    /**
+     * Set the specified User's old password hashes.  This will not set the User's current password hash.
+     *
+     * @param user      the User whose old password hashes will be set
+     * @param oldHashes a list of the User's old password hashes     *
+     */
+    void setOldPasswordHashes(User user, List<String> oldHashes) {
+        List<String> hashes = getAllHashedPasswords(user, true);
+        if (hashes.size() > 1) {
+            hashes.removeAll(hashes.subList(1, hashes.size() - 1));
+        }
+        hashes.addAll(oldHashes);
+    }
+
+    /**
+     * Returns all of the specified User's hashed passwords.  If the User's list of passwords is null,
+     * and create is set to true, an empty password list will be associated with the specified User
+     * and then returned. If the User's password map is null and create is set to false, an exception
+     * will be thrown.
+     *
+     * @param user   the User whose old hashes should be returned
+     * @param create true - if no password list is associated with this user, create one
+     *               false - if no password list is associated with this user, do not create one
+     * @return a List containing all of the specified User's password hashes
+     */
+    List<String> getAllHashedPasswords(User user, boolean create) {
+        List<String> hashes = passwordMap.get(user);
+        if (hashes != null) {
+            return hashes;
+        }
+        if (create) {
+            hashes = new ArrayList<String>();
+            passwordMap.put(user, hashes);
+            return hashes;
+        }
+        throw new RuntimeException("No hashes found for " + user.getAccountName() + ". Is User.hashcode() and equals() implemented correctly?");
+    }
+
+    /**
+     * Get a List of the specified User's old password hashes.  This will not return the User's current
+     * password hash.
+     *
+     * @param user he user whose old password hashes should be returned
+     * @return the specified User's old password hashes
+     */
+    List<String> getOldPasswordHashes(User user) {
+        List<String> hashes = getAllHashedPasswords(user, false);
+        if (hashes.size() > 1) {
+            return Collections.unmodifiableList(hashes.subList(1, hashes.size() - 1));
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * The user map.
+     */
+    private Map<Long, User> userMap = new HashMap<Long, User>();
+
+    // Map<User, List<String>>, where the strings are password hashes, with the current hash in entry 0
+    private Map<User, List<String>> passwordMap = new Hashtable<User, List<String>>();
+
+
+
+    /**
+     *
+     */
+    private FileBasedAuthenticator() {
+    	super();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized User createUser(String accountName, String password1, String password2) throws AuthenticationException {
+        loadUsersIfNecessary();
+        if (accountName == null) {
+            throw new AuthenticationAccountsException("Account creation failed", "Attempt to create user with null accountName");
+        }
+        if (getUser(accountName) != null) {
+            throw new AuthenticationAccountsException("Account creation failed", "Duplicate user creation denied for " + accountName);
+        }
+
+        verifyAccountNameStrength(accountName);
+
+        if (password1 == null) {
+            throw new AuthenticationCredentialsException("Invalid account name", "Attempt to create account " + accountName + " with a null password");
+        }
+        
+        DefaultUser user = new DefaultUser(accountName);
+        
+        verifyPasswordStrength(null, password1, user);
+
+        if (!password1.equals(password2)) {
+            throw new AuthenticationCredentialsException("Passwords do not match", "Passwords for " + accountName + " do not match");
+        }
+
+        try {
+            setHashedPassword(user, hashPassword(password1, accountName));
+        } catch (EncryptionException ee) {
+            throw new AuthenticationException("Internal error", "Error hashing password for " + accountName, ee);
+        }
+        userMap.put(user.getAccountId(), user);
+        logger.info(Logger.SECURITY_SUCCESS, "New user created: " + accountName);
+        saveUsers();
+        return user;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String generateStrongPassword() {
+        return generateStrongPassword("");
+    }
+
+    /**
+     * Generate a strong password that is not similar to the specified old password.
+     *
+     * @param oldPassword the password to be compared to the new password for similarity
+     * @return a new strong password that is dissimilar to the specified old password
+     */
+    private String generateStrongPassword(String oldPassword) {
+        Randomizer r = ESAPI.randomizer();
+        int letters = r.getRandomInteger(4, 6);  // inclusive, exclusive
+        int digits = 7 - letters;
+        String passLetters = r.getRandomString(letters, EncoderConstants.CHAR_PASSWORD_LETTERS);
+        String passDigits = r.getRandomString(digits, EncoderConstants.CHAR_PASSWORD_DIGITS);
+        String passSpecial = r.getRandomString(1, EncoderConstants.CHAR_PASSWORD_SPECIALS);
+        String newPassword = passLetters + passSpecial + passDigits;
+        if (StringUtilities.getLevenshteinDistance(oldPassword, newPassword) > 5) {
+            return newPassword;
+        }
+        return generateStrongPassword(oldPassword);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void changePassword(User user, String currentPassword,
+                               String newPassword, String newPassword2)
+            throws AuthenticationException {
+        String accountName = user.getAccountName();
+        try {
+            String currentHash = getHashedPassword(user);
+            String verifyHash = hashPassword(currentPassword, accountName);
+            if (!currentHash.equals(verifyHash)) {
+                throw new AuthenticationCredentialsException("Password change failed", "Authentication failed for password change on user: " + accountName);
+            }
+            if (newPassword == null || newPassword2 == null || !newPassword.equals(newPassword2)) {
+                throw new AuthenticationCredentialsException("Password change failed", "Passwords do not match for password change on user: " + accountName);
+            }
+            verifyPasswordStrength(currentPassword, newPassword, user);
+            user.setLastPasswordChangeTime(new Date());
+            String newHash = hashPassword(newPassword, accountName);
+            if (getOldPasswordHashes(user).contains(newHash)) {
+                throw new AuthenticationCredentialsException("Password change failed", "Password change matches a recent password for user: " + accountName);
+            }
+            setHashedPassword(user, newHash);
+            logger.info(Logger.SECURITY_SUCCESS, "Password changed for user: " + accountName);
+            // jtm - 11/2/2010 - added to resolve http://code.google.com/p/owasp-esapi-java/issues/detail?id=13
+            saveUsers();
+        } catch (EncryptionException ee) {
+            throw new AuthenticationException("Password change failed", "Encryption exception changing password for " + accountName, ee);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean verifyPassword(User user, String password) {
+        String accountName = user.getAccountName();
+        try {
+            String hash = hashPassword(password, accountName);
+            String currentHash = getHashedPassword(user);
+            if (hash.equals(currentHash)) {
+                user.setLastLoginTime(new Date());
+                ((DefaultUser) user).setFailedLoginCount(0);
+                logger.info(Logger.SECURITY_SUCCESS, "Password verified for " + accountName);
+                return true;
+            }
+        } catch (EncryptionException e) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Encryption error verifying password for " + accountName);
+        }
+        logger.fatal(Logger.SECURITY_FAILURE, "Password verification failed for " + accountName);
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String generateStrongPassword(User user, String oldPassword) {
+        String newPassword = generateStrongPassword(oldPassword);
+        if (newPassword != null) {
+            logger.info(Logger.SECURITY_SUCCESS, "Generated strong password for " + user.getAccountName());
+        }
+        return newPassword;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized User getUser(long accountId) {
+        if (accountId == 0) {
+            return User.ANONYMOUS;
+        }
+        loadUsersIfNecessary();
+        return userMap.get(accountId);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized User getUser(String accountName) {
+        if (accountName == null) {
+            return User.ANONYMOUS;
+        }
+        loadUsersIfNecessary();
+        for (User u : userMap.values()) {
+            if (u.getAccountName().equalsIgnoreCase(accountName)) {
+                return u;
+            }
+        }
+        return null;
+    }
+
+ 
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized Set getUserNames() {
+        loadUsersIfNecessary();
+        HashSet<String> results = new HashSet<String>();
+        for (User u : userMap.values()) {
+            results.add(u.getAccountName());
+        }
+        return results;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws EncryptionException
+     */
+    public String hashPassword(String password, String accountName) throws EncryptionException {
+        String salt = accountName.toLowerCase();
+        return ESAPI.encryptor().hash(password, salt);
+    }
+
+    /**
+     * Load users if they haven't been loaded in a while.
+     */
+    protected void loadUsersIfNecessary() {
+        if (userDB == null) {
+            userDB = ESAPI.securityConfiguration().getResourceFile("users.txt");
+        }
+        if (userDB == null) {
+            userDB = new File(System.getProperty("user.home") + "/.esapi", "users.txt");
+            try {
+                if (!userDB.createNewFile()) throw new IOException("Unable to create the user file");
+                logger.warning(Logger.SECURITY_SUCCESS, "Created " + userDB.getAbsolutePath());
+            } catch (IOException e) {
+                logger.fatal(Logger.SECURITY_FAILURE, "Could not create " + userDB.getAbsolutePath(), e);
+            }
+        }
+
+        // We only check at most every checkInterval milliseconds
+        long now = System.currentTimeMillis();
+        if (now - lastChecked < checkInterval) {
+            return;
+        }
+        lastChecked = now;
+
+        if (lastModified == userDB.lastModified()) {
+            return;
+        }
+        loadUsersImmediately();
+    }
+
+    // file was touched so reload it
+    /**
+     *
+     */
+    protected void loadUsersImmediately() {
+        synchronized (this) {
+            logger.trace(Logger.SECURITY_SUCCESS, "Loading users from " + userDB.getAbsolutePath(), null);
+
+            BufferedReader reader = null;
+            try {
+                HashMap<Long, User> map = new HashMap<Long, User>();
+                reader = new BufferedReader(new FileReader(userDB));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    if (line.length() > 0 && line.charAt(0) != '#') {
+                        DefaultUser user = createUser(line);
+                        if (map.containsKey(new Long(user.getAccountId()))) {
+                            logger.fatal(Logger.SECURITY_FAILURE, "Problem in user file. Skipping duplicate user: " + user, null);
+                        }
+                        map.put(user.getAccountId(), user);
+                    }
+                }
+                userMap = map;
+                this.lastModified = System.currentTimeMillis();
+                logger.trace(Logger.SECURITY_SUCCESS, "User file reloaded: " + map.size(), null);
+            } catch (Exception e) {
+                logger.fatal(Logger.SECURITY_FAILURE, "Failure loading user file: " + userDB.getAbsolutePath(), e);
+            } finally {
+                try {
+                    if (reader != null) {
+                        reader.close();
+                    }
+                } catch (IOException e) {
+                    logger.fatal(Logger.SECURITY_FAILURE, "Failure closing user file: " + userDB.getAbsolutePath(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new user with all attributes from a String.  The format is:
+     * accountId | accountName | password | roles (comma separated) | unlocked | enabled | old password hashes (comma separated) | last host address | last password change time | last long time | last failed login time | expiration time | failed login count
+     * This method verifies the account name and password strength, creates a new CSRF token, then returns the newly created user.
+     *
+     * @param line parameters to set as attributes for the new User.
+     * @return the newly created User
+     * @throws AuthenticationException
+     */
+    private DefaultUser createUser(String line) throws AuthenticationException {
+        String[] parts = line.split(" *\\| *");
+        String accountIdString = parts[0];
+        long accountId = Long.parseLong(accountIdString);
+        String accountName = parts[1];
+
+        verifyAccountNameStrength(accountName);
+        DefaultUser user = new DefaultUser(accountName);
+        user.accountId = accountId;
+
+        String password = parts[2];
+        verifyPasswordStrength(null, password, user);
+        setHashedPassword(user, password);
+
+        String[] roles = parts[3].toLowerCase().split(" *, *");
+        for (String role : roles) {
+            if (!"".equals(role)) {
+                user.addRole(role);
+            }
+        }
+        if (!"unlocked".equalsIgnoreCase(parts[4])) {
+            user.lock();
+        }
+        if ("enabled".equalsIgnoreCase(parts[5])) {
+            user.enable();
+        } else {
+            user.disable();
+        }
+
+        // generate a new csrf token
+        user.resetCSRFToken();
+
+        setOldPasswordHashes(user, Arrays.asList(parts[6].split(" *, *")));
+        user.setLastHostAddress("null".equals(parts[7]) ? null : parts[7]);
+        user.setLastPasswordChangeTime(new Date(Long.parseLong(parts[8])));
+        user.setLastLoginTime(new Date(Long.parseLong(parts[9])));
+        user.setLastFailedLoginTime(new Date(Long.parseLong(parts[10])));
+        user.setExpirationTime(new Date(Long.parseLong(parts[11])));
+        user.setFailedLoginCount(Integer.parseInt(parts[12]));
+        return user;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void removeUser(String accountName) throws AuthenticationException {
+        loadUsersIfNecessary();
+        User user = getUser(accountName);
+        if (user == null) {
+            throw new AuthenticationAccountsException("Remove user failed", "Can't remove invalid accountName " + accountName);
+        }
+        userMap.remove(user.getAccountId());
+        logger.info(Logger.SECURITY_SUCCESS, "Removing user " + user.getAccountName());
+        passwordMap.remove(user);
+        saveUsers();
+    }
+
+    /**
+     * Saves the user database to the file system. In this implementation you must call save to commit any changes to
+     * the user file. Otherwise changes will be lost when the program ends.
+     *
+     * @throws AuthenticationException if the user file could not be written
+     */
+    public synchronized void saveUsers() throws AuthenticationException {
+        PrintWriter writer = null;
+        try {
+            writer = new PrintWriter(new FileWriter(userDB));
+            writer.println("# This is the user file associated with the ESAPI library from http://www.owasp.org");
+            writer.println("# accountId | accountName | hashedPassword | roles | locked | enabled | csrfToken | oldPasswordHashes | lastPasswordChangeTime | lastLoginTime | lastFailedLoginTime | expirationTime | failedLoginCount");
+            writer.println();
+            saveUsers(writer);
+            writer.flush();
+            logger.info(Logger.SECURITY_SUCCESS, "User file written to disk");
+        } catch (IOException e) {
+            logger.fatal(Logger.SECURITY_FAILURE, "Problem saving user file " + userDB.getAbsolutePath(), e);
+            throw new AuthenticationException("Internal Error", "Problem saving user file " + userDB.getAbsolutePath(), e);
+        } finally {
+            if (writer != null) {
+                writer.close();
+                lastModified = userDB.lastModified();
+                lastChecked = lastModified;
+            }
+        }
+    }
+
+    /**
+     * Save users.
+     *
+     * @param writer the print writer to use for saving
+     */
+    protected synchronized void saveUsers(PrintWriter writer) throws AuthenticationCredentialsException {
+        for (Object o : getUserNames()) {
+            String accountName = (String) o;
+            DefaultUser u = (DefaultUser) getUser(accountName);
+            if (u != null && !u.isAnonymous()) {
+                writer.println(save(u));
+            } else {
+                throw new AuthenticationCredentialsException("Problem saving user", "Skipping save of user " + accountName);
+            }
+        }
+    }
+
+    /**
+     * Save.
+     *
+     * @param user the User to save
+     * @return a line containing properly formatted information to save regarding the user
+     */
+    private String save(DefaultUser user) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(user.getAccountId());
+        sb.append(" | ");
+        sb.append(user.getAccountName());
+        sb.append(" | ");
+        sb.append(getHashedPassword(user));
+        sb.append(" | ");
+        sb.append(dump(user.getRoles()));
+        sb.append(" | ");
+        sb.append(user.isLocked() ? "locked" : "unlocked");
+        sb.append(" | ");
+        sb.append(user.isEnabled() ? "enabled" : "disabled");
+        sb.append(" | ");
+        sb.append(dump(getOldPasswordHashes(user)));
+        sb.append(" | ");
+        sb.append(user.getLastHostAddress());
+        sb.append(" | ");
+        sb.append(user.getLastPasswordChangeTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getLastLoginTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getLastFailedLoginTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getExpirationTime().getTime());
+        sb.append(" | ");
+        sb.append(user.getFailedLoginCount());
+        return sb.toString();
+    }
+
+    /**
+     * Dump a collection as a comma-separated list.
+     *
+     * @param c the collection to convert to a comma separated list
+     * @return a comma separated list containing the values in c
+     */
+    private String dump(Collection<String> c) {
+        StringBuilder sb = new StringBuilder();
+        for (String s : c) {
+            sb.append(s).append(",");
+        }
+        if ( c.size() > 0) {
+        	return sb.toString().substring(0, sb.length() - 1);
+        }
+        return "";
+        
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This implementation simply verifies that account names are at least 5 characters long. This helps to defeat a
+     * brute force attack, however the real strength comes from the name length and complexity.
+     *
+     * @param newAccountName
+     */
+    public void verifyAccountNameStrength(String newAccountName) throws AuthenticationException {
+        if (newAccountName == null) {
+            throw new AuthenticationCredentialsException("Invalid account name", "Attempt to create account with a null account name");
+        }
+        if (!ESAPI.validator().isValidInput("verifyAccountNameStrength", newAccountName, "AccountName", MAX_ACCOUNT_NAME_LENGTH, false)) {
+            throw new AuthenticationCredentialsException("Invalid account name", "New account name is not valid: " + newAccountName);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This implementation checks: - for any 3 character substrings of the old password - for use of a length *
+     * character sets > 16 (where character sets are upper, lower, digit, and special
+     * jtm - 11/16/2010 - added check to verify pw != username (fix for http://code.google.com/p/owasp-esapi-java/issues/detail?id=108)
+     */
+    public void verifyPasswordStrength(String oldPassword, String newPassword, User user) throws AuthenticationException {
+        if (newPassword == null) {
+            throw new AuthenticationCredentialsException("Invalid password", "New password cannot be null");
+        }
+
+        // can't change to a password that contains any 3 character substring of old password
+        if (oldPassword != null) {
+            int length = oldPassword.length();
+            for (int i = 0; i < length - 2; i++) {
+                String sub = oldPassword.substring(i, i + 3);
+                if (newPassword.indexOf(sub) > -1) {
+                    throw new AuthenticationCredentialsException("Invalid password", "New password cannot contain pieces of old password");
+                }
+            }
+        }
+
+        // new password must have enough character sets and length
+        int charsets = 0;
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_LOWERS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_UPPERS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_DIGITS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+        for (int i = 0; i < newPassword.length(); i++) {
+            if (Arrays.binarySearch(EncoderConstants.CHAR_SPECIALS, newPassword.charAt(i)) >= 0) {
+                charsets++;
+                break;
+            }
+        }
+
+        // calculate and verify password strength
+        int strength = newPassword.length() * charsets;
+        if (strength < 16) {
+            throw new AuthenticationCredentialsException("Invalid password", "New password is not long and complex enough");
+        }
+        
+        String accountName = user.getAccountName();
+        
+        //jtm - 11/3/2010 - fix for bug http://code.google.com/p/owasp-esapi-java/issues/detail?id=108
+        if (accountName.equalsIgnoreCase(newPassword)) {
+        	//password can't be account name
+        	throw new AuthenticationCredentialsException("Invalid password", "Password matches account name, irrespective of case");
+        }
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/IntegerAccessReferenceMap.java b/src/main/java/org/owasp/esapi/reference/IntegerAccessReferenceMap.java
new file mode 100644
index 0000000..8bb36d6
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/IntegerAccessReferenceMap.java
@@ -0,0 +1,80 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Set;
+
+/**
+ * Reference implementation of the AccessReferenceMap interface. This
+ * implementation generates integers for indirect references.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author Chris Schmidt (chrisisbeef at gmail.com)
+ * @since June 1, 2007
+ * @see org.owasp.esapi.AccessReferenceMap
+ */
+public class IntegerAccessReferenceMap extends AbstractAccessReferenceMap<String> {
+
+	private static final long serialVersionUID = 5311769278372489771L;
+
+	int count = 1;
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap()
+	{
+	}
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap(int initialSize)
+	{
+		super(initialSize);
+	}
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap(Set<Object> directReferences)
+	{
+		super(directReferences.size());
+		update(directReferences);
+	}
+
+	/**
+	 * TODO Javadoc
+	 */
+	public IntegerAccessReferenceMap(Set<Object> directReferences, int initialSize)
+	{
+		super(initialSize);
+		update(directReferences);
+	}
+
+	/**
+	 * TODO Javadoc
+	 * Note: this is final as redefinition by subclasses
+	 * can lead to use before initialization issues as
+	 * {@link #RandomAccessReferenceMap(Set)} and
+	 * {@link #RandomAccessReferenceMap(Set,int)} both call it
+	 * internally.
+	 */
+	protected final synchronized String getUniqueReference() {
+		return "" + count++;  // returns a string version of the counter
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/JavaLogFactory.java b/src/main/java/org/owasp/esapi/reference/JavaLogFactory.java
new file mode 100644
index 0000000..0970818
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/JavaLogFactory.java
@@ -0,0 +1,409 @@
+package org.owasp.esapi.reference;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.logging.Level;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.LogFactory;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+
+/**
+ * Reference implementation of the LogFactory and Logger interfaces. This implementation uses the Java logging package, and marks each
+ * log message with the currently logged in user and the word "SECURITY" for security related events. See the 
+ * <a href="JavaLogFactory.JavaLogger.html">JavaLogFactory.JavaLogger</a> Javadocs for the details on the JavaLogger reference implementation.
+ * 
+ * @author Mike Fauzy (mike.fauzy at aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.LogFactory
+ * @see org.owasp.esapi.reference.JavaLogFactory.JavaLogger
+ */
+public class JavaLogFactory implements LogFactory {
+	private static volatile LogFactory singletonInstance;
+
+    public static LogFactory getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( JavaLogFactory.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new JavaLogFactory();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+	private HashMap<Serializable, Logger> loggersMap = new HashMap<Serializable, Logger>();
+	
+	/**
+	* Null argument constructor for this implementation of the LogFactory interface
+	* needed for dynamic configuration.
+	*/
+	public JavaLogFactory() {}
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public Logger getLogger(Class clazz) {
+    	
+    	// If a logger for this class already exists, we return the same one, otherwise we create a new one.
+    	Logger classLogger = (Logger) loggersMap.get(clazz);
+    	
+    	if (classLogger == null) {
+    		classLogger = new JavaLogger(clazz.getName());
+    		loggersMap.put(clazz, classLogger);
+    	}
+		return classLogger;
+    }
+
+    /**
+	* {@inheritDoc}
+	*/
+    public Logger getLogger(String moduleName) {
+    	
+    	// If a logger for this module already exists, we return the same one, otherwise we create a new one.
+    	Logger moduleLogger = (Logger) loggersMap.get(moduleName);
+    	
+    	if (moduleLogger == null) {
+    		moduleLogger = new JavaLogger(moduleName);
+    		loggersMap.put(moduleName, moduleLogger);    		
+    	}
+		return moduleLogger;
+    }
+
+
+    /**
+     *  A custom logging level defined between Level.SEVERE and Level.WARNING in logger.
+     */
+    public static class JavaLoggerLevel extends Level {
+
+        protected static final long serialVersionUID = 1L;
+
+        /**
+    	 * Defines a custom error level below SEVERE but above WARNING since this level isn't defined directly
+    	 * by java.util.Logger already.
+    	 */
+    	public static final Level ERROR_LEVEL = new JavaLoggerLevel( "ERROR", Level.SEVERE.intValue() - 1);
+    	
+    	/**
+    	 * Constructs an instance of a JavaLoggerLevel which essentially provides a mapping between the name of
+    	 * the defined level and its numeric value.
+    	 * 
+    	 * @param name The name of the JavaLoggerLevel
+    	 * @param value The associated numeric value
+    	 */
+		protected JavaLoggerLevel(String name, int value) {
+			super(name, value);
+		}
+    }
+        
+    /**
+     * Reference implementation of the Logger interface.
+     * 
+     * It implements most of the recommendations defined in the Logger interface description. It does not
+     * filter out any sensitive data specific to the current application or organization, such as credit 
+     * cards, social security numbers, etc.  
+     * 
+     * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+     * @since June 1, 2007
+     * @see org.owasp.esapi.LogFactory
+     */
+    private static class JavaLogger implements org.owasp.esapi.Logger {
+
+    	/** The jlogger object used by this class to log everything. */
+        private java.util.logging.Logger jlogger = null;
+
+        /** The module name using this log. */
+        private String moduleName = null;
+
+        /** The application name defined in ESAPI.properties */
+    	private String applicationName=ESAPI.securityConfiguration().getApplicationName();
+
+        /** Log the application name? */
+    	private static boolean logAppName = ESAPI.securityConfiguration().getLogApplicationName();
+
+    	/** Log the server ip? */
+    	private static boolean logServerIP = ESAPI.securityConfiguration().getLogServerIP();
+    	
+        /**
+         * Public constructor should only ever be called via the appropriate LogFactory
+         * 
+         * @param moduleName the module name
+         */
+        private JavaLogger(String moduleName) {
+            this.moduleName = moduleName;
+            this.jlogger = java.util.logging.Logger.getLogger(applicationName + ":" + moduleName);
+        }
+
+        /**
+         * {@inheritDoc}
+         * Note: In this implementation, this change is not persistent,
+         * meaning that if the application is restarted, the log level will revert to the level defined in the 
+         * ESAPI SecurityConfiguration properties file.
+         */
+        public void setLevel(int level)
+        {
+        	try {
+        		jlogger.setLevel(convertESAPILeveltoLoggerLevel( level ));
+        	}
+        	catch (IllegalArgumentException e) {
+       			this.error(Logger.SECURITY_FAILURE, "", e);    		
+        	}
+         }
+        
+        /**
+         * {@inheritDoc}
+         * @see org.owasp.esapi.reference.Log4JLogger#getESAPILevel()
+         */
+        public int getESAPILevel() {
+        	return jlogger.getLevel().intValue();
+        }
+        
+        /**
+         * Converts the ESAPI logging level (a number) into the levels used by Java's logger.
+         * @param level The ESAPI to convert.
+         * @return The Java logging Level that is equivalent.
+         * @throws IllegalArgumentException if the supplied ESAPI level doesn't make a level that is currently defined.
+         */
+        private static Level convertESAPILeveltoLoggerLevel(int level)
+        {
+        	switch (level) {
+        		case Logger.OFF:     return Level.OFF;
+        		case Logger.FATAL:   return Level.SEVERE;
+        		case Logger.ERROR:   return JavaLoggerLevel.ERROR_LEVEL; // This is a custom level.
+        		case Logger.WARNING: return Level.WARNING;
+        		case Logger.INFO:    return Level.INFO;
+        		case Logger.DEBUG:   return Level.FINE;
+        		case Logger.TRACE:   return Level.FINEST;
+        		case Logger.ALL:     return Level.ALL;       		
+        		default: {
+        			throw new IllegalArgumentException("Invalid logging level. Value was: " + level);
+        		}
+        	}
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void trace(EventType type, String message, Throwable throwable) {
+            log(Level.FINEST, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void trace(EventType type, String message) {
+            log(Level.FINEST, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void debug(EventType type, String message, Throwable throwable) {
+            log(Level.FINE, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void debug(EventType type, String message) {
+            log(Level.FINE, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void info(EventType type, String message) {
+            log(Level.INFO, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void info(EventType type, String message, Throwable throwable) {
+            log(Level.INFO, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void warning(EventType type, String message, Throwable throwable) {
+            log(Level.WARNING, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void warning(EventType type, String message) {
+            log(Level.WARNING, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void error(EventType type, String message, Throwable throwable) {
+            log(Level.SEVERE, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void error(EventType type, String message) {
+            log(Level.SEVERE, type, message, null);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void fatal(EventType type, String message, Throwable throwable) {
+            log(Level.SEVERE, type, message, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public void fatal(EventType type, String message) {
+            log(Level.SEVERE, type, message, null);
+        }
+
+        /**
+         * Log the message after optionally encoding any special characters that might be dangerous when viewed
+         * by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log 
+         * injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging
+         * specific session ID, and the current date/time.
+         * 
+         * It will only log the message if the current logging level is enabled, otherwise it will 
+         * discard the message. 
+         * 
+         * @param level defines the set of recognized logging levels (TRACE, INFO, DEBUG, WARNING, ERROR, FATAL)
+         * @param type the type of the event (SECURITY SUCCESS, SECURITY FAILURE, EVENT SUCCESS, EVENT FAILURE)
+         * @param message the message
+         * @param throwable the throwable
+         */
+        private void log(Level level, EventType type, String message, Throwable throwable) {
+        	
+        	// Check to see if we need to log
+        	if (!jlogger.isLoggable( level )) return;
+
+            // ensure there's something to log
+            if ( message == null ) {
+            	message = "";
+            }
+            
+            // ensure no CRLF injection into logs for forging records
+            String clean = message.replace( '\n', '_' ).replace( '\r', '_' );
+            if ( ESAPI.securityConfiguration().getLogEncodingRequired() ) {
+            	clean = ESAPI.encoder().encodeForHTML(message);
+                if (!message.equals(clean)) {
+                    clean += " (Encoded)";
+                }
+            }
+
+			// log server, port, app name, module name -- server:80/app/module
+			StringBuilder appInfo = new StringBuilder();
+			if ( ESAPI.currentRequest() != null && logServerIP ) {
+				appInfo.append( ESAPI.currentRequest().getLocalAddr() + ":" + ESAPI.currentRequest().getLocalPort() );
+			}
+			if ( logAppName ) {
+				appInfo.append( "/" + applicationName );
+			}
+			appInfo.append( "/"  + moduleName );
+			
+			//get the type text if it exists
+			String typeInfo = "";
+			if (type != null) {
+				typeInfo += type + " ";
+			}
+			
+			// log the message
+			jlogger.log(level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isDebugEnabled() {
+    	    return jlogger.isLoggable(Level.FINE);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isErrorEnabled() {
+    	    return jlogger.isLoggable(JavaLoggerLevel.ERROR_LEVEL);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isFatalEnabled() {
+    	    return jlogger.isLoggable(Level.SEVERE);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isInfoEnabled() {
+    	    return jlogger.isLoggable(Level.INFO);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isTraceEnabled() {
+    	    return jlogger.isLoggable(Level.FINEST);
+        }
+
+        /**
+    	* {@inheritDoc}
+    	*/
+        public boolean isWarningEnabled() {
+    	    return jlogger.isLoggable(Level.WARNING);
+        }
+        
+        public String getUserInfo() {
+            // create a random session number for the user to represent the user's 'session', if it doesn't exist already
+            String sid = null;
+            HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+            if ( request != null ) {
+                HttpSession session = request.getSession( false );
+                if ( session != null ) {
+	                sid = (String)session.getAttribute("ESAPI_SESSION");
+	                // if there is no session ID for the user yet, we create one and store it in the user's session
+		            if ( sid == null ) {
+		            	sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000);
+		            	session.setAttribute("ESAPI_SESSION", sid);
+		            }
+                }
+            }
+            
+			// log user information - username:session at ipaddr
+			User user = ESAPI.authenticator().getCurrentUser();            
+			String userInfo = "";
+			//TODO - Make Type Logging configurable
+			if ( user != null) {
+				userInfo += user.getAccountName()+ ":" + sid + "@"+ user.getLastHostAddress();
+			}
+			
+			return userInfo;
+        }
+
+    	/**
+    	 * {@inheritDoc}
+    	 */
+		public void always(EventType type, String message) {
+            always(type, message, null);	
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public void always(EventType type, String message, Throwable throwable) {
+            log(Level.OFF, type, message, throwable);  // Seems backward, but this is what works, not Level.ALL	
+		}
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/Log4JLogFactory.java b/src/main/java/org/owasp/esapi/reference/Log4JLogFactory.java
new file mode 100644
index 0000000..cab4d5d
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/Log4JLogFactory.java
@@ -0,0 +1,91 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.HashMap;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.spi.LoggerFactory;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.LogFactory;
+import org.owasp.esapi.Logger;
+
+/**
+ * Reference implementation of the LogFactory interface. This implementation uses the Apache Log4J package, and marks each
+ * log message with the currently logged in user and the word "SECURITY" for security related events. See the 
+ * <a href="JavaLogFactory.JavaLogger.html">JavaLogFactory.JavaLogger</a> Javadocs for the details on the JavaLogger reference implementation.
+ * 
+ * At class initialization time, the file log4j.properties or log4j.xml file will be loaded from the classpath. This configuration file is 
+ * fundamental to make log4j work for you. Please see http://logging.apache.org/log4j/1.2/manual.html for more information.
+ *
+ * Note that <b>you must specify the LogFactory</b> in either log4j.properties:
+ *
+ * <code>
+ *     log4j.loggerFactory=org.owasp.esapi.reference.Log4JLoggerFactory
+ * </code>
+ *
+ * or log4j.xml:
+ * 
+ * <code>
+ *     <loggerFactory class="org.owasp.esapi.reference.Log4JLoggerFactory"/>
+
+ * </code>
+ * 
+ * @author Mike H. Fauzy (mike.fauzy at aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jim Manico (jim at manico.net) <a href="http://www.manico.net">Manico.net</a>
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author August Detlefsen (augustd at codemagi dot com) <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.LogFactory
+ * @see org.owasp.esapi.reference.Log4JLogger
+ * @see org.owasp.esapi.reference.Log4JLoggerFactory
+ */
+public class Log4JLogFactory implements LogFactory {
+
+	private static volatile LogFactory singletonInstance;
+
+	//The Log4j logger factory to use
+	LoggerFactory factory = new Log4JLoggerFactory();
+
+    public static LogFactory getInstance() {
+        if ( singletonInstance == null ) {
+            synchronized ( Log4JLogFactory.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new Log4JLogFactory();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+	
+	protected Log4JLogFactory() {}
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public org.owasp.esapi.Logger getLogger(Class clazz) {
+		return (org.owasp.esapi.Logger)LogManager.getLogger(clazz.getName(), factory);
+    }
+
+    /**
+	* {@inheritDoc}
+	*/
+	public org.owasp.esapi.Logger getLogger(String moduleName) {
+		return (org.owasp.esapi.Logger)LogManager.getLogger(moduleName, factory);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/Log4JLogger.java b/src/main/java/org/owasp/esapi/reference/Log4JLogger.java
new file mode 100644
index 0000000..3b4aa95
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/Log4JLogger.java
@@ -0,0 +1,524 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggerFactory;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.User;
+
+/**
+ * Reference implementation of the Logger interface. This implementation extends org.apache.log4j.Logger
+ * in order to take advantage of per-class and per-package configuration options provided by Log4J. 
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLogFactory
+ * @see org.owasp.esapi.reference.Log4JLoggerFactory
+ */
+public class Log4JLogger extends org.apache.log4j.Logger implements org.owasp.esapi.Logger {
+
+	/** The log factory to use in creating new instances. */
+	private static LoggerFactory factory = new Log4JLoggerFactory();
+
+	/** The application name using this log */
+	private static String applicationName = ESAPI.securityConfiguration().getApplicationName();
+
+	/** Log the application name? */
+	private static boolean logAppName = ESAPI.securityConfiguration().getLogApplicationName();
+	
+	/** Log the server ip? */
+	private static boolean logServerIP = ESAPI.securityConfiguration().getLogServerIP();
+
+	public Log4JLogger(String name) {
+		super(name);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getInstance} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static Category getInstance(String name) {
+		return LogManager.getLogger(name, factory);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getInstance} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static Category getInstance(Class clazz) {
+		return LogManager.getLogger(clazz.getName(), factory);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getLogger} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static Logger getLogger(String name) {
+		return LogManager.getLogger(name, factory);
+	}
+
+	/**
+	 * This method overrides {@link Logger#getLogger} by supplying
+	 * its own factory type as a parameter.
+	 */
+	public static org.apache.log4j.Logger getLogger(Class clazz) {
+		return LogManager.getLogger(clazz.getName(), factory);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * Note: In this implementation, this change is not persistent,
+	 * meaning that if the application is restarted, the log level will revert to the level defined in the
+	 * ESAPI SecurityConfiguration properties file.
+	 */
+	public void setLevel(int level) {
+		try {
+			super.setLevel(convertESAPILeveltoLoggerLevel(level));
+		} catch (IllegalArgumentException e) {
+			this.error(org.owasp.esapi.Logger.SECURITY_FAILURE, "", e);
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * Explanation: Since this class extends Log4j's Logger class which has a
+	 * {@code getLevel()} method that returns {@code extended by org.apache.log4j.Level},
+	 * we can't simply have a {@code getLevel()} that simply returns an {@code int}.
+	 * Hence we renamed it to {@code getESAPILevel()}.
+	 */
+	public int getESAPILevel() {
+		return super.getLevel().toInt();
+	}
+
+	/**
+	 * Converts the ESAPI logging level (a number) into the levels used by Java's logger.
+	 * @param level The ESAPI to convert.
+	 * @return The Log4J logging Level that is equivalent.
+	 * @throws IllegalArgumentException if the supplied ESAPI level doesn't make a level that is currently defined.
+	 */
+	private static Level convertESAPILeveltoLoggerLevel(int level) {
+		switch (level) {
+		case org.owasp.esapi.Logger.OFF:
+			return Level.OFF;
+		case org.owasp.esapi.Logger.FATAL:
+			return Level.FATAL;
+		case org.owasp.esapi.Logger.ERROR:
+			return Level.ERROR;
+		case org.owasp.esapi.Logger.WARNING:
+			return Level.WARN;
+		case org.owasp.esapi.Logger.INFO:
+			return Level.INFO;
+		case org.owasp.esapi.Logger.DEBUG:
+			return Level.DEBUG; //fine
+		case org.owasp.esapi.Logger.TRACE:
+			return Level.TRACE; //finest
+		case org.owasp.esapi.Logger.ALL:
+			return Level.ALL;
+		default: {
+			throw new IllegalArgumentException("Invalid logging level. Value was: " + level);
+		}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void always(EventType type, String message, Throwable throwable) {
+		log(Level.OFF, type, message, throwable);	// Seems like Level.ALL is what we
+													// would want here, but this is what
+													// works. Level.ALL does not.
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void always(EventType type, String message) {
+		always(type, message, null);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void trace(EventType type, String message, Throwable throwable) {
+		log(Level.TRACE, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void trace(EventType type, String message) {
+		log(Level.TRACE, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void debug(EventType type, String message, Throwable throwable) {
+		log(Level.DEBUG, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void debug(EventType type, String message) {
+		log(Level.DEBUG, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void info(EventType type, String message) {
+		log(Level.INFO, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void info(EventType type, String message, Throwable throwable) {
+		log(Level.INFO, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void warning(EventType type, String message, Throwable throwable) {
+		log(Level.WARN, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void warning(EventType type, String message) {
+		log(Level.WARN, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void error(EventType type, String message, Throwable throwable) {
+		log(Level.ERROR, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void error(EventType type, String message) {
+		log(Level.ERROR, type, message, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void fatal(EventType type, String message, Throwable throwable) {
+		log(Level.FATAL, type, message, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void fatal(EventType type, String message) {
+		log(Level.FATAL, type, message, null);
+	}
+
+	/**
+	 * Always log the specified message as a {@code SECURITY_AUDIT} event type.
+	 * 
+	 * @param message	The {@code String} representation of the specified message as
+	 * 					logged by calling the object's {@code toString()} method.
+	 */
+	public void always(Object message) {
+		this.always(message, null);
+	}
+
+	/**
+	 * Always log the specified message as a {@code SECURITY_AUDIT} event type, along
+	 * with its associated exception stack trace (if any).
+	 * 
+	 * @param message	The {@code String} representation of the specified message as
+	 * 					logged by calling the object's {@code toString()} method.
+	 */
+	public void always(Object message, Throwable throwable) {
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+		this.always(org.owasp.esapi.Logger.SECURITY_AUDIT, toLog, throwable);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void trace(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.trace(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void trace(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.trace(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void debug(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.debug(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void debug(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.debug(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void info(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.info(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void info(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.info(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void warn(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.warning(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void warn(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.warning(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void error(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.error(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void error(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.error(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fatal(Object message) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.fatal(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void fatal(Object message, Throwable throwable) {
+
+		String toLog = (message instanceof String) ? (String) message : message.toString();
+
+		this.fatal(org.owasp.esapi.Logger.EVENT_UNSPECIFIED, toLog, throwable);
+	}
+
+	/**
+	 * Log the message after optionally encoding any special characters that might be dangerous when viewed
+	 * by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log
+	 * injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging
+	 * specific session ID, and the current date/time.
+	 *
+	 * It will only log the message if the current logging level is enabled, otherwise it will
+	 * discard the message.
+	 *
+	 * @param level defines the set of recognized logging levels (TRACE, INFO, DEBUG, WARNING, ERROR, FATAL)
+	 * @param type the type of the event (SECURITY SUCCESS, SECURITY FAILURE, EVENT SUCCESS, EVENT FAILURE)
+	 * @param message the message to be logged
+	 * @param throwable the {@code Throwable} from which to generate an exception stack trace.
+	 */
+	private void log(Level level, EventType type, String message, Throwable throwable) {
+
+		// Check to see if we need to log.
+		if (!isEnabledFor(level)) {
+			return;
+		}
+
+		// ensure there's something to log
+		if (message == null) {
+			message = "";
+		}
+
+		// ensure no CRLF injection into logs for forging records
+		String clean = message.replace('\n', '_').replace('\r', '_');
+		if (ESAPI.securityConfiguration().getLogEncodingRequired()) {
+			clean = ESAPI.encoder().encodeForHTML(message);
+			if (!message.equals(clean)) {
+				clean += " (Encoded)";
+			}
+		}
+
+		// log server, port, app name, module name -- server:80/app/module
+		StringBuilder appInfo = new StringBuilder();
+		if (ESAPI.currentRequest() != null && logServerIP) {
+			appInfo.append(ESAPI.currentRequest().getLocalAddr()).append(":").append(ESAPI.currentRequest().getLocalPort());
+		}
+		if (logAppName) {
+			appInfo.append("/").append(applicationName);
+		}
+		appInfo.append("/").append(getName());
+
+		//get the type text if it exists
+		String typeInfo = "";
+		if (type != null) {
+			typeInfo += type + " ";
+		}
+
+		// log the message
+		log(level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isDebugEnabled() {
+		return isEnabledFor(Level.DEBUG);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isErrorEnabled() {
+		return isEnabledFor(Level.ERROR);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isFatalEnabled() {
+		return isEnabledFor(Level.FATAL);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isInfoEnabled() {
+		return isEnabledFor(Level.INFO);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isTraceEnabled() {
+		return isEnabledFor(Level.TRACE);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isWarningEnabled() {
+		return isEnabledFor(Level.WARN);
+	}
+
+	public String getUserInfo() {
+		// create a random session number for the user to represent the user's 'session', if it doesn't exist already
+		String sid = null;
+		HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest();
+		if (request != null) {
+			HttpSession session = request.getSession(false);
+			if (session != null) {
+				sid = (String) session.getAttribute("ESAPI_SESSION");
+				// if there is no session ID for the user yet, we create one and store it in the user's session
+				if (sid == null) {
+					sid = "" + ESAPI.randomizer().getRandomInteger(0, 1000000);
+					session.setAttribute("ESAPI_SESSION", sid);
+				}
+			}
+		}
+
+		// log user information - username:session at ipaddr
+		User user = ESAPI.authenticator().getCurrentUser();
+		String userInfo = "";
+		//TODO - make type logging configurable
+		if (user != null) {
+			userInfo += user.getAccountName() + ":" + sid + "@" + user.getLastHostAddress();
+		}
+
+		return userInfo;
+	}
+	
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/Log4JLoggerFactory.java b/src/main/java/org/owasp/esapi/reference/Log4JLoggerFactory.java
new file mode 100644
index 0000000..f5a6713
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/Log4JLoggerFactory.java
@@ -0,0 +1,47 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.apache.log4j.spi.LoggerFactory;
+
+/**
+ * Implementation of the LoggerFactory interface. This implementation has been 
+ * overridden to return instances of org.owasp.esapi.reference.Log4JLogger.
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLogFactory
+ * @see org.owasp.esapi.reference.Log4JLogger
+ */
+public class Log4JLoggerFactory implements LoggerFactory {
+
+	/**
+	 * This constructor must be public so it can be accessed from within log4j
+	 */
+	public Log4JLoggerFactory() {}
+
+	/**
+	 * Overridden to return instances of org.owasp.esapi.reference.Log4JLogger.
+	 * 
+	 * @param name The class name to return a logger for.
+	 * @return org.owasp.esapi.reference.Log4JLogger
+	 */
+	public org.apache.log4j.Logger makeNewLoggerInstance(String name) {
+		return new Log4JLogger(name);
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/RandomAccessReferenceMap.java b/src/main/java/org/owasp/esapi/reference/RandomAccessReferenceMap.java
new file mode 100644
index 0000000..28c3cb8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/RandomAccessReferenceMap.java
@@ -0,0 +1,85 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+
+import java.util.Set;
+
+/**
+ * Reference implementation of the AccessReferenceMap interface. This
+ * implementation generates random 6 character alphanumeric strings for indirect
+ * references. It is possible to use simple integers as indirect references, but
+ * the random string approach provides a certain level of protection from CSRF
+ * attacks, because an attacker would have difficulty guessing the indirect
+ * reference.
+ *
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author Chris Schmidt (chrisisbeef at gmail.com)
+ * @see org.owasp.esapi.AccessReferenceMap
+ * @since June 1, 2007
+ */
+public class RandomAccessReferenceMap extends AbstractAccessReferenceMap<String>
+{
+
+   private static final long serialVersionUID = 8544133840739803001L;
+
+   public RandomAccessReferenceMap(int initialSize)
+   {
+      super(initialSize);
+   }
+
+   /**
+    * This AccessReferenceMap implementation uses short random strings to
+    * create a layer of indirection. Other possible implementations would use
+    * simple integers as indirect references.
+    */
+   public RandomAccessReferenceMap()
+   {
+      // call update to set up the references
+   }
+
+   public RandomAccessReferenceMap(Set<Object> directReferences)
+   {
+      super(directReferences.size());
+      update(directReferences);
+   }
+
+   public RandomAccessReferenceMap(Set<Object> directReferences, int initialSize)
+   {
+      super(initialSize);
+      update(directReferences);
+   }
+
+   /**
+    * {@inheritDoc}
+    * Note: this is final as redefinition by subclasses can lead to use
+    * before initialization issues as
+    * {@link #RandomAccessReferenceMap(Set)} and
+    * {@link #RandomAccessReferenceMap(Set,int)} both call it internally.
+    */
+   protected final synchronized String getUniqueReference()
+   {
+      String candidate;
+      do
+      {
+         candidate = ESAPI.randomizer().getRandomString(6, EncoderConstants.CHAR_ALPHANUMERICS);
+      }
+      while (itod.keySet().contains(candidate));
+      return candidate;
+   }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/all-wcprops b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/all-wcprops
new file mode 100644
index 0000000..e651d73
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/all-wcprops
@@ -0,0 +1,53 @@
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol
+END
+AlwaysTrueACR.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysTrueACR.java
+END
+FileBasedACRs.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/FileBasedACRs.java
+END
+DynaBeanACRParameter.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java
+END
+EchoRuntimeParameterACR.java
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/EchoRuntimeParameterACR.java
+END
+DelegatingACR.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java
+END
+BaseACR.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/BaseACR.java
+END
+AlwaysFalseACR.java
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysFalseACR.java
+END
+ExperimentalAccessController.java
+K 25
+svn:wc:ra_dav:version-url
+V 123
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/ExperimentalAccessController.java
+END
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/entries b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/entries
new file mode 100644
index 0000000..037afda
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/entries
@@ -0,0 +1,303 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-11-05T04:21:56.553937Z
+1646
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+BaseACR.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+1a6025c7e3387bf7db4d0b2c1438ba44
+2009-04-17T06:16:02.118921Z
+464
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+412
+

+AlwaysFalseACR.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+69d8f3516cd93c52a19414ec4e0153cb
+2009-04-08T22:18:24.697030Z
+457
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+212
+

+ExperimentalAccessController.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+3f388467e51439c46284e35b9773986d
+2009-11-09T04:39:12.162255Z
+761
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6694
+

+policyloader
+dir
+

+AlwaysTrueACR.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+e531b00244cbdd27a5154f1491f7e033
+2009-04-08T22:18:24.697030Z
+457
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+211
+

+FileBasedACRs.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+fb0b5611eac41032d1bee1559fcc855f
+2009-12-22T18:35:13.133576Z
+918
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+19175
+

+DynaBeanACRParameter.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+17f55064af4bea416a7ad427c2746f67
+2009-07-06T03:21:40.869182Z
+561
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4735
+

+EchoRuntimeParameterACR.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+161e812ff1ef73329cdb65f144f470fe
+2010-11-05T04:21:56.553937Z
+1646
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+398
+

+DelegatingACR.java
+file
+
+
+
+
+2014-02-18T16:19:52.897965Z
+2a05bfa4ca7d70c8ac89b4fe911d6cee
+2009-04-17T20:10:12.050344Z
+466
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3816
+

diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AlwaysFalseACR.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AlwaysFalseACR.java.svn-base
new file mode 100644
index 0000000..9d3863d
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AlwaysFalseACR.java.svn-base
@@ -0,0 +1,9 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+public class AlwaysFalseACR extends BaseACR<Object, Object> {
+
+//	@Override
+	public boolean isAuthorized(Object runtimeParameter) {
+		return false;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AlwaysTrueACR.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AlwaysTrueACR.java.svn-base
new file mode 100644
index 0000000..d7cab33
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AlwaysTrueACR.java.svn-base
@@ -0,0 +1,9 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+public class AlwaysTrueACR extends BaseACR<Object, Object> {
+	
+//	@Override
+	public boolean isAuthorized(Object runtimeParameter) {
+		return true;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/BaseACR.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/BaseACR.java.svn-base
new file mode 100644
index 0000000..f2221e0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/BaseACR.java.svn-base
@@ -0,0 +1,18 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import org.owasp.esapi.AccessControlRule;
+
+abstract public class BaseACR<P, R> implements AccessControlRule<P, R> {
+
+	protected P policyParameters;
+	
+//	@Override
+	public void setPolicyParameters(P policyParameter) {
+		this.policyParameters = policyParameter;
+	}
+	
+//	@Override
+	public P getPolicyParameters() {
+		return policyParameters;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/DelegatingACR.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/DelegatingACR.java.svn-base
new file mode 100644
index 0000000..193f91e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/DelegatingACR.java.svn-base
@@ -0,0 +1,99 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.commons.collections.iterators.ArrayListIterator;
+
+public class DelegatingACR extends BaseACR<DynaBeanACRParameter, Object[]> {
+	protected Method delegateMethod;
+	protected Object delegateInstance;
+	
+	@Override
+	public void setPolicyParameters(DynaBeanACRParameter policyParameter) {
+		String delegateClassName = policyParameter.getString("delegateClass", "").trim();
+		String methodName = policyParameter.getString("delegateMethod", "").trim();
+		String[] parameterClassNames = policyParameter.getStringArray("parameterClasses");
+
+		//Convert the classNames into Classes and get the delegate method.
+		Class delegateClass = getClass(delegateClassName, "delegate");
+		Class parameterClasses[] = getParameters(parameterClassNames);
+		try {
+			this.delegateMethod = delegateClass.getMethod(methodName, parameterClasses);
+		} catch (SecurityException e) {
+			throw new IllegalArgumentException(e.getMessage() + 
+					" delegateClass.delegateMethod(parameterClasses): \"" +  
+					delegateClassName + "." + methodName + "(" + parameterClassNames +
+					")\" must be public.", e);
+		} catch (NoSuchMethodException e) {
+			throw new IllegalArgumentException(e.getMessage() + 
+					" delegateClass.delegateMethod(parameterClasses): \"" +  
+					delegateClassName + "." + methodName + "(" + parameterClassNames +
+					")\" does not exist.", e);
+		}
+	
+		//static methods do not need a delegateInstance. Non-static methods do.
+		if(!Modifier.isStatic(this.delegateMethod.getModifiers())) {
+			try {
+				this.delegateInstance = delegateClass.newInstance();
+			} catch (InstantiationException ex) {
+				throw new IllegalArgumentException( 
+						" Delegate class \"" + delegateClassName + 
+						"\" must be concrete, because method " +
+						delegateClassName + "." + methodName + "(" + parameterClassNames +
+						") is not static.", ex);
+			} catch (IllegalAccessException ex) {
+				new IllegalArgumentException( 
+						" Delegate class \"" + delegateClassName + 
+						"\" must must have a zero-argument constructor, because " +
+						"method delegateClass.delegateMethod(parameterClasses): \"" +  
+						delegateClassName + "." + methodName + "(" + parameterClassNames +
+						")\" is not static.", ex);
+			}	
+		} else {
+			this.delegateInstance = null;
+		}
+	}
+	/**
+	 * Convert an array of fully qualified class names into an array of Class objects
+	 * @param parameterClassNames
+	 * @return
+	 */
+	protected final Class[] getParameters(String[] parameterClassNames) {
+		if(parameterClassNames == null) {
+			return new Class[0];
+		}
+		Vector<Class> classes = new Vector<Class>();
+		Iterator<String> classNames = new ArrayListIterator(parameterClassNames);
+		while(classNames.hasNext()) {
+			classes.add(getClass(classNames.next(), "parameter"));
+		}
+		return classes.toArray(new Class[classes.size()]);
+	}
+	/**
+	 * Convert a single fully qualified class name into a Class object
+	 * @param className
+	 * @param purpose
+	 * @return
+	 */
+	protected final Class getClass(String className, String purpose) {
+		try {
+	        Class theClass = Class.forName(className);
+	        return theClass;
+	    } catch ( ClassNotFoundException ex ) {
+			throw new IllegalArgumentException(ex.getMessage() + 
+					" " + purpose + " Class " + className + 
+					" must be in the classpath", ex);
+	    } 
+	}
+	/**
+	 * Delegates to the method specified in setPolicyParameters
+	 */
+	public boolean isAuthorized(Object[] runtimeParameters) throws Exception {
+		return ((Boolean)delegateMethod.invoke(delegateInstance, runtimeParameters)).booleanValue();
+	}
+}
+
+
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/DynaBeanACRParameter.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/DynaBeanACRParameter.java.svn-base
new file mode 100644
index 0000000..7c4879e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/DynaBeanACRParameter.java.svn-base
@@ -0,0 +1,186 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.apache.commons.beanutils.*;
+import org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters;
+
+/**
+ * A DynaBean comes from the apache bean utils. It is basically a 
+ * convenient way to dynamically assign getters and setters. Essentially, 
+ * the way we use DynaBean is a HashMap that can be set to read only.
+ * @author Mike H. Fauzy
+ */
+public class DynaBeanACRParameter implements PolicyParameters {
+	protected LazyDynaMap policyProperties;
+	
+	public DynaBeanACRParameter() {
+		policyProperties = new LazyDynaMap();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters#get(java.lang.String)
+	 */
+	public Object get(String key) {
+		return policyProperties.get(key);
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public boolean getBoolean(String key) {
+		return ((Boolean)get(key)).booleanValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public byte getByte(String key) {
+		return ((Byte)get(key)).byteValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public char getChar(String key) {
+		return ((Character)get(key)).charValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public int getInt(String key) {
+		return ((Integer)get(key)).intValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public long getLong(String key) {
+		return ((Long)get(key)).longValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public float getFloat(String key) {
+		return ((Float)get(key)).floatValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public double getDouble(String key) {
+		return ((Double)get(key)).doubleValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public BigDecimal getBigDecimal(String key) {
+		return (BigDecimal)get(key);
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public BigInteger getBigInteger(String key) {
+		return (BigInteger)get(key);
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public Date getDate(String key) {
+		return (Date)get(key);
+	}
+	
+	/**
+	 * Convenience method to avoid common casts. Note that the time object
+	 * is the same as a date object
+	 * @param key
+	 * @return
+	 */
+	public Date getTime(String key) {
+		return (Date)get(key);
+	}
+	
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public String getString(String key) {
+		return (String)get(key);
+	}
+	
+	public String getString(String key, String defaultValue) {
+		return (String)get(key) == null ? defaultValue : (String)get(key);
+	}
+	
+	public String[] getStringArray(String key) {
+		return (String[])get(key);
+	}
+	
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public Object getObject(String key) {
+		return get(key);
+	}	
+
+	/* (non-Javadoc)
+	 * @see org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters#set(java.lang.String, java.lang.Object)
+	 */
+	public void set(String key, Object value) throws IllegalArgumentException {
+		policyProperties.set(key, value);
+	}
+	/* (non-Javadoc)
+	 * @see org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters#put(java.lang.String, java.lang.Object)
+	 */
+	public void put(String key, Object value) throws IllegalArgumentException {
+		set(key, value);
+	}
+	
+	/**
+	 * This makes the map itself read only, but the mutability of objects 
+	 * that this map contains is not affected. Specifically, properties 
+	 * cannot be added or removed and the reference cannot be changed to 
+	 * a different object, but this does not change whether the values that the 
+	 * object contains can be changed. 
+	 */
+	public void lock() {
+		policyProperties.setRestricted(true);
+	}
+	
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		Iterator keys = policyProperties.getMap().keySet().iterator();
+		String currentKey;
+		while(keys.hasNext()) {
+			currentKey = (String)keys.next();
+			sb.append(currentKey);
+			sb.append("=");
+			sb.append(policyProperties.get(currentKey));
+			if(keys.hasNext()) {
+				sb.append(",");
+			}
+		}
+		return sb.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/EchoRuntimeParameterACR.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/EchoRuntimeParameterACR.java.svn-base
new file mode 100644
index 0000000..bbc8168
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/EchoRuntimeParameterACR.java.svn-base
@@ -0,0 +1,13 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+public class EchoRuntimeParameterACR extends BaseACR<Object, Boolean>{
+
+	/**
+	 * Returns true iff runtimeParameter is a Boolean true.
+	 * throws ClassCastException if runtimeParameter is not a Boolean.
+	 */
+	public boolean isAuthorized(Boolean runtimeParameter) throws ClassCastException{
+		return runtimeParameter.booleanValue();
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/ExperimentalAccessController.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/ExperimentalAccessController.java.svn-base
new file mode 100644
index 0000000..f97b226
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/ExperimentalAccessController.java.svn-base
@@ -0,0 +1,184 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.util.Map;
+
+import org.owasp.esapi.AccessControlRule;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.reference.accesscontrol.policyloader.ACRPolicyFileLoader;
+import org.owasp.esapi.reference.accesscontrol.policyloader.PolicyDTO;
+
+public class ExperimentalAccessController implements AccessController {
+	private Map ruleMap;
+
+	protected final Logger logger = ESAPI.getLogger("DefaultAccessController");
+	
+	public ExperimentalAccessController(Map ruleMap) {
+		this.ruleMap = ruleMap;	
+	}
+	public ExperimentalAccessController() throws AccessControlException {
+		ACRPolicyFileLoader policyDescriptor = new ACRPolicyFileLoader();
+		PolicyDTO policyDTO = policyDescriptor.load();		
+		ruleMap = policyDTO.getAccessControlRules();
+	}
+	
+	public boolean isAuthorized(Object key, Object runtimeParameter) {
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied",
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Evaluating Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			return rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			try {
+				//Log the exception by throwing and then catching it.
+				//TODO figure out what which string goes where.		
+				throw new AccessControlException("Access Denied",
+					"An unhandled Exception was " +
+					"caught, so access is denied.",  
+					e);	
+			} catch(AccessControlException ace) {
+				//the exception was just logged. There's nothing left to do.
+			}
+			return false; //fail closed
+		}
+	}
+
+	public void assertAuthorized(Object key, Object runtimeParameter)
+		throws AccessControlException {
+		boolean isAuthorized = false;
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied", 
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Asserting Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			isAuthorized = rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			//TODO figure out what which string goes where.		
+			throw new AccessControlException("Access Denied", "An unhandled Exception was " +
+					"caught, so access is denied." +
+					"AccessControlException.",
+					e);
+		}
+		if(!isAuthorized) {
+			throw new AccessControlException("Access Denied", 
+					"Access Denied for key: " + key + 
+					" runtimeParameter: " + runtimeParameter);
+		}
+	}
+	
+	/**** Below this line is legacy support ****/
+	
+	/**
+	 * @param action
+	 * @param data
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForData(java.lang.String, java.lang.Object)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForData(String action, Object data)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+	/**
+	 * @param filepath
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForFile(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForFile(String filepath)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+	/**
+	 * @param functionName
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForFunction(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForFunction(String functionName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+	/**
+	 * @param serviceName
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForService(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForService(String serviceName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+	/**
+	 * @param url
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForURL(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForURL(String url)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+
+	/**
+	 * @param action
+	 * @param data
+	 * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForData(java.lang.String, java.lang.Object)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForData(String action, Object data) {
+		return this.isAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+	/**
+	 * @param filepath
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForFile(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForFile(String filepath) {
+		return this.isAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+	/**
+	 * @param functionName
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForFunction(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForFunction(String functionName) {
+		return this.isAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+	/**
+	 * @param serviceName
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForService(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForService(String serviceName) {
+		return this.isAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+	/**
+	 * @param url
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForURL(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForURL(String url) {
+		return this.isAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/FileBasedACRs.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/FileBasedACRs.java.svn-base
new file mode 100644
index 0000000..c788b87
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/FileBasedACRs.java.svn-base
@@ -0,0 +1,559 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Mike Fauzy <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.IntrusionException;
+
+// CHECKME: If this exists for backward compatibility, should this
+//          class be deprecated??? If so, mark it using annotation.
+/**
+ * This class exists for backwards compatibility with the AccessController 1.0 
+ * reference implementation.
+ *
+ * This reference implementation uses a simple model for specifying a set of
+ * access control rules. Many organizations will want to create their own
+ * implementation of the methods provided in the AccessController interface.
+ * <P>
+ * This reference implementation uses a simple scheme for specifying the rules.
+ * The first step is to create a namespace for the resources being accessed. For
+ * files and URL's, this is easy as they already have a namespace. Be extremely
+ * careful about canonicalizing when relying on information from the user in an
+ * access control decision.
+ * <P>
+ * For functions, data, and services, you will have to come up with your own
+ * namespace for the resources being accessed. You might simply define a flat
+ * namespace with a list of category names. For example, you might specify
+ * 'FunctionA', 'FunctionB', and 'FunctionC'. Or you can create a richer
+ * namespace with a hierarchical structure, such as:
+ * <P>
+ * /functions
+ * <ul>
+ * <li>purchasing</li>
+ * <li>shipping</li>
+ * <li>inventory</li>
+ * </ul>
+ * /admin
+ * <ul>
+ * <li>createUser</li>
+ * <li>deleteUser</li>
+ * </ul>
+ * Once you've defined your namespace, you have to work out the rules that
+ * govern access to the different parts of the namespace. This implementation
+ * allows you to attach a simple access control list (ACL) to any part of the
+ * namespace tree. The ACL lists a set of roles that are either allowed or
+ * denied access to a part of the tree. You specify these rules in a textfile
+ * with a simple format.
+ * <P>
+ * There is a single configuration file supporting each of the five methods in
+ * the AccessController interface. These files are located in the ESAPI
+ * resources directory as specified when the JVM was started. The use of a
+ * default deny rule is STRONGLY recommended. The file format is as follows:
+ * 
+ * <pre>
+ * path          | role,role   | allow/deny | comment
+ * ------------------------------------------------------------------------------------
+ * /banking/*    | user,admin  | allow      | authenticated users can access /banking
+ * /admin        | admin       | allow      | only admin role can access /admin
+ * /             | any         | deny       | default deny rule
+ * </pre>
+ * 
+ * To find the matching rules, this implementation follows the general approach
+ * used in Java EE when matching HTTP requests to servlets in web.xml. The four
+ * mapping rules are used in the following order:
+ * <ul>
+ * <li>exact match, e.g. /access/login</li>
+ * <li>longest path prefix match, beginning / and ending /*, e.g. /access/* or
+ * /*</li>
+ * <li>extension match, beginning *., e.g. *.css</li>
+ * <li>default rule, specified by the single character pattern /</li>
+ * </ul>
+ * 
+ * @author Mike Fauzy (mike.fauzy at aspectsecurity.com)
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @since June 1, 2007
+ */
+public class FileBasedACRs {
+
+	/** The url map. */
+	private Map urlMap = new HashMap();
+
+	/** The function map. */
+	private Map functionMap = new HashMap();
+
+	/** The data map. */
+	private Map dataMap = new HashMap();
+
+	/** The file map. */
+	private Map fileMap = new HashMap();
+
+	/** The service map. */
+	private Map serviceMap = new HashMap();
+
+	/** A rule containing "deny". */
+	private Rule deny = new Rule();
+
+	/** The logger. */
+	private Logger logger = ESAPI.getLogger("FileBasedACRs");
+
+    /**
+	* Check if URL is authorized.
+	* @param url The URL tested for authorization
+	* @return {@code true} if access is allowed, {@code false} otherwise.
+	*/
+    public boolean isAuthorizedForURL(String url) {
+		if (urlMap==null || urlMap.isEmpty()) {
+			urlMap = loadRules("URLAccessRules.txt");
+		}
+		return matchRule(urlMap, url);
+	}
+
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForFunction(String functionName) throws AccessControlException {
+    	if (functionMap==null || functionMap.isEmpty()) {
+			functionMap = loadRules("FunctionAccessRules.txt");
+		}
+		return matchRule(functionMap, functionName);
+	}
+  
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForData(String action, Object data) throws AccessControlException{
+    	if (dataMap==null || dataMap.isEmpty()) {
+			dataMap = loadDataRules("DataAccessRules.txt");
+    	}		
+    	return matchRule(dataMap, (Class)data, action);    	
+    }
+    
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForFile(String filepath) throws AccessControlException {
+		if (fileMap==null || fileMap.isEmpty()) {
+			fileMap = loadRules("FileAccessRules.txt");
+		}
+		return matchRule(fileMap, filepath.replaceAll("\\\\","/"));
+	}
+
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForService(String serviceName) throws AccessControlException {    	
+		if (serviceMap==null || serviceMap.isEmpty()) {
+			serviceMap = loadRules("ServiceAccessRules.txt");
+		}
+		return matchRule(serviceMap, serviceName);
+	}
+
+	/**
+	 * Checks to see if the current user has access to the specified data, File, Object, etc.
+	 * If the User has access, as specified by the map parameter, this method returns true.  If the 
+	 * User does not have access or an exception is thrown, false is returned.
+	 * 
+	 * @param map
+	 *       the map containing access rules
+	 * @param path
+	 *       the path of the requested File, URL, Object, etc.
+	 * 
+	 * @return 
+	 * 		true, if the user has access, false otherwise
+	 * 
+	 */
+	private boolean matchRule(Map map, String path) {
+		// get users roles
+		User user = ESAPI.authenticator().getCurrentUser();
+		Set roles = user.getRoles();
+		// search for the first rule that matches the path and rules
+		Rule rule = searchForRule(map, roles, path);
+		return rule.allow;
+	}
+	
+	/**
+	 * Checks to see if the current user has access to the specified Class and action.
+	 * If the User has access, as specified by the map parameter, this method returns true.
+     * If the User does not have access or an exception is thrown, false is returned.
+	 * 
+	 * @param map
+	 *       the map containing access rules
+	 * @param clazz
+	 *       the Class being requested for access
+	 * @param action
+	 * 		 the action the User has asked to perform
+	 * @return 
+	 * 		true, if the user has access, false otherwise
+	 * 
+	 */
+	private boolean matchRule(Map map, Class clazz, String action) {
+		// get users roles
+		User user = ESAPI.authenticator().getCurrentUser();
+		Set roles = user.getRoles();
+		// search for the first rule that matches the path and rules
+		Rule rule = searchForRule(map, roles, clazz, action);
+		return rule != null;
+	}
+
+	/**
+	 * Search for rule. Four mapping rules are used in order: - exact match,
+	 * e.g. /access/login - longest path prefix match, beginning / and ending
+	 * /*, e.g. /access/* or /* - extension match, beginning *., e.g. *.css -
+	 * default servlet, specified by the single character pattern /
+	 * 
+	 * @param map
+	 *       the map containing access rules
+	 * @param roles
+	 *       the roles of the User being checked for access
+	 * @param path
+	 *       the File, URL, Object, etc. being checked for access
+	 * 
+	 * @return 
+	 *       the rule stating whether to allow or deny access
+	 * 
+	 */
+	private Rule searchForRule(Map map, Set roles, String path) {
+		String canonical = ESAPI.encoder().canonicalize(path);
+
+		String part = canonical;
+        if ( part == null ) {
+            part = "";
+        }
+        
+		while (part.endsWith("/")) {
+			part = part.substring(0, part.length() - 1);
+		}
+
+		if (part.indexOf("..") != -1) {
+			throw new IntrusionException("Attempt to manipulate access control path", "Attempt to manipulate access control path: " + path );
+		}
+		
+		// extract extension if any
+		String extension = "";
+		int extIndex = part.lastIndexOf(".");
+		if (extIndex != -1) {
+			extension = part.substring(extIndex + 1);
+		}
+
+		// Check for exact match - ignore any ending slash
+		Rule rule = (Rule) map.get(part);
+
+		// Check for ending with /*
+		if (rule == null)
+			rule = (Rule) map.get(part + "/*");
+
+		// Check for matching extension rule *.ext
+		if (rule == null)
+			rule = (Rule) map.get("*." + extension);
+
+		// if rule found and user's roles match rules' roles, return the rule
+		if (rule != null && overlap(rule.roles, roles))
+			return rule;
+
+		// rule hasn't been found - if there are no more parts, return a deny
+		int slash = part.lastIndexOf('/');
+		if ( slash == -1 ) {
+			return deny;
+		}
+		
+		// if there are more parts, strip off the last part and recurse
+		part = part.substring(0, part.lastIndexOf('/'));
+		
+		// return default deny
+		if (part.length() <= 1) {
+			return deny;
+		}
+		
+		return searchForRule(map, roles, part);
+	}
+	
+	/**
+	 * Search for rule. Searches the specified access map to see if any of the roles specified have 
+	 * access to perform the specified action on the specified Class.
+	 * 
+	 * @param map
+	 *      the map containing access rules
+	 * @param roles
+	 *      the roles used to determine access level
+	 * @param clazz
+	 *      the Class being requested for access
+	 * @param action
+	 * 		the action the User has asked to perform
+	 * 
+	 * @return 
+	 * 		the rule that allows the specified roles access to perform the requested action on the specified Class, or null if access is not granted
+	 * 
+	 */
+	private Rule searchForRule(Map map, Set roles, Class clazz, String action) {
+
+		// Check for exact match - ignore any ending slash
+		Rule rule = (Rule) map.get(clazz);
+		if( ( rule != null ) && ( overlap(rule.actions, action) ) && ( overlap(rule.roles, roles) )){
+			return rule;
+		}
+		return null;
+	}
+
+	/**
+	 * Return true if there is overlap between the two sets.  This method merely checks to see if 
+	 * ruleRoles contains any of the roles listed in userRoles.
+	 * 
+	 * @param ruleRoles
+	 *      the rule roles
+	 * @param userRoles
+	 *      the user roles
+	 * 
+	 * @return 
+	 * 		true, if any roles exist in both Sets.  False otherwise.
+	 */
+	private boolean overlap(Set ruleRoles, Set userRoles) {
+		if (ruleRoles.contains("any")) {
+			return true;
+		}
+		Iterator i = userRoles.iterator();
+		while (i.hasNext()) {
+			String role = (String) i.next();
+			if (ruleRoles.contains(role)) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * This method merely checks to see if ruleActions contains the action requested.
+	 * 
+	 * @param ruleActions
+	 *      actions listed for a rule
+	 * @param action
+	 *      the action requested that will be searched for in ruleActions
+	 * 
+	 * @return 
+	 * 		true, if any action exists in ruleActions.  False otherwise.
+	 */
+	private boolean overlap( List ruleActions, String action){
+		if( ruleActions.contains(action) )
+			return true;
+		return false;
+	}
+		
+	/**
+	 * Checks that the roles passed in contain only letters, numbers, and underscores.  Also checks that
+	 * roles are no more than 10 characters long.  If a role does not pass validation, it is not included in the 
+	 * list of roles returned by this method.  A log warning is also generated for any invalid roles.
+	 * 
+	 * @param roles
+	 * 		roles to validate according to criteria started above
+	 * @return
+	 * 		a List of roles that are valid according to the criteria stated above.
+	 * 
+	 */
+	private List validateRoles(List roles){
+		List ret = new ArrayList();	
+		for(int x = 0; x < roles.size(); x++){
+			String canonical = ESAPI.encoder().canonicalize(((String)roles.get(x)).trim());
+
+			if(!ESAPI.validator().isValidInput("Validating user roles in FileBasedAccessController", canonical, "RoleName", 20, false)) {
+				logger.warning( Logger.SECURITY_FAILURE, "Role: " + ((String)roles.get(x)).trim() + " is invalid, so was not added to the list of roles for this Rule.");
+			} else { 
+				ret.add(canonical.trim());
+			}
+		}
+		return ret;
+	}
+	
+	/**
+	 * Loads access rules by storing them in a hashmap.  This method begins reading the File specified by
+	 * the ruleset parameter, ignoring any lines that begin with '#' characters as comments.  Sections of the access rules file
+	 * are split by the pipe character ('|').  The method loads all paths, replacing '\' characters with '/' for uniformity then loads
+	 * the list of comma separated roles. The roles are validated to be sure they are within a 
+	 * length and character set, specified in the validateRoles(String) method.  Then the permissions are stored for each item in the rules list.
+	 * If the word "allow" appears on the line, the specified roles are granted access to the data - otherwise, they will be denied access.
+	 * 
+	 * Each path may only appear once in the access rules file.  Any entry, after the first, containing the same path will be logged and ignored. 
+	 *  
+	 * @param ruleset
+	 *      the name of the data that contains access rules
+	 * 
+	 * @return 
+	 * 		a hash map containing the ruleset
+	 */
+	private Map loadRules(String ruleset) {
+		ruleset = "fbac-policies/" + ruleset;
+		Map map = new HashMap();
+		InputStream is = null;
+		try {
+			is = ESAPI.securityConfiguration().getResourceStream(ruleset);
+			String line = "";
+			while ((line = ESAPI.validator().safeReadLine(is, 500)) != null) {
+				if (line.length() > 0 && line.charAt(0) != '#') {
+					Rule rule = new Rule();
+					String[] parts = line.split("\\|");
+					// fix Windows paths
+					rule.path = parts[0].trim().replaceAll("\\\\", "/");
+					
+					List roles = commaSplit(parts[1].trim().toLowerCase());
+					roles = validateRoles(roles);
+					for(int x = 0; x < roles.size(); x++)
+						rule.roles.add(((String)roles.get(x)).trim());
+					
+					String action = parts[2].trim();
+					rule.allow = action.equalsIgnoreCase("allow");
+					if (map.containsKey(rule.path)) {
+						logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file. Duplicate rule ignored: " + rule);
+					} else {
+						map.put(rule.path, rule);
+					}
+				}
+			}
+		} catch (Exception e) {
+			logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file: " + ruleset, e );
+		} finally {
+			try {
+				if (is != null) {
+					is.close();
+				}
+			} catch (IOException e) {
+				logger.warning(Logger.SECURITY_FAILURE, "Failure closing access control file: " + ruleset, e);
+			}
+		}
+		return map;
+	}
+	
+	/**
+	 * Loads access rules by storing them in a hashmap.  This method begins reading the File specified by
+	 * the ruleset parameter, ignoring any lines that begin with '#' characters as comments.  Sections of the access rules file
+	 * are split by the pipe character ('|').  The method then loads all Classes, loads the list of comma separated roles, then the list of comma separated actions.  
+	 * The roles are validated to be sure they are within a length and character set, specified in the validateRoles(String) method.  
+	 * 
+	 * Each path may only appear once in the access rules file.  Any entry, after the first, containing the same path will be logged and ignored. 
+	 *  
+	 * @param ruleset
+	 *      the name of the data that contains access rules
+	 * 
+	 * @return 
+	 * 		a hash map containing the ruleset
+	 */
+	private Map loadDataRules(String ruleset) {
+		Map map = new HashMap();
+		InputStream is = null;
+
+		try {
+			ruleset = "fbac-policies/" + ruleset;
+			is = ESAPI.securityConfiguration().getResourceStream(ruleset);
+			String line = "";
+			while ((line = ESAPI.validator().safeReadLine(is, 500)) != null) {
+				if (line.length() > 0 && line.charAt(0) != '#') {
+					Rule rule = new Rule();
+					String[] parts = line.split("\\|");
+					rule.clazz = Class.forName(parts[0].trim());
+					
+					List roles = commaSplit(parts[1].trim().toLowerCase());
+					roles = validateRoles(roles);
+					for(int x = 0; x < roles.size(); x++)
+						rule.roles.add(((String)roles.get(x)).trim());
+					
+					List action = commaSplit(parts[2].trim().toLowerCase());
+					for(int x = 0; x < action.size(); x++)
+						rule.actions.add(((String) action.get(x)).trim());
+					
+					if (map.containsKey(rule.path)) {
+						logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file. Duplicate rule ignored: " + rule);
+					} else {
+						map.put(rule.clazz, rule);		
+					}
+				}
+			}
+		} catch (Exception e) {
+			logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file : " + ruleset, e );
+		} finally {
+			
+			try {
+				if (is != null) {
+					is.close();
+				}
+			} catch (IOException e) {
+				logger.warning(Logger.SECURITY_FAILURE, "Failure closing access control file : " + ruleset, e);
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * This method splits a String by the ',' and returns the result as a List.
+	 * 
+	 * @param input
+	 * 		the String to split by ','
+	 * @return
+	 * 		a List where each entry was on either side of a ',' in the original String
+	 */
+	private List commaSplit(String input){
+		String[] array = input.split(",");
+		return Arrays.asList(array);
+	}
+	
+	/**
+	 * The Class Rule.
+	 */
+	private class Rule {
+
+		
+		protected String path = "";
+
+		
+		protected Set roles = new HashSet();
+
+		
+		protected boolean allow = false;
+		
+		
+		protected Class clazz = null;
+		
+		
+		protected List actions = new ArrayList();
+
+		/**
+		 * 
+		 * Creates a new Rule object.
+		 */
+		protected Rule() {
+			// to replace synthetic accessor method
+		}
+
+		/**
+	     * {@inheritDoc}
+		 */
+		public String toString() {
+			return "URL:" + path + " | " + roles + " | " + (allow ? "allow" : "deny");
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysFalseACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysFalseACR.java
new file mode 100644
index 0000000..9d3863d
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysFalseACR.java
@@ -0,0 +1,9 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+public class AlwaysFalseACR extends BaseACR<Object, Object> {
+
+//	@Override
+	public boolean isAuthorized(Object runtimeParameter) {
+		return false;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysTrueACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysTrueACR.java
new file mode 100644
index 0000000..d7cab33
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/AlwaysTrueACR.java
@@ -0,0 +1,9 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+public class AlwaysTrueACR extends BaseACR<Object, Object> {
+	
+//	@Override
+	public boolean isAuthorized(Object runtimeParameter) {
+		return true;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/BaseACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/BaseACR.java
new file mode 100644
index 0000000..f2221e0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/BaseACR.java
@@ -0,0 +1,18 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import org.owasp.esapi.AccessControlRule;
+
+abstract public class BaseACR<P, R> implements AccessControlRule<P, R> {
+
+	protected P policyParameters;
+	
+//	@Override
+	public void setPolicyParameters(P policyParameter) {
+		this.policyParameters = policyParameter;
+	}
+	
+//	@Override
+	public P getPolicyParameters() {
+		return policyParameters;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java
new file mode 100644
index 0000000..193f91e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java
@@ -0,0 +1,99 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.apache.commons.collections.iterators.ArrayListIterator;
+
+public class DelegatingACR extends BaseACR<DynaBeanACRParameter, Object[]> {
+	protected Method delegateMethod;
+	protected Object delegateInstance;
+	
+	@Override
+	public void setPolicyParameters(DynaBeanACRParameter policyParameter) {
+		String delegateClassName = policyParameter.getString("delegateClass", "").trim();
+		String methodName = policyParameter.getString("delegateMethod", "").trim();
+		String[] parameterClassNames = policyParameter.getStringArray("parameterClasses");
+
+		//Convert the classNames into Classes and get the delegate method.
+		Class delegateClass = getClass(delegateClassName, "delegate");
+		Class parameterClasses[] = getParameters(parameterClassNames);
+		try {
+			this.delegateMethod = delegateClass.getMethod(methodName, parameterClasses);
+		} catch (SecurityException e) {
+			throw new IllegalArgumentException(e.getMessage() + 
+					" delegateClass.delegateMethod(parameterClasses): \"" +  
+					delegateClassName + "." + methodName + "(" + parameterClassNames +
+					")\" must be public.", e);
+		} catch (NoSuchMethodException e) {
+			throw new IllegalArgumentException(e.getMessage() + 
+					" delegateClass.delegateMethod(parameterClasses): \"" +  
+					delegateClassName + "." + methodName + "(" + parameterClassNames +
+					")\" does not exist.", e);
+		}
+	
+		//static methods do not need a delegateInstance. Non-static methods do.
+		if(!Modifier.isStatic(this.delegateMethod.getModifiers())) {
+			try {
+				this.delegateInstance = delegateClass.newInstance();
+			} catch (InstantiationException ex) {
+				throw new IllegalArgumentException( 
+						" Delegate class \"" + delegateClassName + 
+						"\" must be concrete, because method " +
+						delegateClassName + "." + methodName + "(" + parameterClassNames +
+						") is not static.", ex);
+			} catch (IllegalAccessException ex) {
+				new IllegalArgumentException( 
+						" Delegate class \"" + delegateClassName + 
+						"\" must must have a zero-argument constructor, because " +
+						"method delegateClass.delegateMethod(parameterClasses): \"" +  
+						delegateClassName + "." + methodName + "(" + parameterClassNames +
+						")\" is not static.", ex);
+			}	
+		} else {
+			this.delegateInstance = null;
+		}
+	}
+	/**
+	 * Convert an array of fully qualified class names into an array of Class objects
+	 * @param parameterClassNames
+	 * @return
+	 */
+	protected final Class[] getParameters(String[] parameterClassNames) {
+		if(parameterClassNames == null) {
+			return new Class[0];
+		}
+		Vector<Class> classes = new Vector<Class>();
+		Iterator<String> classNames = new ArrayListIterator(parameterClassNames);
+		while(classNames.hasNext()) {
+			classes.add(getClass(classNames.next(), "parameter"));
+		}
+		return classes.toArray(new Class[classes.size()]);
+	}
+	/**
+	 * Convert a single fully qualified class name into a Class object
+	 * @param className
+	 * @param purpose
+	 * @return
+	 */
+	protected final Class getClass(String className, String purpose) {
+		try {
+	        Class theClass = Class.forName(className);
+	        return theClass;
+	    } catch ( ClassNotFoundException ex ) {
+			throw new IllegalArgumentException(ex.getMessage() + 
+					" " + purpose + " Class " + className + 
+					" must be in the classpath", ex);
+	    } 
+	}
+	/**
+	 * Delegates to the method specified in setPolicyParameters
+	 */
+	public boolean isAuthorized(Object[] runtimeParameters) throws Exception {
+		return ((Boolean)delegateMethod.invoke(delegateInstance, runtimeParameters)).booleanValue();
+	}
+}
+
+
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java
new file mode 100644
index 0000000..7c4879e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java
@@ -0,0 +1,186 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.apache.commons.beanutils.*;
+import org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters;
+
+/**
+ * A DynaBean comes from the apache bean utils. It is basically a 
+ * convenient way to dynamically assign getters and setters. Essentially, 
+ * the way we use DynaBean is a HashMap that can be set to read only.
+ * @author Mike H. Fauzy
+ */
+public class DynaBeanACRParameter implements PolicyParameters {
+	protected LazyDynaMap policyProperties;
+	
+	public DynaBeanACRParameter() {
+		policyProperties = new LazyDynaMap();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters#get(java.lang.String)
+	 */
+	public Object get(String key) {
+		return policyProperties.get(key);
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public boolean getBoolean(String key) {
+		return ((Boolean)get(key)).booleanValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public byte getByte(String key) {
+		return ((Byte)get(key)).byteValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public char getChar(String key) {
+		return ((Character)get(key)).charValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public int getInt(String key) {
+		return ((Integer)get(key)).intValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public long getLong(String key) {
+		return ((Long)get(key)).longValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public float getFloat(String key) {
+		return ((Float)get(key)).floatValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public double getDouble(String key) {
+		return ((Double)get(key)).doubleValue();
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public BigDecimal getBigDecimal(String key) {
+		return (BigDecimal)get(key);
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public BigInteger getBigInteger(String key) {
+		return (BigInteger)get(key);
+	}
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public Date getDate(String key) {
+		return (Date)get(key);
+	}
+	
+	/**
+	 * Convenience method to avoid common casts. Note that the time object
+	 * is the same as a date object
+	 * @param key
+	 * @return
+	 */
+	public Date getTime(String key) {
+		return (Date)get(key);
+	}
+	
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public String getString(String key) {
+		return (String)get(key);
+	}
+	
+	public String getString(String key, String defaultValue) {
+		return (String)get(key) == null ? defaultValue : (String)get(key);
+	}
+	
+	public String[] getStringArray(String key) {
+		return (String[])get(key);
+	}
+	
+	/**
+	 * Convenience method to avoid common casts.
+	 * @param key
+	 * @return
+	 */
+	public Object getObject(String key) {
+		return get(key);
+	}	
+
+	/* (non-Javadoc)
+	 * @see org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters#set(java.lang.String, java.lang.Object)
+	 */
+	public void set(String key, Object value) throws IllegalArgumentException {
+		policyProperties.set(key, value);
+	}
+	/* (non-Javadoc)
+	 * @see org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters#put(java.lang.String, java.lang.Object)
+	 */
+	public void put(String key, Object value) throws IllegalArgumentException {
+		set(key, value);
+	}
+	
+	/**
+	 * This makes the map itself read only, but the mutability of objects 
+	 * that this map contains is not affected. Specifically, properties 
+	 * cannot be added or removed and the reference cannot be changed to 
+	 * a different object, but this does not change whether the values that the 
+	 * object contains can be changed. 
+	 */
+	public void lock() {
+		policyProperties.setRestricted(true);
+	}
+	
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		Iterator keys = policyProperties.getMap().keySet().iterator();
+		String currentKey;
+		while(keys.hasNext()) {
+			currentKey = (String)keys.next();
+			sb.append(currentKey);
+			sb.append("=");
+			sb.append(policyProperties.get(currentKey));
+			if(keys.hasNext()) {
+				sb.append(",");
+			}
+		}
+		return sb.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/EchoRuntimeParameterACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/EchoRuntimeParameterACR.java
new file mode 100644
index 0000000..bbc8168
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/EchoRuntimeParameterACR.java
@@ -0,0 +1,13 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+public class EchoRuntimeParameterACR extends BaseACR<Object, Boolean>{
+
+	/**
+	 * Returns true iff runtimeParameter is a Boolean true.
+	 * throws ClassCastException if runtimeParameter is not a Boolean.
+	 */
+	public boolean isAuthorized(Boolean runtimeParameter) throws ClassCastException{
+		return runtimeParameter.booleanValue();
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/ExperimentalAccessController.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/ExperimentalAccessController.java
new file mode 100644
index 0000000..f97b226
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/ExperimentalAccessController.java
@@ -0,0 +1,184 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.util.Map;
+
+import org.owasp.esapi.AccessControlRule;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.reference.accesscontrol.policyloader.ACRPolicyFileLoader;
+import org.owasp.esapi.reference.accesscontrol.policyloader.PolicyDTO;
+
+public class ExperimentalAccessController implements AccessController {
+	private Map ruleMap;
+
+	protected final Logger logger = ESAPI.getLogger("DefaultAccessController");
+	
+	public ExperimentalAccessController(Map ruleMap) {
+		this.ruleMap = ruleMap;	
+	}
+	public ExperimentalAccessController() throws AccessControlException {
+		ACRPolicyFileLoader policyDescriptor = new ACRPolicyFileLoader();
+		PolicyDTO policyDTO = policyDescriptor.load();		
+		ruleMap = policyDTO.getAccessControlRules();
+	}
+	
+	public boolean isAuthorized(Object key, Object runtimeParameter) {
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied",
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Evaluating Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			return rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			try {
+				//Log the exception by throwing and then catching it.
+				//TODO figure out what which string goes where.		
+				throw new AccessControlException("Access Denied",
+					"An unhandled Exception was " +
+					"caught, so access is denied.",  
+					e);	
+			} catch(AccessControlException ace) {
+				//the exception was just logged. There's nothing left to do.
+			}
+			return false; //fail closed
+		}
+	}
+
+	public void assertAuthorized(Object key, Object runtimeParameter)
+		throws AccessControlException {
+		boolean isAuthorized = false;
+		try {
+			AccessControlRule rule = (AccessControlRule)ruleMap.get(key);
+			if(rule == null) {
+				throw new AccessControlException("Access Denied", 
+						"AccessControlRule was not found for key: " + key); 
+			}
+			if(logger.isDebugEnabled()){ logger.debug(Logger.EVENT_SUCCESS, "Asserting Authorization Rule \"" + key + "\" Using class: " + rule.getClass().getCanonicalName()); }
+			isAuthorized = rule.isAuthorized(runtimeParameter);
+		} catch(Exception e) {
+			//TODO figure out what which string goes where.		
+			throw new AccessControlException("Access Denied", "An unhandled Exception was " +
+					"caught, so access is denied." +
+					"AccessControlException.",
+					e);
+		}
+		if(!isAuthorized) {
+			throw new AccessControlException("Access Denied", 
+					"Access Denied for key: " + key + 
+					" runtimeParameter: " + runtimeParameter);
+		}
+	}
+	
+	/**** Below this line is legacy support ****/
+	
+	/**
+	 * @param action
+	 * @param data
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForData(java.lang.String, java.lang.Object)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForData(String action, Object data)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+	/**
+	 * @param filepath
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForFile(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForFile(String filepath)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+	/**
+	 * @param functionName
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForFunction(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForFunction(String functionName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+	/**
+	 * @param serviceName
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForService(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForService(String serviceName)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+	/**
+	 * @param url
+	 * @throws AccessControlException
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#assertAuthorizedForURL(java.lang.String)
+	 * @deprecated
+	 */
+	public void assertAuthorizedForURL(String url)
+			throws AccessControlException {
+		this.assertAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+
+	/**
+	 * @param action
+	 * @param data
+	 * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForData(java.lang.String, java.lang.Object)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForData(String action, Object data) {
+		return this.isAuthorized("AC 1.0 Data", new Object[] {action, data});
+	}
+
+	/**
+	 * @param filepath
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForFile(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForFile(String filepath) {
+		return this.isAuthorized("AC 1.0 File", new Object[] {filepath});
+	}
+
+	/**
+	 * @param functionName
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForFunction(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForFunction(String functionName) {
+		return this.isAuthorized("AC 1.0 Function", new Object[] {functionName});
+	}
+
+	/**
+	 * @param serviceName
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForService(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForService(String serviceName) {
+		return this.isAuthorized("AC 1.0 Service", new Object[] {serviceName});
+	}
+
+	/**
+	 * @param url
+     * @return {@code true} if access is permitted; {@code false} otherwise.
+	 * @see org.owasp.esapi.reference.accesscontrol.FileBasedACRs#isAuthorizedForURL(java.lang.String)
+	 * @deprecated
+	 */
+	public boolean isAuthorizedForURL(String url) {
+		return this.isAuthorized("AC 1.0 URL", new Object[] {url});
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/FileBasedACRs.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/FileBasedACRs.java
new file mode 100644
index 0000000..c788b87
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/FileBasedACRs.java
@@ -0,0 +1,559 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Mike Fauzy <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.accesscontrol;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.IntrusionException;
+
+// CHECKME: If this exists for backward compatibility, should this
+//          class be deprecated??? If so, mark it using annotation.
+/**
+ * This class exists for backwards compatibility with the AccessController 1.0 
+ * reference implementation.
+ *
+ * This reference implementation uses a simple model for specifying a set of
+ * access control rules. Many organizations will want to create their own
+ * implementation of the methods provided in the AccessController interface.
+ * <P>
+ * This reference implementation uses a simple scheme for specifying the rules.
+ * The first step is to create a namespace for the resources being accessed. For
+ * files and URL's, this is easy as they already have a namespace. Be extremely
+ * careful about canonicalizing when relying on information from the user in an
+ * access control decision.
+ * <P>
+ * For functions, data, and services, you will have to come up with your own
+ * namespace for the resources being accessed. You might simply define a flat
+ * namespace with a list of category names. For example, you might specify
+ * 'FunctionA', 'FunctionB', and 'FunctionC'. Or you can create a richer
+ * namespace with a hierarchical structure, such as:
+ * <P>
+ * /functions
+ * <ul>
+ * <li>purchasing</li>
+ * <li>shipping</li>
+ * <li>inventory</li>
+ * </ul>
+ * /admin
+ * <ul>
+ * <li>createUser</li>
+ * <li>deleteUser</li>
+ * </ul>
+ * Once you've defined your namespace, you have to work out the rules that
+ * govern access to the different parts of the namespace. This implementation
+ * allows you to attach a simple access control list (ACL) to any part of the
+ * namespace tree. The ACL lists a set of roles that are either allowed or
+ * denied access to a part of the tree. You specify these rules in a textfile
+ * with a simple format.
+ * <P>
+ * There is a single configuration file supporting each of the five methods in
+ * the AccessController interface. These files are located in the ESAPI
+ * resources directory as specified when the JVM was started. The use of a
+ * default deny rule is STRONGLY recommended. The file format is as follows:
+ * 
+ * <pre>
+ * path          | role,role   | allow/deny | comment
+ * ------------------------------------------------------------------------------------
+ * /banking/*    | user,admin  | allow      | authenticated users can access /banking
+ * /admin        | admin       | allow      | only admin role can access /admin
+ * /             | any         | deny       | default deny rule
+ * </pre>
+ * 
+ * To find the matching rules, this implementation follows the general approach
+ * used in Java EE when matching HTTP requests to servlets in web.xml. The four
+ * mapping rules are used in the following order:
+ * <ul>
+ * <li>exact match, e.g. /access/login</li>
+ * <li>longest path prefix match, beginning / and ending /*, e.g. /access/* or
+ * /*</li>
+ * <li>extension match, beginning *., e.g. *.css</li>
+ * <li>default rule, specified by the single character pattern /</li>
+ * </ul>
+ * 
+ * @author Mike Fauzy (mike.fauzy at aspectsecurity.com)
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @since June 1, 2007
+ */
+public class FileBasedACRs {
+
+	/** The url map. */
+	private Map urlMap = new HashMap();
+
+	/** The function map. */
+	private Map functionMap = new HashMap();
+
+	/** The data map. */
+	private Map dataMap = new HashMap();
+
+	/** The file map. */
+	private Map fileMap = new HashMap();
+
+	/** The service map. */
+	private Map serviceMap = new HashMap();
+
+	/** A rule containing "deny". */
+	private Rule deny = new Rule();
+
+	/** The logger. */
+	private Logger logger = ESAPI.getLogger("FileBasedACRs");
+
+    /**
+	* Check if URL is authorized.
+	* @param url The URL tested for authorization
+	* @return {@code true} if access is allowed, {@code false} otherwise.
+	*/
+    public boolean isAuthorizedForURL(String url) {
+		if (urlMap==null || urlMap.isEmpty()) {
+			urlMap = loadRules("URLAccessRules.txt");
+		}
+		return matchRule(urlMap, url);
+	}
+
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForFunction(String functionName) throws AccessControlException {
+    	if (functionMap==null || functionMap.isEmpty()) {
+			functionMap = loadRules("FunctionAccessRules.txt");
+		}
+		return matchRule(functionMap, functionName);
+	}
+  
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForData(String action, Object data) throws AccessControlException{
+    	if (dataMap==null || dataMap.isEmpty()) {
+			dataMap = loadDataRules("DataAccessRules.txt");
+    	}		
+    	return matchRule(dataMap, (Class)data, action);    	
+    }
+    
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForFile(String filepath) throws AccessControlException {
+		if (fileMap==null || fileMap.isEmpty()) {
+			fileMap = loadRules("FileAccessRules.txt");
+		}
+		return matchRule(fileMap, filepath.replaceAll("\\\\","/"));
+	}
+
+    /**
+	* TODO Javadoc
+	*/
+    public boolean isAuthorizedForService(String serviceName) throws AccessControlException {    	
+		if (serviceMap==null || serviceMap.isEmpty()) {
+			serviceMap = loadRules("ServiceAccessRules.txt");
+		}
+		return matchRule(serviceMap, serviceName);
+	}
+
+	/**
+	 * Checks to see if the current user has access to the specified data, File, Object, etc.
+	 * If the User has access, as specified by the map parameter, this method returns true.  If the 
+	 * User does not have access or an exception is thrown, false is returned.
+	 * 
+	 * @param map
+	 *       the map containing access rules
+	 * @param path
+	 *       the path of the requested File, URL, Object, etc.
+	 * 
+	 * @return 
+	 * 		true, if the user has access, false otherwise
+	 * 
+	 */
+	private boolean matchRule(Map map, String path) {
+		// get users roles
+		User user = ESAPI.authenticator().getCurrentUser();
+		Set roles = user.getRoles();
+		// search for the first rule that matches the path and rules
+		Rule rule = searchForRule(map, roles, path);
+		return rule.allow;
+	}
+	
+	/**
+	 * Checks to see if the current user has access to the specified Class and action.
+	 * If the User has access, as specified by the map parameter, this method returns true.
+     * If the User does not have access or an exception is thrown, false is returned.
+	 * 
+	 * @param map
+	 *       the map containing access rules
+	 * @param clazz
+	 *       the Class being requested for access
+	 * @param action
+	 * 		 the action the User has asked to perform
+	 * @return 
+	 * 		true, if the user has access, false otherwise
+	 * 
+	 */
+	private boolean matchRule(Map map, Class clazz, String action) {
+		// get users roles
+		User user = ESAPI.authenticator().getCurrentUser();
+		Set roles = user.getRoles();
+		// search for the first rule that matches the path and rules
+		Rule rule = searchForRule(map, roles, clazz, action);
+		return rule != null;
+	}
+
+	/**
+	 * Search for rule. Four mapping rules are used in order: - exact match,
+	 * e.g. /access/login - longest path prefix match, beginning / and ending
+	 * /*, e.g. /access/* or /* - extension match, beginning *., e.g. *.css -
+	 * default servlet, specified by the single character pattern /
+	 * 
+	 * @param map
+	 *       the map containing access rules
+	 * @param roles
+	 *       the roles of the User being checked for access
+	 * @param path
+	 *       the File, URL, Object, etc. being checked for access
+	 * 
+	 * @return 
+	 *       the rule stating whether to allow or deny access
+	 * 
+	 */
+	private Rule searchForRule(Map map, Set roles, String path) {
+		String canonical = ESAPI.encoder().canonicalize(path);
+
+		String part = canonical;
+        if ( part == null ) {
+            part = "";
+        }
+        
+		while (part.endsWith("/")) {
+			part = part.substring(0, part.length() - 1);
+		}
+
+		if (part.indexOf("..") != -1) {
+			throw new IntrusionException("Attempt to manipulate access control path", "Attempt to manipulate access control path: " + path );
+		}
+		
+		// extract extension if any
+		String extension = "";
+		int extIndex = part.lastIndexOf(".");
+		if (extIndex != -1) {
+			extension = part.substring(extIndex + 1);
+		}
+
+		// Check for exact match - ignore any ending slash
+		Rule rule = (Rule) map.get(part);
+
+		// Check for ending with /*
+		if (rule == null)
+			rule = (Rule) map.get(part + "/*");
+
+		// Check for matching extension rule *.ext
+		if (rule == null)
+			rule = (Rule) map.get("*." + extension);
+
+		// if rule found and user's roles match rules' roles, return the rule
+		if (rule != null && overlap(rule.roles, roles))
+			return rule;
+
+		// rule hasn't been found - if there are no more parts, return a deny
+		int slash = part.lastIndexOf('/');
+		if ( slash == -1 ) {
+			return deny;
+		}
+		
+		// if there are more parts, strip off the last part and recurse
+		part = part.substring(0, part.lastIndexOf('/'));
+		
+		// return default deny
+		if (part.length() <= 1) {
+			return deny;
+		}
+		
+		return searchForRule(map, roles, part);
+	}
+	
+	/**
+	 * Search for rule. Searches the specified access map to see if any of the roles specified have 
+	 * access to perform the specified action on the specified Class.
+	 * 
+	 * @param map
+	 *      the map containing access rules
+	 * @param roles
+	 *      the roles used to determine access level
+	 * @param clazz
+	 *      the Class being requested for access
+	 * @param action
+	 * 		the action the User has asked to perform
+	 * 
+	 * @return 
+	 * 		the rule that allows the specified roles access to perform the requested action on the specified Class, or null if access is not granted
+	 * 
+	 */
+	private Rule searchForRule(Map map, Set roles, Class clazz, String action) {
+
+		// Check for exact match - ignore any ending slash
+		Rule rule = (Rule) map.get(clazz);
+		if( ( rule != null ) && ( overlap(rule.actions, action) ) && ( overlap(rule.roles, roles) )){
+			return rule;
+		}
+		return null;
+	}
+
+	/**
+	 * Return true if there is overlap between the two sets.  This method merely checks to see if 
+	 * ruleRoles contains any of the roles listed in userRoles.
+	 * 
+	 * @param ruleRoles
+	 *      the rule roles
+	 * @param userRoles
+	 *      the user roles
+	 * 
+	 * @return 
+	 * 		true, if any roles exist in both Sets.  False otherwise.
+	 */
+	private boolean overlap(Set ruleRoles, Set userRoles) {
+		if (ruleRoles.contains("any")) {
+			return true;
+		}
+		Iterator i = userRoles.iterator();
+		while (i.hasNext()) {
+			String role = (String) i.next();
+			if (ruleRoles.contains(role)) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * This method merely checks to see if ruleActions contains the action requested.
+	 * 
+	 * @param ruleActions
+	 *      actions listed for a rule
+	 * @param action
+	 *      the action requested that will be searched for in ruleActions
+	 * 
+	 * @return 
+	 * 		true, if any action exists in ruleActions.  False otherwise.
+	 */
+	private boolean overlap( List ruleActions, String action){
+		if( ruleActions.contains(action) )
+			return true;
+		return false;
+	}
+		
+	/**
+	 * Checks that the roles passed in contain only letters, numbers, and underscores.  Also checks that
+	 * roles are no more than 10 characters long.  If a role does not pass validation, it is not included in the 
+	 * list of roles returned by this method.  A log warning is also generated for any invalid roles.
+	 * 
+	 * @param roles
+	 * 		roles to validate according to criteria started above
+	 * @return
+	 * 		a List of roles that are valid according to the criteria stated above.
+	 * 
+	 */
+	private List validateRoles(List roles){
+		List ret = new ArrayList();	
+		for(int x = 0; x < roles.size(); x++){
+			String canonical = ESAPI.encoder().canonicalize(((String)roles.get(x)).trim());
+
+			if(!ESAPI.validator().isValidInput("Validating user roles in FileBasedAccessController", canonical, "RoleName", 20, false)) {
+				logger.warning( Logger.SECURITY_FAILURE, "Role: " + ((String)roles.get(x)).trim() + " is invalid, so was not added to the list of roles for this Rule.");
+			} else { 
+				ret.add(canonical.trim());
+			}
+		}
+		return ret;
+	}
+	
+	/**
+	 * Loads access rules by storing them in a hashmap.  This method begins reading the File specified by
+	 * the ruleset parameter, ignoring any lines that begin with '#' characters as comments.  Sections of the access rules file
+	 * are split by the pipe character ('|').  The method loads all paths, replacing '\' characters with '/' for uniformity then loads
+	 * the list of comma separated roles. The roles are validated to be sure they are within a 
+	 * length and character set, specified in the validateRoles(String) method.  Then the permissions are stored for each item in the rules list.
+	 * If the word "allow" appears on the line, the specified roles are granted access to the data - otherwise, they will be denied access.
+	 * 
+	 * Each path may only appear once in the access rules file.  Any entry, after the first, containing the same path will be logged and ignored. 
+	 *  
+	 * @param ruleset
+	 *      the name of the data that contains access rules
+	 * 
+	 * @return 
+	 * 		a hash map containing the ruleset
+	 */
+	private Map loadRules(String ruleset) {
+		ruleset = "fbac-policies/" + ruleset;
+		Map map = new HashMap();
+		InputStream is = null;
+		try {
+			is = ESAPI.securityConfiguration().getResourceStream(ruleset);
+			String line = "";
+			while ((line = ESAPI.validator().safeReadLine(is, 500)) != null) {
+				if (line.length() > 0 && line.charAt(0) != '#') {
+					Rule rule = new Rule();
+					String[] parts = line.split("\\|");
+					// fix Windows paths
+					rule.path = parts[0].trim().replaceAll("\\\\", "/");
+					
+					List roles = commaSplit(parts[1].trim().toLowerCase());
+					roles = validateRoles(roles);
+					for(int x = 0; x < roles.size(); x++)
+						rule.roles.add(((String)roles.get(x)).trim());
+					
+					String action = parts[2].trim();
+					rule.allow = action.equalsIgnoreCase("allow");
+					if (map.containsKey(rule.path)) {
+						logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file. Duplicate rule ignored: " + rule);
+					} else {
+						map.put(rule.path, rule);
+					}
+				}
+			}
+		} catch (Exception e) {
+			logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file: " + ruleset, e );
+		} finally {
+			try {
+				if (is != null) {
+					is.close();
+				}
+			} catch (IOException e) {
+				logger.warning(Logger.SECURITY_FAILURE, "Failure closing access control file: " + ruleset, e);
+			}
+		}
+		return map;
+	}
+	
+	/**
+	 * Loads access rules by storing them in a hashmap.  This method begins reading the File specified by
+	 * the ruleset parameter, ignoring any lines that begin with '#' characters as comments.  Sections of the access rules file
+	 * are split by the pipe character ('|').  The method then loads all Classes, loads the list of comma separated roles, then the list of comma separated actions.  
+	 * The roles are validated to be sure they are within a length and character set, specified in the validateRoles(String) method.  
+	 * 
+	 * Each path may only appear once in the access rules file.  Any entry, after the first, containing the same path will be logged and ignored. 
+	 *  
+	 * @param ruleset
+	 *      the name of the data that contains access rules
+	 * 
+	 * @return 
+	 * 		a hash map containing the ruleset
+	 */
+	private Map loadDataRules(String ruleset) {
+		Map map = new HashMap();
+		InputStream is = null;
+
+		try {
+			ruleset = "fbac-policies/" + ruleset;
+			is = ESAPI.securityConfiguration().getResourceStream(ruleset);
+			String line = "";
+			while ((line = ESAPI.validator().safeReadLine(is, 500)) != null) {
+				if (line.length() > 0 && line.charAt(0) != '#') {
+					Rule rule = new Rule();
+					String[] parts = line.split("\\|");
+					rule.clazz = Class.forName(parts[0].trim());
+					
+					List roles = commaSplit(parts[1].trim().toLowerCase());
+					roles = validateRoles(roles);
+					for(int x = 0; x < roles.size(); x++)
+						rule.roles.add(((String)roles.get(x)).trim());
+					
+					List action = commaSplit(parts[2].trim().toLowerCase());
+					for(int x = 0; x < action.size(); x++)
+						rule.actions.add(((String) action.get(x)).trim());
+					
+					if (map.containsKey(rule.path)) {
+						logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file. Duplicate rule ignored: " + rule);
+					} else {
+						map.put(rule.clazz, rule);		
+					}
+				}
+			}
+		} catch (Exception e) {
+			logger.warning( Logger.SECURITY_FAILURE, "Problem in access control file : " + ruleset, e );
+		} finally {
+			
+			try {
+				if (is != null) {
+					is.close();
+				}
+			} catch (IOException e) {
+				logger.warning(Logger.SECURITY_FAILURE, "Failure closing access control file : " + ruleset, e);
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * This method splits a String by the ',' and returns the result as a List.
+	 * 
+	 * @param input
+	 * 		the String to split by ','
+	 * @return
+	 * 		a List where each entry was on either side of a ',' in the original String
+	 */
+	private List commaSplit(String input){
+		String[] array = input.split(",");
+		return Arrays.asList(array);
+	}
+	
+	/**
+	 * The Class Rule.
+	 */
+	private class Rule {
+
+		
+		protected String path = "";
+
+		
+		protected Set roles = new HashSet();
+
+		
+		protected boolean allow = false;
+		
+		
+		protected Class clazz = null;
+		
+		
+		protected List actions = new ArrayList();
+
+		/**
+		 * 
+		 * Creates a new Rule object.
+		 */
+		protected Rule() {
+			// to replace synthetic accessor method
+		}
+
+		/**
+	     * {@inheritDoc}
+		 */
+		public String toString() {
+			return "URL:" + path + " | " + roles + " | " + (allow ? "allow" : "deny");
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/all-wcprops b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/all-wcprops
new file mode 100644
index 0000000..30f35a6
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader
+END
+DynaBeanACRParameterLoader.java
+K 25
+svn:wc:ra_dav:version-url
+V 134
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/DynaBeanACRParameterLoader.java
+END
+PolicyParameters.java
+K 25
+svn:wc:ra_dav:version-url
+V 124
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyParameters.java
+END
+PolicyDTO.java
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyDTO.java
+END
+EchoDynaBeanPolicyParameterACR.java
+K 25
+svn:wc:ra_dav:version-url
+V 138
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/EchoDynaBeanPolicyParameterACR.java
+END
+ACRPolicyFileLoader.java
+K 25
+svn:wc:ra_dav:version-url
+V 127
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoader.java
+END
+ACRParameterLoaderHelper.java
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoaderHelper.java
+END
+ACRParameterLoader.java
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoader.java
+END
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/entries b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/entries
new file mode 100644
index 0000000..8166e17
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/entries
@@ -0,0 +1,266 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-11-05T04:21:56.553937Z
+1646
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+DynaBeanACRParameterLoader.java
+file
+
+
+
+
+2014-02-18T16:19:52.885965Z
+2c004849a85dd896ec008750962f31ee
+2009-04-17T20:10:12.050344Z
+466
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1716
+

+PolicyParameters.java
+file
+
+
+
+
+2014-02-18T16:19:52.885965Z
+d8b127bffff7516c5f0f49a88c23251c
+2009-04-08T22:18:24.697030Z
+457
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1243
+

+PolicyDTO.java
+file
+
+
+
+
+2014-02-18T16:19:52.885965Z
+68dcc0b8464974b975e64185885767cf
+2009-05-10T04:33:18.539487Z
+510
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1652
+

+EchoDynaBeanPolicyParameterACR.java
+file
+
+
+
+
+2014-02-18T16:19:52.885965Z
+a23794beedd0b14d898fc5b07969da44
+2010-11-05T04:21:56.553937Z
+1646
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+641
+

+ACRPolicyFileLoader.java
+file
+
+
+
+
+2014-02-18T16:19:52.885965Z
+e5e23084649de2728ddb8a29cc6739f8
+2010-02-05T23:50:35.754048Z
+1118
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4389
+

+ACRParameterLoaderHelper.java
+file
+
+
+
+
+2014-02-18T16:19:52.885965Z
+0e95395cacc5a36ac09dede6e724388c
+2009-04-17T20:10:12.050344Z
+466
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2196
+

+ACRParameterLoader.java
+file
+
+
+
+
+2014-02-18T16:19:52.885965Z
+d27845d0cb9ed0a4c96ea260e1b15c62
+2009-05-10T04:33:18.539487Z
+510
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+326
+

diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRParameterLoader.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRParameterLoader.java.svn-base
new file mode 100644
index 0000000..7de31d9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRParameterLoader.java.svn-base
@@ -0,0 +1,9 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+
+public interface ACRParameterLoader <T> {
+	public abstract T getParameters(XMLConfiguration config, int currentRule)
+		throws java.lang.Exception; //TODO this exception could be more specific
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRParameterLoaderHelper.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRParameterLoaderHelper.java.svn-base
new file mode 100644
index 0000000..720dcc3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRParameterLoaderHelper.java.svn-base
@@ -0,0 +1,46 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+final public class ACRParameterLoaderHelper {
+	
+	public static Object getParameterValue(XMLConfiguration config, int currentRule, int currentParameter, String parameterType) throws Exception {
+		String key = "AccessControlRules.AccessControlRule(" + 
+			currentRule + ").Parameters.Parameter(" + currentParameter + ")[@value]";
+		Object parameterValue;
+		if("String".equalsIgnoreCase(parameterType)) {
+			parameterValue = config.getString(key);
+		} else if("StringArray".equalsIgnoreCase(parameterType)) {
+			parameterValue = config.getStringArray(key);
+		} else if("Boolean".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getBoolean(key);
+		} else if("Byte".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getByte(key);
+		} else if("Int".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getInt(key);
+		} else if("Long".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getLong(key);
+		} else if("Float".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getFloat(key);
+		} else if("Double".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getDouble(key);
+		} else if("BigDecimal".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getBigDecimal(key);
+		} else if("BigInteger".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getBigInteger(key);
+		} else if("Date".equalsIgnoreCase(parameterType)){
+			parameterValue = java.text.DateFormat.getDateInstance().parse(config.getString(key));
+		} else if("Time".equalsIgnoreCase(parameterType)){
+			java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("h:mm a");
+			parameterValue = sdf.parseObject(config.getString(key)); 
+//			parameterValue = java.text.DateFormat.getTimeInstance().parse(config.getString(key));
+		}		
+		//add timestamp. check for other stuff.
+		else {
+			throw new IllegalArgumentException("Unable to load the key \"" + key 
+					+ "\", because " + "the type \"" + parameterType + 
+					"\" was not recognized." );
+		}
+		return parameterValue;
+	}	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRPolicyFileLoader.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRPolicyFileLoader.java.svn-base
new file mode 100644
index 0000000..d0ce361
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRPolicyFileLoader.java.svn-base
@@ -0,0 +1,99 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AccessControlException;
+
+final public class ACRPolicyFileLoader {
+	protected final Logger logger = ESAPI.getLogger("ACRPolicyFileLoader");
+	
+	public PolicyDTO load() throws AccessControlException {
+		PolicyDTO policyDTO = new PolicyDTO();
+		XMLConfiguration config;
+		File file = ESAPI.securityConfiguration().getResourceFile("ESAPI-AccessControlPolicy.xml"); 
+		try
+		{
+		    config = new XMLConfiguration(file);		    
+		}
+		catch(ConfigurationException cex)
+		{
+			if(file == null) {
+				throw new AccessControlException("Unable to load configuration file for the following: " + "ESAPI-AccessControlPolicy.xml", "", cex);
+			}
+		    throw new AccessControlException("Unable to load configuration file from the following location: " + file.getAbsolutePath(), "", cex);
+		} 
+
+		Object property = config.getProperty("AccessControlRules.AccessControlRule[@name]");
+		logger.info(Logger.EVENT_SUCCESS, "Loading Property: " + property);
+		int numberOfRules = 0;
+		if(property instanceof Collection) {
+			numberOfRules = ((Collection)property).size();
+		} //implied else property == null -> return new PolicyDTO
+				 
+		String ruleName = "";
+		String ruleClass = "";
+		Object rulePolicyParameter = null;
+		int currentRule = 0;
+	    try {	    	
+	    	logger.info(Logger.EVENT_SUCCESS, "Number of rules: " + numberOfRules);
+			for(currentRule = 0; currentRule < numberOfRules; currentRule++) {
+				logger.trace(Logger.EVENT_SUCCESS, "----");
+				ruleName = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ")[@name]");
+				logger.trace(Logger.EVENT_SUCCESS, "Rule name: " + ruleName);
+				ruleClass = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ")[@class]");
+				logger.trace(Logger.EVENT_SUCCESS, "Rule Class: " + ruleClass);
+				rulePolicyParameter = getPolicyParameter(config, currentRule);
+				logger.trace(Logger.EVENT_SUCCESS, "rulePolicyParameters: " + rulePolicyParameter);
+				policyDTO.addAccessControlRule(
+						ruleName,
+						ruleClass,
+						rulePolicyParameter);		    	
+			}
+			logger.info(Logger.EVENT_SUCCESS, "policyDTO loaded: " + policyDTO);
+		} catch (Exception e) {
+			throw new AccessControlException("Unable to load AccessControlRule parameter. " + 
+					" Rule number: " + currentRule + 
+					" Probably: Rule.name: " + ruleName +
+					" Probably: Rule.class: " + ruleClass +
+					e.getMessage(), "", e);
+		}
+		return policyDTO;
+	}
+
+	protected Object getPolicyParameter(XMLConfiguration config, int currentRule)
+		throws ClassNotFoundException, IllegalAccessException, InstantiationException, Exception {
+		//If there aren't any properties: short circuit and return null.
+//		Properties tempParameters = config.getProperties("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter[@name]");
+		Object property = config.getProperty("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter[@name]");
+		if(property == null) {
+			return null;
+		}
+		
+		int numberOfProperties = 0;		
+		if(property instanceof Collection) {
+			numberOfProperties = ((Collection)property).size(); 
+		} else {
+			numberOfProperties = 1;
+		}
+		logger.info(Logger.EVENT_SUCCESS, "Number of properties: " + numberOfProperties);
+		
+		if(numberOfProperties < 1) {
+			return null;
+		}
+		String parametersLoaderClassName = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters[@parametersLoader]");
+		if("".equals(parametersLoaderClassName) || parametersLoaderClassName == null) {
+			//this default should have a properties file override option
+			parametersLoaderClassName = "org.owasp.esapi.reference.accesscontrol.policyloader.DynaBeanACRParameterLoader";
+		}
+		logger.info(Logger.EVENT_SUCCESS, "Parameters Loader:" + parametersLoaderClassName);
+		ACRParameterLoader acrParamaterLoader = 
+			(ACRParameterLoader)
+			Class.forName(parametersLoaderClassName).newInstance();
+		return acrParamaterLoader.getParameters(config, currentRule);		
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/DynaBeanACRParameterLoader.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/DynaBeanACRParameterLoader.java.svn-base
new file mode 100644
index 0000000..acf7c10
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/DynaBeanACRParameterLoader.java.svn-base
@@ -0,0 +1,30 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.apache.commons.configuration.XMLConfiguration;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.reference.accesscontrol.DynaBeanACRParameter;
+
+import static org.owasp.esapi.reference.accesscontrol.policyloader.ACRParameterLoaderHelper.getParameterValue;
+
+final public class DynaBeanACRParameterLoader  
+	implements ACRParameterLoader<DynaBeanACRParameter> {
+	
+	Logger logger = ESAPI.getLogger(this.getClass());
+	
+//	@Override
+	public DynaBeanACRParameter getParameters(XMLConfiguration config, int currentRule) throws java.lang.Exception { //TODO reduce the exception
+		DynaBeanACRParameter policyParameter = new DynaBeanACRParameter();
+		int numberOfParameters = config.getList("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter[@name]").size();
+		for(int currentParameter = 0; currentParameter < numberOfParameters; currentParameter++) {
+			String parameterName = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter(" + currentParameter + ")[@name]");
+			String parameterType = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter(" + currentParameter + ")[@type]");
+			Object parameterValue = getParameterValue(config, currentRule, currentParameter, parameterType);
+			policyParameter.set(parameterName, parameterValue);
+		}
+		policyParameter.lock(); //This line makes the policyParameter read only. 
+		logger.info(Logger.SECURITY_SUCCESS, "Loaded " + numberOfParameters + 
+				" parameters: " + policyParameter.toString());
+		return policyParameter;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/EchoDynaBeanPolicyParameterACR.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/EchoDynaBeanPolicyParameterACR.java.svn-base
new file mode 100644
index 0000000..ad35cef
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/EchoDynaBeanPolicyParameterACR.java.svn-base
@@ -0,0 +1,15 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.owasp.esapi.reference.accesscontrol.BaseACR;
+import org.owasp.esapi.reference.accesscontrol.DynaBeanACRParameter;
+
+//public class EchoDynaBeanPolicyParameterACR extends BaseDynaBeanACR {
+public class EchoDynaBeanPolicyParameterACR extends BaseACR<DynaBeanACRParameter, Object> {
+	/**
+	 * Returns true if runtimeParameter is a Boolean true.
+	 * throws ClassCastException if runtimeParameter is not a Boolean.
+	 */
+	public boolean isAuthorized(Object runtimeParameter) throws ClassCastException{		
+		return getPolicyParameters().getBoolean("isTrue");
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/PolicyDTO.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/PolicyDTO.java.svn-base
new file mode 100644
index 0000000..78b5f61
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/PolicyDTO.java.svn-base
@@ -0,0 +1,55 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.owasp.esapi.AccessControlRule;
+import org.owasp.esapi.errors.AccessControlException;
+
+/**
+ * The point of the loaders is to create this
+ * @author Mike H. Fauzy
+ *
+ */
+final public class PolicyDTO {
+	private Map accessControlRules;
+
+	public PolicyDTO() {
+		this.accessControlRules = new HashMap();
+	}
+	
+	public Map getAccessControlRules() {
+		return accessControlRules;
+	}
+
+	public void addAccessControlRule(String key, String accessControlRuleClassName,
+			Object policyParameter) throws AccessControlException {
+		if (accessControlRules.get(key) != null) {
+			throw new AccessControlException("Duplicate keys are not allowed. "
+					+ "Key: " + key, "");
+		}
+		Constructor accessControlRuleConstructor;
+		try {
+			
+			
+			Class accessControlRuleClass = Class.forName(accessControlRuleClassName, false, this.getClass().getClassLoader());
+			accessControlRuleConstructor = accessControlRuleClass
+					.getConstructor();
+			AccessControlRule accessControlRule = 
+				(AccessControlRule) accessControlRuleConstructor
+					.newInstance();
+			accessControlRule.setPolicyParameters(policyParameter);
+			accessControlRules.put(key, accessControlRule);
+		} catch (Exception e) {
+			throw new AccessControlException(
+					"Unable to create Access Control Rule for key: \"" + key
+							+ "\" with policyParameters: \"" + policyParameter + "\"",
+					"", 
+					e);
+		}
+	}
+	public String toString() {
+		return accessControlRules.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/PolicyParameters.java.svn-base b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/PolicyParameters.java.svn-base
new file mode 100644
index 0000000..0bcbe85
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/PolicyParameters.java.svn-base
@@ -0,0 +1,42 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+public interface PolicyParameters {
+
+	/**
+	 * Follows the contract for java.util.Map;
+	 * @param key
+	 * @return
+	 * @see java.util.Map
+	 */
+	public abstract Object get(String key);
+
+	/**
+	 * This works just like a Map, except it will throw an exception if lock()
+	 * has been called. 
+	 * @param key
+	 * @param value
+	 * @throws IllegalArgumentException if this DynaBeanACRParameter instance 
+	 * has already been locked.
+	 */
+	public abstract void set(String key, Object value)
+			throws IllegalArgumentException;
+
+	/**
+	 * This is a convenience method for developers that prefer to think of this
+	 * as a map instead of being bean-like. 
+	 * 
+	 * @see set(String, Object)
+	 */
+	public abstract void put(String key, Object value)
+			throws IllegalArgumentException;
+
+	/**
+	 * This makes the map itself read only, but the mutability of objects 
+	 * that this map contains is not affected. Specifically, properties 
+	 * cannot be added or removed and the reference cannot be changed to 
+	 * a different object, but this does not change whether the values that the 
+	 * object contains can be changed.
+	 */
+	public abstract void lock();
+	
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoader.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoader.java
new file mode 100644
index 0000000..7de31d9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoader.java
@@ -0,0 +1,9 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+
+public interface ACRParameterLoader <T> {
+	public abstract T getParameters(XMLConfiguration config, int currentRule)
+		throws java.lang.Exception; //TODO this exception could be more specific
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoaderHelper.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoaderHelper.java
new file mode 100644
index 0000000..720dcc3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRParameterLoaderHelper.java
@@ -0,0 +1,46 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+final public class ACRParameterLoaderHelper {
+	
+	public static Object getParameterValue(XMLConfiguration config, int currentRule, int currentParameter, String parameterType) throws Exception {
+		String key = "AccessControlRules.AccessControlRule(" + 
+			currentRule + ").Parameters.Parameter(" + currentParameter + ")[@value]";
+		Object parameterValue;
+		if("String".equalsIgnoreCase(parameterType)) {
+			parameterValue = config.getString(key);
+		} else if("StringArray".equalsIgnoreCase(parameterType)) {
+			parameterValue = config.getStringArray(key);
+		} else if("Boolean".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getBoolean(key);
+		} else if("Byte".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getByte(key);
+		} else if("Int".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getInt(key);
+		} else if("Long".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getLong(key);
+		} else if("Float".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getFloat(key);
+		} else if("Double".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getDouble(key);
+		} else if("BigDecimal".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getBigDecimal(key);
+		} else if("BigInteger".equalsIgnoreCase(parameterType)){ 
+			parameterValue = config.getBigInteger(key);
+		} else if("Date".equalsIgnoreCase(parameterType)){
+			parameterValue = java.text.DateFormat.getDateInstance().parse(config.getString(key));
+		} else if("Time".equalsIgnoreCase(parameterType)){
+			java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("h:mm a");
+			parameterValue = sdf.parseObject(config.getString(key)); 
+//			parameterValue = java.text.DateFormat.getTimeInstance().parse(config.getString(key));
+		}		
+		//add timestamp. check for other stuff.
+		else {
+			throw new IllegalArgumentException("Unable to load the key \"" + key 
+					+ "\", because " + "the type \"" + parameterType + 
+					"\" was not recognized." );
+		}
+		return parameterValue;
+	}	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoader.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoader.java
new file mode 100644
index 0000000..d0ce361
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoader.java
@@ -0,0 +1,99 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AccessControlException;
+
+final public class ACRPolicyFileLoader {
+	protected final Logger logger = ESAPI.getLogger("ACRPolicyFileLoader");
+	
+	public PolicyDTO load() throws AccessControlException {
+		PolicyDTO policyDTO = new PolicyDTO();
+		XMLConfiguration config;
+		File file = ESAPI.securityConfiguration().getResourceFile("ESAPI-AccessControlPolicy.xml"); 
+		try
+		{
+		    config = new XMLConfiguration(file);		    
+		}
+		catch(ConfigurationException cex)
+		{
+			if(file == null) {
+				throw new AccessControlException("Unable to load configuration file for the following: " + "ESAPI-AccessControlPolicy.xml", "", cex);
+			}
+		    throw new AccessControlException("Unable to load configuration file from the following location: " + file.getAbsolutePath(), "", cex);
+		} 
+
+		Object property = config.getProperty("AccessControlRules.AccessControlRule[@name]");
+		logger.info(Logger.EVENT_SUCCESS, "Loading Property: " + property);
+		int numberOfRules = 0;
+		if(property instanceof Collection) {
+			numberOfRules = ((Collection)property).size();
+		} //implied else property == null -> return new PolicyDTO
+				 
+		String ruleName = "";
+		String ruleClass = "";
+		Object rulePolicyParameter = null;
+		int currentRule = 0;
+	    try {	    	
+	    	logger.info(Logger.EVENT_SUCCESS, "Number of rules: " + numberOfRules);
+			for(currentRule = 0; currentRule < numberOfRules; currentRule++) {
+				logger.trace(Logger.EVENT_SUCCESS, "----");
+				ruleName = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ")[@name]");
+				logger.trace(Logger.EVENT_SUCCESS, "Rule name: " + ruleName);
+				ruleClass = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ")[@class]");
+				logger.trace(Logger.EVENT_SUCCESS, "Rule Class: " + ruleClass);
+				rulePolicyParameter = getPolicyParameter(config, currentRule);
+				logger.trace(Logger.EVENT_SUCCESS, "rulePolicyParameters: " + rulePolicyParameter);
+				policyDTO.addAccessControlRule(
+						ruleName,
+						ruleClass,
+						rulePolicyParameter);		    	
+			}
+			logger.info(Logger.EVENT_SUCCESS, "policyDTO loaded: " + policyDTO);
+		} catch (Exception e) {
+			throw new AccessControlException("Unable to load AccessControlRule parameter. " + 
+					" Rule number: " + currentRule + 
+					" Probably: Rule.name: " + ruleName +
+					" Probably: Rule.class: " + ruleClass +
+					e.getMessage(), "", e);
+		}
+		return policyDTO;
+	}
+
+	protected Object getPolicyParameter(XMLConfiguration config, int currentRule)
+		throws ClassNotFoundException, IllegalAccessException, InstantiationException, Exception {
+		//If there aren't any properties: short circuit and return null.
+//		Properties tempParameters = config.getProperties("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter[@name]");
+		Object property = config.getProperty("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter[@name]");
+		if(property == null) {
+			return null;
+		}
+		
+		int numberOfProperties = 0;		
+		if(property instanceof Collection) {
+			numberOfProperties = ((Collection)property).size(); 
+		} else {
+			numberOfProperties = 1;
+		}
+		logger.info(Logger.EVENT_SUCCESS, "Number of properties: " + numberOfProperties);
+		
+		if(numberOfProperties < 1) {
+			return null;
+		}
+		String parametersLoaderClassName = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters[@parametersLoader]");
+		if("".equals(parametersLoaderClassName) || parametersLoaderClassName == null) {
+			//this default should have a properties file override option
+			parametersLoaderClassName = "org.owasp.esapi.reference.accesscontrol.policyloader.DynaBeanACRParameterLoader";
+		}
+		logger.info(Logger.EVENT_SUCCESS, "Parameters Loader:" + parametersLoaderClassName);
+		ACRParameterLoader acrParamaterLoader = 
+			(ACRParameterLoader)
+			Class.forName(parametersLoaderClassName).newInstance();
+		return acrParamaterLoader.getParameters(config, currentRule);		
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/DynaBeanACRParameterLoader.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/DynaBeanACRParameterLoader.java
new file mode 100644
index 0000000..acf7c10
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/DynaBeanACRParameterLoader.java
@@ -0,0 +1,30 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.apache.commons.configuration.XMLConfiguration;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.reference.accesscontrol.DynaBeanACRParameter;
+
+import static org.owasp.esapi.reference.accesscontrol.policyloader.ACRParameterLoaderHelper.getParameterValue;
+
+final public class DynaBeanACRParameterLoader  
+	implements ACRParameterLoader<DynaBeanACRParameter> {
+	
+	Logger logger = ESAPI.getLogger(this.getClass());
+	
+//	@Override
+	public DynaBeanACRParameter getParameters(XMLConfiguration config, int currentRule) throws java.lang.Exception { //TODO reduce the exception
+		DynaBeanACRParameter policyParameter = new DynaBeanACRParameter();
+		int numberOfParameters = config.getList("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter[@name]").size();
+		for(int currentParameter = 0; currentParameter < numberOfParameters; currentParameter++) {
+			String parameterName = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter(" + currentParameter + ")[@name]");
+			String parameterType = config.getString("AccessControlRules.AccessControlRule(" + currentRule + ").Parameters.Parameter(" + currentParameter + ")[@type]");
+			Object parameterValue = getParameterValue(config, currentRule, currentParameter, parameterType);
+			policyParameter.set(parameterName, parameterValue);
+		}
+		policyParameter.lock(); //This line makes the policyParameter read only. 
+		logger.info(Logger.SECURITY_SUCCESS, "Loaded " + numberOfParameters + 
+				" parameters: " + policyParameter.toString());
+		return policyParameter;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/EchoDynaBeanPolicyParameterACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/EchoDynaBeanPolicyParameterACR.java
new file mode 100644
index 0000000..ad35cef
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/EchoDynaBeanPolicyParameterACR.java
@@ -0,0 +1,15 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import org.owasp.esapi.reference.accesscontrol.BaseACR;
+import org.owasp.esapi.reference.accesscontrol.DynaBeanACRParameter;
+
+//public class EchoDynaBeanPolicyParameterACR extends BaseDynaBeanACR {
+public class EchoDynaBeanPolicyParameterACR extends BaseACR<DynaBeanACRParameter, Object> {
+	/**
+	 * Returns true if runtimeParameter is a Boolean true.
+	 * throws ClassCastException if runtimeParameter is not a Boolean.
+	 */
+	public boolean isAuthorized(Object runtimeParameter) throws ClassCastException{		
+		return getPolicyParameters().getBoolean("isTrue");
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyDTO.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyDTO.java
new file mode 100644
index 0000000..78b5f61
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyDTO.java
@@ -0,0 +1,55 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.owasp.esapi.AccessControlRule;
+import org.owasp.esapi.errors.AccessControlException;
+
+/**
+ * The point of the loaders is to create this
+ * @author Mike H. Fauzy
+ *
+ */
+final public class PolicyDTO {
+	private Map accessControlRules;
+
+	public PolicyDTO() {
+		this.accessControlRules = new HashMap();
+	}
+	
+	public Map getAccessControlRules() {
+		return accessControlRules;
+	}
+
+	public void addAccessControlRule(String key, String accessControlRuleClassName,
+			Object policyParameter) throws AccessControlException {
+		if (accessControlRules.get(key) != null) {
+			throw new AccessControlException("Duplicate keys are not allowed. "
+					+ "Key: " + key, "");
+		}
+		Constructor accessControlRuleConstructor;
+		try {
+			
+			
+			Class accessControlRuleClass = Class.forName(accessControlRuleClassName, false, this.getClass().getClassLoader());
+			accessControlRuleConstructor = accessControlRuleClass
+					.getConstructor();
+			AccessControlRule accessControlRule = 
+				(AccessControlRule) accessControlRuleConstructor
+					.newInstance();
+			accessControlRule.setPolicyParameters(policyParameter);
+			accessControlRules.put(key, accessControlRule);
+		} catch (Exception e) {
+			throw new AccessControlException(
+					"Unable to create Access Control Rule for key: \"" + key
+							+ "\" with policyParameters: \"" + policyParameter + "\"",
+					"", 
+					e);
+		}
+	}
+	public String toString() {
+		return accessControlRules.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyParameters.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyParameters.java
new file mode 100644
index 0000000..0bcbe85
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/policyloader/PolicyParameters.java
@@ -0,0 +1,42 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+public interface PolicyParameters {
+
+	/**
+	 * Follows the contract for java.util.Map;
+	 * @param key
+	 * @return
+	 * @see java.util.Map
+	 */
+	public abstract Object get(String key);
+
+	/**
+	 * This works just like a Map, except it will throw an exception if lock()
+	 * has been called. 
+	 * @param key
+	 * @param value
+	 * @throws IllegalArgumentException if this DynaBeanACRParameter instance 
+	 * has already been locked.
+	 */
+	public abstract void set(String key, Object value)
+			throws IllegalArgumentException;
+
+	/**
+	 * This is a convenience method for developers that prefer to think of this
+	 * as a map instead of being bean-like. 
+	 * 
+	 * @see set(String, Object)
+	 */
+	public abstract void put(String key, Object value)
+			throws IllegalArgumentException;
+
+	/**
+	 * This makes the map itself read only, but the mutability of objects 
+	 * that this map contains is not affected. Specifically, properties 
+	 * cannot be added or removed and the reference cannot be changed to 
+	 * a different object, but this does not change whether the values that the 
+	 * object contains can be changed.
+	 */
+	public abstract void lock();
+	
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/all-wcprops b/src/main/java/org/owasp/esapi/reference/crypto/.svn/all-wcprops
new file mode 100644
index 0000000..7979836
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/crypto
+END
+EncryptedPropertiesUtils.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtils.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/crypto/package.html
+END
+DefaultEncryptedProperties.java
+K 25
+svn:wc:ra_dav:version-url
+V 114
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/crypto/DefaultEncryptedProperties.java
+END
+JavaEncryptor.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java
+END
+ReferenceEncryptedProperties.java
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java
+END
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/entries b/src/main/java/org/owasp/esapi/reference/crypto/.svn/entries
new file mode 100644
index 0000000..3ef6fff
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/entries
@@ -0,0 +1,198 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/crypto
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:43:12.271347Z
+1893
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+JavaEncryptor.java
+file
+
+
+
+
+2014-02-18T16:19:52.793963Z
+ed2954ed5e6973300fe747b5f9f68716
+2013-08-31T22:43:12.271347Z
+1893
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+53481
+

+ReferenceEncryptedProperties.java
+file
+
+
+
+
+2014-02-18T16:19:52.793963Z
+f632ff80a5faf024100beded7734bebe
+2012-07-22T00:28:32.768860Z
+1872
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10042
+

+EncryptedPropertiesUtils.java
+file
+
+
+
+
+2014-02-18T16:19:52.793963Z
+8f88d86fbcc6213c9478b4e05fb47bee
+2010-10-14T22:58:11.369098Z
+1566
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6567
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.793963Z
+d102c56d45f40c8bab4edd52fdcdefcb
+2010-02-04T03:27:06.114285Z
+1079
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+356
+

+DefaultEncryptedProperties.java
+file
+
+
+
+
+2014-02-18T16:19:52.793963Z
+348ebb93ff1572a5f331e1f6579a0fba
+2012-07-22T00:22:08.141609Z
+1871
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7860
+

diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/prop-base/package.html.svn-base b/src/main/java/org/owasp/esapi/reference/crypto/.svn/prop-base/package.html.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/prop-base/package.html.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/DefaultEncryptedProperties.java.svn-base b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/DefaultEncryptedProperties.java.svn-base
new file mode 100644
index 0000000..4f6d1a3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/DefaultEncryptedProperties.java.svn-base
@@ -0,0 +1,214 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Reference implementation of the {@code EncryptedProperties} interface. This
+ * implementation wraps a normal properties file, and creates surrogates for the
+ * {@code getProperty} and {@code setProperty} methods that perform encryption
+ * and decryption based on {@code Encryptor}.
+ * <p>
+ * A very simple main program is provided that can be used to create an
+ * encrypted properties file. A better approach would be to allow unencrypted
+ * properties in the file and to encrypt them the first time the file is
+ * accessed.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @since June 1, 2007
+ * @see org.owasp.esapi.EncryptedProperties
+ * @see org.owasp.esapi.reference.crypto.ReferenceEncryptedProperties
+ */
+public class DefaultEncryptedProperties implements org.owasp.esapi.EncryptedProperties {
+
+	/** The properties. */
+	private final Properties properties = new Properties();
+
+	/** The logger. */
+	private final Logger logger = ESAPI.getLogger("EncryptedProperties");
+
+	/**
+	 * Instantiates a new encrypted properties.
+	 */
+	public DefaultEncryptedProperties() {
+		// hidden
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public synchronized String getProperty(String key) throws EncryptionException {
+	    String[] errorMsgs = new String[] {
+	            ": failed decoding from base64",
+	            ": failed to deserialize properly",
+	            ": failed to decrypt properly"
+	        };
+
+	    int progressMark = 0;
+	    try {
+	        String encryptedValue = properties.getProperty(key);
+
+	        if(encryptedValue==null)
+	            return null;
+
+	        progressMark = 0;
+	        byte[] serializedCiphertext   = ESAPI.encoder().decodeFromBase64(encryptedValue);
+	        progressMark++;
+	        CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(serializedCiphertext);
+	        progressMark++;
+	        PlainText plaintext           = ESAPI.encryptor().decrypt(restoredCipherText);
+	        
+	        return plaintext.toString();
+	    } catch (Exception e) {
+	        throw new EncryptionException("Property retrieval failure",
+	                                      "Couldn't retrieve encrypted property for property " + key +
+	                                      errorMsgs[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public synchronized String setProperty(String key, String value) throws EncryptionException {
+	    String[] errorMsgs = new String[] {
+	            ": failed to encrypt properly",
+	            ": failed to serialize correctly",
+	            ": failed to base64-encode properly",
+	            ": failed to set base64-encoded value as property. Illegal key name?"
+	    };
+
+	    int progressMark = 0;
+	    try {
+	        if ( key == null ) {
+	            throw new NullPointerException("Property name may not be null.");
+	        }
+	        if ( value == null ) {
+	            throw new NullPointerException("Property value may not be null.");
+	        }
+	        // NOTE: Not backward compatible w/ ESAPI 1.4.
+	        PlainText pt = new PlainText(value);
+	        CipherText ct = ESAPI.encryptor().encrypt(pt);
+	        progressMark++;
+	        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+	        progressMark++;
+	        String b64str = ESAPI.encoder().encodeForBase64(serializedCiphertext, false);
+	        progressMark++;
+	        String encryptedValue = (String)properties.setProperty(key, b64str);
+	        progressMark++;
+	        return encryptedValue;
+	    } catch (Exception e) {
+	        throw new EncryptionException("Property setting failure",
+	                                      "Couldn't set encrypted property " + key +
+	                                      errorMsgs[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Set<?> keySet() {
+		return properties.keySet();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void load(InputStream in) throws IOException {
+		properties.load(in);
+		logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void store(OutputStream out, String comments) throws IOException {
+		properties.store(out, comments);
+	}
+
+	/**
+	 * Loads encrypted properties file based on the location passed in args then prompts the 
+	 * user to input key-value pairs.  When the user enters a null or blank key, the values 
+	 * are stored to the properties file.
+	 * 
+	 * @param args
+	 *            the location of the properties file to load and write to
+	 * 
+	 * @throws Exception
+	 *             Any exception thrown
+	 * @deprecated Use {@code EncryptedPropertiesUtils} instead, which allows creating, reading,
+	 *			   and writing encrypted properties.
+	 */
+	public static void main(String[] args) throws Exception {
+		File f = new File(args[0]);
+		ESAPI.getLogger( "EncryptedProperties.main" ).debug(Logger.SECURITY_SUCCESS, "Loading encrypted properties from " + f.getAbsolutePath() );
+		if ( !f.exists() ) throw new IOException( "Properties file not found: " + f.getAbsolutePath() );
+		ESAPI.getLogger( "EncryptedProperties.main" ).debug(Logger.SECURITY_SUCCESS, "Encrypted properties found in " + f.getAbsolutePath() );
+		DefaultEncryptedProperties ep = new DefaultEncryptedProperties();
+
+		FileInputStream in = null;
+		FileOutputStream out = null;
+		try {
+    		in = new FileInputStream(f);
+            out = new FileOutputStream(f);
+
+            ep.load(in);   
+    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+    		String key = null;
+    		do {
+    			System.out.print("Enter key: ");
+    			key = br.readLine();
+    			System.out.print("Enter value: ");
+    			String value = br.readLine();
+    			if (key != null && key.length() > 0 && value != null && value.length() > 0) {
+    				ep.setProperty(key, value);
+    			}
+    		} while (key != null && key.length() > 0);
+    		ep.store(out, "Encrypted Properties File");
+		} finally {
+		    // FindBugs and PMD both complain about these next lines, that they may
+		    // ignore thrown exceptions. Really!!! That's the whole point.
+    		try { if ( in != null ) in.close(); } catch( Exception e ) {}
+    		try { if ( out != null ) out.close(); } catch( Exception e ) {}
+		}
+		
+		Iterator<?> i = ep.keySet().iterator();
+		while (i.hasNext()) {
+			String k = (String) i.next();
+			String value = ep.getProperty(k);
+			System.out.println("   " + k + "=" + value);
+		}
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesUtils.java.svn-base b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesUtils.java.svn-base
new file mode 100644
index 0000000..cb1d512
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesUtils.java.svn-base
@@ -0,0 +1,212 @@
+package org.owasp.esapi.reference.crypto;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import org.owasp.esapi.EncryptedProperties;
+
+/**
+ * Command line utilities for reading, writing and creating encrypted properties files.
+ * <p>
+ * Usage:<br/>
+ * <code>
+ *    java org.owasp.esapi.reference.crypto.EncryptedPropertiesUtils [--in file] [--out file] 
+ *			[--in-encrypted true|false] [--verbose true|false]
+ * </code>
+ * <p>
+ * Command line parameters:<br/>
+ * <ul>
+ * <li><b>--in</b> (Optional) Encrypted or plaintext file to read from. If no input file is specified, a new properties file will be created.</li>
+ * <li><b>--out</b> (Optional) Encrypted file to output to. Default: Overwrite input file</li>
+ * <li><b>--in-encrypted</b> (Optional) True if the input file is encrypted. Default: true</li>
+ * <li><b>--verbose</b> (Optional) If true, output (potentially unencrypted) information to the terminal. Default: false</li>
+ * </ul>
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ * @see org.owasp.esapi.EncryptedProperties
+ */
+public class EncryptedPropertiesUtils {
+
+	/**
+	 * Loads encrypted or plaintext properties file based on the location passed in args
+	 * then prompts the user to input key-value pairs.  When the user enters a null or
+	 * blank key, the values are stored to the properties file.
+	 *
+	 * @throws Exception Any exception thrown
+	 */
+	public static void main(String[] args) throws Exception {
+
+		//command line options
+		String inFile = null;
+		String outFile = null;
+		boolean inFileEncrypted = true;
+		boolean verbose = false;
+
+		//parse command line params
+		for (int i = 0; i < args.length; i = i + 2) {
+			String paramType = args[i];
+
+			if ("--in".equals(paramType) && args.length >= i + 1) {
+				inFile = args[i + 1];
+
+			} else if ("--out".equals(paramType) && args.length >= i + 1) {
+				outFile = args[i + 1];
+
+			} else if ("--in-encrypted".equals(paramType) && args.length >= i + 1) {
+				inFileEncrypted = Boolean.valueOf(args[i + 1]);
+
+			} else if ("--verbose".equals(paramType) && args.length >= i + 1) {
+				verbose = Boolean.valueOf(args[i + 1]);
+			}
+
+		}
+
+		if (outFile == null) {
+			outFile = inFile; //if no output file is specified we will overwrite the input file
+		}
+		if (outFile == null) {
+			//no input or output file specified. Can't continue.
+			System.out.println("You must specify an input file or output file");
+			System.exit(1);
+		}
+
+		//load in existing properties from a file
+		Properties props = loadProperties(inFile, inFileEncrypted);
+
+		//read user input and add keys and values to the property file
+		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+		String key = null;
+		do {
+			System.out.print("Enter key: ");
+			key = br.readLine();
+
+			if (props.containsKey(key)) {
+				System.out.print("Key already exists. Replace? ");
+				String confirm = br.readLine();
+				if (!("y".equals(confirm) || "yes".equals(confirm))) {
+					continue;
+				}
+			}
+
+			System.out.print("Enter value: ");
+			String value = br.readLine();
+
+			addProperty(props, key, value);
+
+		} while (key != null && key.length() > 0);
+
+		//save output file
+		storeProperties(outFile, props,
+				"Encrypted Properties File generated by org.owasp.esapi.reference.crypto.EncryptedPropertiesUtils");
+
+		System.out.println("Encrypted Properties file output to " + outFile);
+
+		if (verbose) {
+			for (Object oKey : props.keySet()) {
+				String sKey = (String) oKey;
+				String value = props.getProperty(sKey);
+				System.out.println("   " + sKey + "=" + value);
+			}
+		}
+
+	}
+
+	/**
+	 * Loads a Properties file from a filename. If the filename is unspecified
+	 * or the file could not be found, a new Properties is returned.
+	 *
+	 * @param inFile Filename to load Properties from.
+	 * @param inFileEncrypted If true, the input file is assumed to be already encrypted. Default true.
+	 * @return Either the loaded Properties object or a new one if the file could not be found.
+	 * @throws IOException
+	 */
+	public static Properties loadProperties(String inFile, Boolean inFileEncrypted) throws IOException {
+
+		if (inFileEncrypted == null) inFileEncrypted = true;
+
+		Properties props;
+
+		if (inFile != null) {
+
+			File f = new File(inFile);
+			if (!f.exists()) {
+				System.out.println("Input properties file not found. Creating new.");
+				props = new ReferenceEncryptedProperties();
+
+			} else {
+				String encrypted = inFileEncrypted ? "Encrypted" : "Plaintext";
+				System.out.println(encrypted + " properties found in " + f.getAbsolutePath());
+
+				Properties inProperties;
+				if (inFileEncrypted) {
+					inProperties = new ReferenceEncryptedProperties();
+				} else {
+					inProperties = new Properties();
+				}
+
+				InputStream in = null;
+				try {
+					in = new FileInputStream(f);
+					inProperties.load(in);
+				} finally {
+					try {
+						if (in != null) in.close(); //quietly close the InputStream
+					} catch (Exception e) {}
+				}
+
+				//Use the existing properties
+				props = new ReferenceEncryptedProperties(inProperties);
+			}
+		} else {
+			System.out.println("Input properties file not found. Creating new.");
+			props = new ReferenceEncryptedProperties();
+		}
+
+		return props;
+	}
+
+	/**
+	 * Stores a Properties object to a file.
+	 *
+	 * @param outFile Filename to store to
+	 * @param props Properties to store
+	 * @param message A message to add to the comments in the stored file
+	 * @throws Exception
+	 */
+	public static void storeProperties(String outFile, Properties props, String message) throws Exception {
+		OutputStream out = null;
+		try {
+			out = new FileOutputStream(new File(outFile));
+			props.store(out, message);
+		} finally {
+			try {
+				if (out != null) out.close();  //quietly close OutputStream
+			} catch (Exception e) {}
+		}
+	}
+
+	/**
+	 * Adds a new key-value property to the passed Properties object
+	 *
+	 * @param props The Properties object to add to
+	 * @param key The key to add
+	 * @param value The value to set
+	 * @return The previous value of the property, or null if it is newly added.
+	 */
+	public static Object addProperty(Properties props, String key, String value) {
+		if (props != null && key != null && key.length() > 0 && value != null && value.length() > 0) {
+			return props.setProperty(key, value);
+		}
+		return null;
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/JavaEncryptor.java.svn-base b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/JavaEncryptor.java.svn-base
new file mode 100644
index 0000000..a20c612
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/JavaEncryptor.java.svn-base
@@ -0,0 +1,1050 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+// import javax.crypto.Mac;			// Uncomment if computeHMAC() is included.
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherSpec;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.crypto.KeyDerivationFunction;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.crypto.SecurityProviderLoader;
+import org.owasp.esapi.errors.ConfigurationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.reference.DefaultSecurityConfiguration;
+
+/**
+ * Reference implementation of the {@code Encryptor} interface. This implementation
+ * layers on the JCE provided cryptographic package. Algorithms used are
+ * configurable in the {@code ESAPI.properties} file. The main property
+ * controlling the selection of this class is {@code ESAPI.Encryptor}. Most of
+ * the other encryption related properties have property names that start with
+ * the string "Encryptor.".
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com)
+ * @since June 1, 2007; some methods since ESAPI Java 2.0
+ * @see org.owasp.esapi.Encryptor
+ */
+public final class JavaEncryptor implements Encryptor {
+    private static volatile Encryptor singletonInstance;
+
+    // Note: This double-check pattern only works because singletonInstance
+    //       is declared to be volatile.  Usually this method is called
+    //       via ESAPI.encryptor() rather than directly.
+    public static Encryptor getInstance() throws EncryptionException {
+        if ( singletonInstance == null ) {
+            synchronized ( JavaEncryptor.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new JavaEncryptor();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    private static boolean initialized = false;
+    
+    // encryption
+    private static SecretKeySpec secretKeySpec = null; // DISCUSS: Why static? Implies one key?!?
+    private static String encryptAlgorithm = "AES";
+    private static String encoding = "UTF-8"; 
+    private static int encryptionKeyLength = 128;
+    
+    // digital signatures
+    private static PrivateKey privateKey = null;
+	private static PublicKey publicKey = null;
+	private static String signatureAlgorithm = "SHA1withDSA";
+    private static String randomAlgorithm = "SHA1PRNG";
+	private static int signatureKeyLength = 1024;
+	
+	// hashing
+	private static String hashAlgorithm = "SHA-512";
+	private static int hashIterations = 1024;
+	
+	// Logging - DISCUSS: This "sticks" us with a specific logger to whatever it was when
+	//					  this class is first loaded. Is this a big limitation? Since there
+	//                    is no method to reset it, we may has well make it 'final' also.
+	private static Logger logger = ESAPI.getLogger("JavaEncryptor");
+	    // Used to print out warnings about deprecated methods.
+	private static int encryptCounter = 0;
+	private static int decryptCounter = 0;
+        // DISCUSS: OK to not have a property for this to set the frequency?
+        //          The desire is to persuade people to move away from these
+	    //          two deprecated encrypt(String) / decrypt(String) methods,
+        //          so perhaps the annoyance factor of not being able to
+        //          change it will help. For now, it is just hard-coded here.
+        //          We could be mean and just print a warning *every* time.
+	private static final int logEveryNthUse = 25;
+	
+    // *Only* use this string for user messages for EncryptionException when
+    // decryption fails. This is to prevent information leakage that may be
+    // valuable in various forms of ciphertext attacks, such as the
+	// Padded Oracle attack described by Rizzo and Duong.
+    private static final String DECRYPTION_FAILED =
+        "Decryption failed; see logs for details.";
+
+    // # of seconds that all failed decryption attempts will take. Used to
+    // help prevent side-channel timing attacks.
+    private static int N_SECS = 2;
+
+	// Load the preferred JCE provider if one has been specified.
+	static {
+	    try {
+            SecurityProviderLoader.loadESAPIPreferredJCEProvider();
+        } catch (NoSuchProviderException ex) {
+        	// Note that audit logging is done elsewhere in called method.
+            logger.fatal(Logger.SECURITY_FAILURE,
+                         "JavaEncryptor failed to load preferred JCE provider.", ex);
+            throw new ExceptionInInitializerError(ex);
+        }
+        setupAlgorithms();
+	}
+	
+    /**
+     * Generates a new strongly random secret key and salt that can be
+     * copy and pasted in the <b>ESAPI.properties</b> file.
+     * 
+     * @param args Set first argument to "-print" to display available algorithms on standard output.
+     * @throws java.lang.Exception	To cover a multitude of sins, mostly in configuring ESAPI.properties.
+     */
+    public static void main( String[] args ) throws Exception {
+		System.out.println( "Generating a new secret master key" );
+		
+		// print out available ciphers
+		if ( args.length == 1 && args[0].equalsIgnoreCase("-print" ) ) {
+			System.out.println( "AVAILABLE ALGORITHMS" );
+
+			Provider[] providers = Security.getProviders();
+			TreeMap<String, String> tm = new TreeMap<String, String>();
+			// DISCUSS: Note: We go through multiple providers, yet nowhere do I
+			//			see where we print out the PROVIDER NAME. Not all providers
+			//			will implement the same algorithms and some "partner" with
+			//			whom we are exchanging different cryptographic messages may
+			//			have _different_ providers in their java.security file. So
+			//			it would be useful to know the provider name where each
+			//			algorithm is implemented. Might be good to prepend the provider
+			//			name to the 'key' with something like "providerName: ". Thoughts?
+			for (int i = 0; i != providers.length; i++) {
+				// DISCUSS: Print security provider name here???
+					// Note: For some odd reason, Provider.keySet() returns
+					//		 Set<Object> of the property keys (which are Strings)
+					//		 contained in this provider, but Set<String> seems
+					//		 more appropriate. But that's why we need the cast below.
+	            System.out.println("===== Provider " + i + ":" + providers[i].getName() + " ======");
+				Iterator<Object> it = providers[i].keySet().iterator();
+				while (it.hasNext()) {
+					String key = (String)it.next();
+		            String value = providers[i].getProperty( key );
+		            tm.put(key, value);
+	                System.out.println("\t\t   " + key + " -> "+ value );
+				}
+			}
+
+			Set< Entry<String,String> > keyValueSet = tm.entrySet();
+			Iterator<Entry<String, String>> it = keyValueSet.iterator();
+			while( it.hasNext() ) {
+				Map.Entry<String,String> entry = it.next();
+				String key = entry.getKey();
+				String value = entry.getValue();
+	        	System.out.println( "   " + key + " -> "+ value );
+			}
+		} else {
+				// Used to print a similar line to use '-print' even when it was specified.
+			System.out.println( "\tuse '-print' to also show available crypto algorithms from all the security providers" );
+		}
+		
+        // setup algorithms -- Each of these have defaults if not set, although
+		//					   someone could set them to something invalid. If
+		//					   so a suitable exception will be thrown and displayed.
+        encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm();
+		encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength();
+		randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm();
+
+		SecureRandom random = SecureRandom.getInstance(randomAlgorithm);
+		SecretKey secretKey = CryptoHelper.generateSecretKey(encryptAlgorithm, encryptionKeyLength);
+        byte[] raw = secretKey.getEncoded();
+        byte[] salt = new byte[20];	// Or 160-bits; big enough for SHA1, but not SHA-256 or SHA-512.
+        random.nextBytes( salt );
+        String eol = System.getProperty("line.separator", "\n"); // So it works on Windows too.
+        System.out.println( eol + "Copy and paste these lines into your ESAPI.properties" + eol);
+        System.out.println( "#==============================================================");
+        System.out.println( "Encryptor.MasterKey=" + ESAPI.encoder().encodeForBase64(raw, false) );
+        System.out.println( "Encryptor.MasterSalt=" + ESAPI.encoder().encodeForBase64(salt, false) );
+        System.out.println( "#==============================================================" + eol);
+    }
+	
+    
+    /**
+     * Private CTOR for {@code JavaEncryptor}, called by {@code getInstance()}.
+     * @throws EncryptionException if can't construct this object for some reason.
+     * 					Original exception will be attached as the 'cause'.
+     */
+    private JavaEncryptor() throws EncryptionException {
+        byte[] salt = ESAPI.securityConfiguration().getMasterSalt();
+        byte[] skey = ESAPI.securityConfiguration().getMasterKey();
+
+        assert salt != null : "Can't obtain master salt, Encryptor.MasterSalt";
+        assert salt.length >= 16 : "Encryptor.MasterSalt must be at least 16 bytes. " +
+                                   "Length is: " + salt.length + " bytes.";
+        assert skey != null : "Can't obtain master key, Encryptor.MasterKey";
+        assert skey.length >= 7 : "Encryptor.MasterKey must be at least 7 bytes. " +
+                                  "Length is: " + skey.length + " bytes.";
+        
+        // Set up secretKeySpec for use for symmetric encryption and decryption,
+        // and set up the public/private keys for asymmetric encryption /
+        // decryption.
+        // TODO: Note: If we dump ESAPI 1.4 crypto backward compatibility,
+        //       then we probably will ditch the Encryptor.EncryptionAlgorithm
+        //       property. If so, encryptAlgorithm should probably use
+        //       Encryptor.CipherTransformation and just pull off the cipher
+        //       algorithm name so we can use it here.
+        synchronized(JavaEncryptor.class) {
+            if ( ! initialized ) {
+                //
+                // For symmetric encryption
+                //
+                //      NOTE: FindBugs complains about this
+                //            (ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD) but
+                //            it should be OK since it is synchronized and only
+                //            done once. While we could separate this out and
+                //            handle in a static initializer, it just seems to
+                //            fit better here.
+                secretKeySpec = new SecretKeySpec(skey, encryptAlgorithm );
+                
+                //
+                // For asymmetric encryption (i.e., public/private key)
+                //
+                try {
+                    SecureRandom prng = SecureRandom.getInstance(randomAlgorithm);
+
+                    // Because hash() is not static (but it could be were in not
+                    // for the interface method specification in Encryptor), we
+                    // cannot do this initialization in a static method or static
+                    // initializer.
+                    byte[] seed = hash(new String(skey, encoding),new String(salt, encoding)).getBytes(encoding);
+                    prng.setSeed(seed);
+                    initKeyPair(prng);
+                } catch (Exception e) {
+                    throw new EncryptionException("Encryption failure", "Error creating Encryptor", e);
+                }             
+                
+                // Mark everything as initialized.
+                initialized = true;
+            }
+        }
+    }
+     
+
+
+	/**
+     * {@inheritDoc}
+     * 
+	 * Hashes the data with the supplied salt and the number of iterations specified in
+	 * the ESAPI SecurityConfiguration.
+	 */
+	public String hash(String plaintext, String salt) throws EncryptionException {
+		return hash( plaintext, salt, hashIterations );
+	}
+	
+	/**
+     * {@inheritDoc}
+     * 
+	 * Hashes the data using the specified algorithm and the Java MessageDigest class. This method
+	 * first adds the salt, a separator (":"), and the data, and then rehashes the specified number of iterations
+	 * in order to help strengthen weak passwords.
+	 */
+	public String hash(String plaintext, String salt, int iterations) throws EncryptionException {
+		byte[] bytes = null;
+		try {
+			MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);
+			digest.reset();
+			digest.update(ESAPI.securityConfiguration().getMasterSalt());
+			digest.update(salt.getBytes(encoding));
+			digest.update(plaintext.getBytes(encoding));
+
+			// rehash a number of times to help strengthen weak passwords
+			bytes = digest.digest();
+			for (int i = 0; i < iterations; i++) {
+				digest.reset();
+				bytes = digest.digest(bytes);
+			}
+			String encoded = ESAPI.encoder().encodeForBase64(bytes,false);
+			return encoded;
+		} catch (NoSuchAlgorithmException e) {
+			throw new EncryptionException("Internal error", "Can't find hash algorithm " + hashAlgorithm, e);
+		} catch (UnsupportedEncodingException ex) {
+			throw new EncryptionException("Internal error", "Can't find encoding for " + encoding, ex);
+		}
+	}
+
+	/**
+	* {@inheritDoc}
+	*/
+	 public CipherText encrypt(PlainText plaintext) throws EncryptionException {
+		 // Now more of a convenience function for using the master key.
+		 return encrypt(secretKeySpec, plaintext);
+	 }
+	 
+	 /**
+	  * {@inheritDoc}
+	  */
+	 public CipherText encrypt(SecretKey key, PlainText plain)
+	 			throws EncryptionException
+	 {
+		 if ( key == null ) {
+			 throw new IllegalArgumentException("(Master) encryption key arg may not be null. Is Encryptor.MasterKey set?");
+		 }
+		 if ( plain == null ) {
+			 throw new IllegalArgumentException("PlainText may arg not be null");
+		 }
+		 byte[] plaintext = plain.asBytes();
+		 boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText();
+
+		 boolean success = false;	// Used in 'finally' clause.
+		 String xform = null;
+		 int keySize = key.getEncoded().length * 8;	// Convert to # bits
+
+		try {
+			 xform = ESAPI.securityConfiguration().getCipherTransformation();
+             String[] parts = xform.split("/");
+             assert parts.length == 3 : "Malformed cipher transformation: " + xform;
+             String cipherMode = parts[1];
+             
+             // This way we can prevent modes like OFB and CFB where the IV should never
+             // be repeated with the same encryption key (at least until we support
+             // Encryptor.ChooseIVMethod=specified and allow us to specify some mechanism
+             // to ensure the IV will never be repeated (such as a time stamp or other
+             // monotonically increasing function).
+             // DISCUSS: Should we include the permitted cipher modes in the exception msg?
+             if ( ! CryptoHelper.isAllowedCipherMode(cipherMode) ) {
+                 throw new EncryptionException("Encryption failure: invalid cipher mode ( " + cipherMode + ") for encryption",
+                             "Encryption failure: Cipher transformation " + xform + " specifies invalid " +
+                             "cipher mode " + cipherMode);
+             }
+             
+			 // Note - Cipher is not thread-safe so we create one locally
+			 //        Also, we need to change this eventually so other algorithms can
+			 //        be supported. Eventually, there will be an encrypt() method that
+			 //        takes a (new class) CryptoControls, as something like this:
+			 //          public CipherText encrypt(CryptoControls ctrl, SecretKey skey, PlainText plaintext)
+			 //        and this method will just call that one.
+			 Cipher encrypter = Cipher.getInstance(xform);
+			 String cipherAlg = encrypter.getAlgorithm();
+			 int keyLen = ESAPI.securityConfiguration().getEncryptionKeyLength();
+
+			 // DISCUSS: OK, what do we want to do here if keyLen != keySize? If use keyLen, encryption
+			 //		     could fail with an exception, but perhaps that's what we want. Or we may just be
+			 //			 OK with silently using keySize as long as keySize >= keyLen, which then interprets
+			 //			 ESAPI.EncryptionKeyLength as the *minimum* key size, but as long as we have something
+			 //			 stronger it's OK to use it. For now, I am just going to log warning if different, but use
+			 //			 keySize unless keySize is SMALLER than ESAPI.EncryptionKeyLength, in which case I'm going
+			 //			 to log an error.
+			 //
+			 //			 IMPORTANT NOTE:	When we generate key sizes for both DES and DESede the result of
+			 //								SecretKey.getEncoding().length includes the TRUE key size (i.e.,
+			 //								*with* the even parity bits) rather than the EFFECTIVE key size
+			 //								(which incidentally is what KeyGenerator.init() expects for DES
+			 //								and DESede; duh! Nothing like being consistent). This leads to
+			 //								the following dilemma:
+			 //
+			 //													EFFECTIVE Key Size		TRUE Key Size
+			 //													(KeyGenerator.init())	(SecretKey.getEncoding().length)
+			 //									========================================================================
+			 //									For DES:			56 bits					64 bits
+			 //									For DESede:			112 bits / 168 bits		192 bits (always)
+			 //
+			 //								We are trying to automatically determine the key size from SecretKey
+			 //								based on 8 * SecretKey.getEncoding().length, but as you can see, the
+			 //								2 key 3DES and the 3 key 3DES both use the same key size (192 bits)
+			 //								regardless of what is passed to KeyGenerator.init(). There are no advertised
+			 //								methods to get the key size specified by the init() method so I'm not sure how
+			 //								this is actually working internally. However, it does present a problem if we
+			 //								wish to communicate the 3DES key size to a recipient for later decryption as
+			 //								they would not be able to distinguish 2 key 3DES from 3 key 3DES.
+			 //
+			 //								The only workaround I know is to pass the explicit key size down. However, if
+			 //								we are going to do that, I'd propose passing in a CipherSpec object so we could
+			 //								tell what cipher transformation to use as well instead of just the key size. Then
+			 //								we would extract keySize from the CipherSpec object of from the SecretKey object.
+			 //
+			 if ( keySize != keyLen ) {
+				 // DISCUSS: Technically this is not a security "failure" per se, but not really a "success" either.
+				 logger.warning(Logger.SECURITY_FAILURE, "Encryption key length mismatch. ESAPI.EncryptionKeyLength is " +
+						 keyLen + " bits, but length of actual encryption key is " + keySize +
+				 		" bits.  Did you remember to regenerate your master key (if that is what you are using)???");
+			 }
+			 // DISCUSS: Reconsider these warnings. If thousands of encryptions are done in tight loop, no one needs
+			 //          more than 1 warning. Should we do something more intelligent here?
+			 if ( keySize < keyLen ) {
+				 // ESAPI.EncryptionKeyLength defaults to 128, but that means that we could not use DES (as weak as it
+				 // is), even for legacy code. Therefore, this has been changed to simple log a warning rather than
+				 //	throw the following exception.
+				 //				 throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " +
+				 //						  "encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits.");
+				 logger.warning(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN specified " +
+						 "encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits with cipher algorithm " + cipherAlg);
+			 }
+			 if ( keySize < 112 ) {		// NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030.
+				 						// Note that 112 bits 'just happens' to be size of 2-key Triple DES!
+				 logger.warning(Logger.SECURITY_FAILURE, "Potentially unsecure encryption. Key size of " + keySize + "bits " +
+				                "not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " +
+				                "of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57.");
+			 }
+			 // Check if algorithm mentioned in SecretKey is same as that being used for Cipher object.
+			 // They should be the same. If they are different, things could fail. (E.g., DES and DESede
+			 // require keys with even parity. Even if key was sufficient size, if it didn't have the correct
+			 // parity it could fail.)
+			 //
+			 String skeyAlg = key.getAlgorithm();
+			 if ( !( cipherAlg.startsWith( skeyAlg + "/" ) || cipherAlg.equals( skeyAlg ) ) ) {
+				 // DISCUSS: Should we thrown a ConfigurationException here or just log a warning??? I'm game for
+				 //			 either, but personally I'd prefer the squeaky wheel to the annoying throwing of
+				 //			 a ConfigurationException (which is a RuntimeException). Less likely to upset
+				 //			 the development community.
+				 logger.warning(Logger.SECURITY_FAILURE, "Encryption mismatch between cipher algorithm (" +
+						 cipherAlg + ") and SecretKey algorithm (" + skeyAlg + "). Cipher will use algorithm " + cipherAlg);
+			 }
+
+			 byte[] ivBytes = null;
+			 CipherSpec cipherSpec = new CipherSpec(encrypter, keySize);	// Could pass the ACTUAL (intended) key size
+			 
+             // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then
+             // use the specified SecretKey as-is rather than computing a derived key from it. We also
+             // don't expect a separate MAC in the specified CipherText object so therefore don't try
+             // to validate it.
+             boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( cipherMode );
+             SecretKey encKey = null;
+			 if ( preferredCipherMode ) {
+			     encKey = key;
+			 } else {
+			     encKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(),
+			    		 				    key, keySize, "encryption");
+			 }
+			 
+			 if ( cipherSpec.requiresIV() ) {
+				 String ivType = ESAPI.securityConfiguration().getIVType();
+				 IvParameterSpec ivSpec = null;
+				 if ( ivType.equalsIgnoreCase("random") ) {
+					 ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize());
+				 } else if ( ivType.equalsIgnoreCase("fixed") ) {
+					 String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV();
+					 ivBytes = Hex.decode(fixedIVAsHex);
+					 /* FUTURE		 } else if ( ivType.equalsIgnoreCase("specified")) {
+					 		// FUTURE - TODO  - Create instance of specified class to use for IV generation and
+					 		//					 use it to create the ivBytes. (The intent is to make sure that
+					 		//				     1) IVs are never repeated for cipher modes like OFB and CFB, and
+					 		//					 2) to screen for weak IVs for the particular cipher algorithm.
+					 		//		In meantime, use 'random' for block cipher in feedback mode. Unlikely they will
+					 		//		be repeated unless you are salting SecureRandom with same value each time. Anything
+					 		//		monotonically increasing should be suitable, like a counter, but need to remember
+					 		//		it across JVM restarts. Was thinking of using System.currentTimeMillis(). While
+					 		//		it's not perfect it probably is good enough. Could even all (advanced) developers
+					 		//      to define their own class to create a unique IV to allow them some choice, but
+					 		//      definitely need to provide a safe, default implementation.
+					  */
+				 } else {
+					 // TODO: Update to add 'specified' once that is supported and added above.
+					 throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'");
+				 }
+				 ivSpec = new IvParameterSpec(ivBytes);
+				 cipherSpec.setIV(ivBytes);
+				 encrypter.init(Cipher.ENCRYPT_MODE, encKey, ivSpec);
+			 } else {
+				 encrypter.init(Cipher.ENCRYPT_MODE, encKey);
+			 }
+			 logger.debug(Logger.EVENT_SUCCESS, "Encrypting with " + cipherSpec);
+			 byte[] raw = encrypter.doFinal(plaintext);
+                 // Convert to CipherText.
+             CipherText ciphertext = new CipherText(cipherSpec, raw);
+			 
+			 // If we are using a "preferred" cipher mode--i.e., one that supports *both* confidentiality and
+			 // authenticity, there is no point to store a separate MAC in the CipherText object. Thus we only
+             // do this when we are not using such a cipher mode.
+			 if ( !preferredCipherMode ) {
+			     // Compute derived key, and then use it to compute and store separate MAC in CipherText object.
+			     SecretKey authKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(),
+			    		 							   key, keySize, "authenticity");
+			     ciphertext.computeAndStoreMAC(  authKey );
+			 }
+			 logger.debug(Logger.EVENT_SUCCESS, "JavaEncryptor.encrypt(SecretKey,byte[],boolean,boolean) -- success!");
+			 success = true;	// W00t!!!
+			 return ciphertext;
+		} catch (InvalidKeyException ike) {
+			 throw new EncryptionException("Encryption failure: Invalid key exception.",
+					 "Requested key size: " + keySize + "bits greater than 128 bits. Must install unlimited strength crypto extension from Sun: " +
+					 ike.getMessage(), ike);
+		 } catch (ConfigurationException cex) {
+			 throw new EncryptionException("Encryption failure: Configuration error. Details in log.", "Key size mismatch or unsupported IV method. " +
+					 "Check encryption key size vs. ESAPI.EncryptionKeyLength or Encryptor.ChooseIVMethod property.", cex);
+		 } catch (InvalidAlgorithmParameterException e) {
+			 throw new EncryptionException("Encryption failure (invalid IV)",
+					 "Encryption problem: Invalid IV spec: " + e.getMessage(), e);
+		 } catch (IllegalBlockSizeException e) {
+			 throw new EncryptionException("Encryption failure (no padding used; invalid input size)",
+					 "Encryption problem: Invalid input size without padding (" + xform + "). " + e.getMessage(), e);
+		 } catch (BadPaddingException e) {
+			 throw new EncryptionException("Encryption failure",
+					 "[Note: Should NEVER happen in encryption mode.] Encryption problem: " + e.getMessage(), e);
+		 } catch (NoSuchAlgorithmException e) {
+			 throw new EncryptionException("Encryption failure (unavailable cipher requested)",
+					 "Encryption problem: specified algorithm in cipher xform " + xform + " not available: " + e.getMessage(), e);
+		 } catch (NoSuchPaddingException e) {
+			 throw new EncryptionException("Encryption failure (unavailable padding scheme requested)",
+					 "Encryption problem: specified padding scheme in cipher xform " + xform + " not available: " + e.getMessage(), e);
+		 } finally {
+			 // Don't overwrite anything in the case of exceptions because they may wish to retry.
+			 if ( success && overwritePlaintext ) {
+				 plain.overwrite();		// Note: Same as overwriting 'plaintext' byte array.
+		}
+	}
+	 }
+
+	/**
+	* {@inheritDoc}
+	*/
+	public PlainText decrypt(CipherText ciphertext) throws EncryptionException {
+		 // Now more of a convenience function for using the master key.
+		 return decrypt(secretKeySpec, ciphertext);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public PlainText decrypt(SecretKey key, CipherText ciphertext)
+	    throws EncryptionException, IllegalArgumentException
+	{
+	    long start = System.nanoTime();  // Current time in nanosecs; used to prevent timing attacks
+	    if ( key == null ) {
+	        throw new IllegalArgumentException("SecretKey arg may not be null");
+	    }
+	    if ( ciphertext == null ) {
+	        throw new IllegalArgumentException("Ciphertext may arg not be null");
+	    }
+
+	    if ( ! CryptoHelper.isAllowedCipherMode(ciphertext.getCipherMode()) ) {
+	        // This really should be an illegal argument exception, but it could
+	        // mean that a partner encrypted something using a cipher mode that
+	        // you do not accept, so it's a bit more complex than that. Also
+	        // throwing an IllegalArgumentException doesn't allow us to provide
+	        // the two separate error messages or automatically log it.
+	        throw new EncryptionException(DECRYPTION_FAILED,
+	                "Invalid cipher mode " + ciphertext.getCipherMode() +
+	        " not permitted for decryption or encryption operations.");
+	    }
+	    logger.debug(Logger.EVENT_SUCCESS,
+	            "Args valid for JavaEncryptor.decrypt(SecretKey,CipherText): " +
+	            ciphertext);
+
+	    PlainText plaintext = null;
+	    boolean caughtException = false;
+	    int progressMark = 0;
+	    try {
+	        // First we validate the MAC.
+	        boolean valid = CryptoHelper.isCipherTextMACvalid(key, ciphertext);
+	        if ( !valid ) {
+	            try {
+	                // This is going to fail, but we want the same processing
+	                // to occur as much as possible so as to prevent timing
+	                // attacks. We _could_ just be satisfied by the additional
+	                // sleep in the 'finally' clause, but an attacker on the
+	                // same server who can run something like 'ps' can tell
+	                // CPU time versus when the process is sleeping. Hence we
+	                // try to make this as close as possible. Since we know
+	                // it is going to fail, we ignore the result and ignore
+	                // the (expected) exception.
+	                handleDecryption(key, ciphertext); // Ignore return (should fail).
+	            } catch(Exception ex) {
+	                ;   // Ignore
+	            }
+	            throw new EncryptionException(DECRYPTION_FAILED,
+	                    "Decryption failed because MAC invalid for " +
+	                    ciphertext);
+	        }
+	        progressMark++;
+	        // The decryption only counts if the MAC was valid.
+	        plaintext = handleDecryption(key, ciphertext);
+	        progressMark++;
+	    } catch(EncryptionException ex) {
+	        caughtException = true;
+	        String logMsg = null;
+	        switch( progressMark ) {
+	        case 1:
+	            logMsg = "Decryption failed because MAC invalid. See logged exception for details.";
+	            break;
+	        case 2:
+	            logMsg = "Decryption failed because handleDecryption() failed. See logged exception for details.";
+	            break;
+	        default:
+	            logMsg = "Programming error: unexpected progress mark == " + progressMark;
+	        break;
+	        }
+	        logger.error(Logger.SECURITY_FAILURE, logMsg);
+	        throw ex;           // Re-throw
+	    }
+	    finally {
+	        if ( caughtException ) {
+	            // The rest of this code is to try to account for any minute differences
+	            // in the time it might take for the various reasons that decryption fails
+	            // in order to prevent any other possible timing attacks. Perhaps it is
+	            // going overboard. If nothing else, if N_SECS is large enough, it might
+	            // deter attempted repeated attacks by making them take much longer.
+	            long now = System.nanoTime();
+	            long elapsed = now - start;
+	            final long NANOSECS_IN_SEC = 1000000000L; // nanosec is 10**-9 sec
+	            long nSecs = N_SECS * NANOSECS_IN_SEC;  // N seconds in nano seconds
+	            if ( elapsed < nSecs ) {
+	                // Want to sleep so total time taken is N seconds.
+	                long extraSleep = nSecs - elapsed;
+
+	                // 'extraSleep' is in nanoseconds. Need to convert to a millisec
+	                // part and nanosec part. Nanosec is 10**-9, millsec is
+	                // 10**-3, so divide by (10**-9 / 10**-3), or 10**6 to
+	                // convert to from nanoseconds to milliseconds.
+	                long millis = extraSleep / 1000000L;
+	                long nanos  = (extraSleep - (millis * 1000000L));
+	                assert nanos >= 0 && nanos <= Integer.MAX_VALUE :
+                            "Nanosecs out of bounds; nanos = " + nanos;
+	                try {
+	                    Thread.sleep(millis, (int)nanos);
+	                } catch(InterruptedException ex) {
+	                    ;   // Ignore
+	                }
+	            } // Else ... time already exceeds N_SECS sec, so do not sleep.
+	        }
+	    }
+	    return plaintext;
+	}
+
+    // Handle the actual decryption portion. At this point it is assumed that
+    // any MAC has already been validated. (But see "DISCUSS" issue, below.)
+    private PlainText handleDecryption(SecretKey key, CipherText ciphertext)
+        throws EncryptionException
+    {
+        int keySize = 0;
+        try {
+            Cipher decrypter = Cipher.getInstance(ciphertext.getCipherTransformation());
+            keySize = key.getEncoded().length * 8;  // Convert to # bits
+
+            // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then
+            // use the specified SecretKey as-is rather than computing a derived key from it. We also
+            // don't expect a separate MAC in the specified CipherText object so therefore don't try
+            // to validate it.
+            boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( ciphertext.getCipherMode() );
+            SecretKey encKey = null;
+            if ( preferredCipherMode ) {
+                encKey = key;
+            } else {
+                // TODO: PERFORMANCE: Calculate avg time this takes and consider caching for very short interval
+                //       (e.g., 2 to 5 sec tops). Otherwise doing lots of encryptions in a loop could take a LOT longer.
+                //       But remember Jon Bentley's "Rule #1 on performance: First make it right, then make it fast."
+            	//		 This would be a security trade-off as it would leave keys in memory a bit longer, so it
+            	//		 should probably be off by default and controlled via a property.
+            	//
+            	// TODO: Feed in some additional parms here to use as the 'context' for the
+            	//		 KeyDerivationFunction...especially the KDF version. We would have to
+            	//		 store that in the CipherText object. We *possibly* could make it
+            	//		 transient so it would not be serialized with the CipherText object,
+            	//		 otherwise we would have to implement readObject() and writeObject()
+            	//		 methods there to support backward compatibility. Anyhow the intent
+            	//		 is to prevent down grade attacks when we finally re-design and
+            	//		 re-implement the MAC. Think about this in version 2.1.1.
+                encKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(),
+                		                    key, keySize, "encryption");
+            }
+            if ( ciphertext.requiresIV() ) {
+                decrypter.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(ciphertext.getIV()));
+            } else {
+                decrypter.init(Cipher.DECRYPT_MODE, encKey);
+            }
+            byte[] output = decrypter.doFinal(ciphertext.getRawCipherText());
+            return new PlainText(output);
+
+        } catch (InvalidKeyException ike) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Must install JCE Unlimited Strength Jurisdiction Policy Files from Sun", ike);
+        } catch (NoSuchAlgorithmException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Invalid algorithm for available JCE providers - " +
+                    ciphertext.getCipherTransformation() + ": " + e.getMessage(), e);
+        } catch (NoSuchPaddingException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Invalid padding scheme (" +
+                    ciphertext.getPaddingScheme() + ") for cipher transformation " + ciphertext.getCipherTransformation() +
+                    ": " + e.getMessage(), e);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
+        } catch (IllegalBlockSizeException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
+        } catch (BadPaddingException e) {
+            //DISCUSS: This needs fixed. Already validated MAC in CryptoHelper.isCipherTextMACvalid() above.
+            //So only way we could get a padding exception is if invalid padding were used originally by
+            //the party doing the encryption. (This might happen with a buggy padding scheme for instance.)
+            //It *seems* harmless though, so will leave it for now, and technically, we need to either catch it
+            //or declare it in a throws class. Clearly we don't want to do the later. This should be discussed
+            //during a code inspection.
+            SecretKey authKey;
+            try {
+                authKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(),
+                		                     key, keySize, "authenticity");
+            } catch (Exception e1) {
+                throw new EncryptionException(DECRYPTION_FAILED,
+                        "Decryption problem -- failed to compute derived key for authenticity: " + e1.getMessage(), e1);
+            }
+            boolean success = ciphertext.validateMAC( authKey );
+            if ( success ) {
+                throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
+            } else {
+                throw new EncryptionException(DECRYPTION_FAILED,
+                        "Decryption problem: WARNING: Adversary may have tampered with " +
+                        "CipherText object orCipherText object mangled in transit: " + e.getMessage(), e);
+            }
+        }
+    }
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public String sign(String data) throws EncryptionException {
+		try {
+			Signature signer = Signature.getInstance(signatureAlgorithm);
+			signer.initSign(privateKey);
+			signer.update(data.getBytes(encoding));
+			byte[] bytes = signer.sign();
+			return ESAPI.encoder().encodeForBase64(bytes, false);
+		} catch (InvalidKeyException ike) {
+			throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike);
+		} catch (Exception e) {
+			throw new EncryptionException("Signature failure", "Can't find signature algorithm " + signatureAlgorithm, e);
+		}
+	}
+		
+	/**
+	* {@inheritDoc}
+	*/
+	public boolean verifySignature(String signature, String data) {
+		try {
+			byte[] bytes = ESAPI.encoder().decodeFromBase64(signature);
+			Signature signer = Signature.getInstance(signatureAlgorithm);
+			signer.initVerify(publicKey);
+			signer.update(data.getBytes(encoding));
+			return signer.verify(bytes);
+		} catch (Exception e) {
+		    // NOTE: EncryptionException constructed *only* for side-effect of causing logging.
+		    // FindBugs complains about this and since it examines byte-code, there's no way to
+		    // shut it up.
+			new EncryptionException("Invalid signature", "Problem verifying signature: " + e.getMessage(), e);
+			return false;
+		}
+	}
+
+	/**
+	* {@inheritDoc}
+     *
+     * @param expiration
+     * @throws IntegrityException
+     */
+	public String seal(String data, long expiration) throws IntegrityException {
+	    if ( data == null ) {
+	        throw new IllegalArgumentException("Data to be sealed may not be null.");
+	    }
+	    
+		try {
+		    String b64data = null;
+            try {
+                b64data = ESAPI.encoder().encodeForBase64(data.getBytes("UTF-8"), false);
+            } catch (UnsupportedEncodingException e) {
+                ; // Ignore; should never happen since UTF-8 built into rt.jar
+            }
+			// mix in some random data so even identical data and timestamp produces different seals
+			String nonce = ESAPI.randomizer().getRandomString(10, EncoderConstants.CHAR_ALPHANUMERICS);
+			String plaintext = expiration + ":" + nonce + ":" + b64data;
+			// add integrity check; signature is already base64 encoded.
+			String sig = this.sign( plaintext );
+			CipherText ciphertext = this.encrypt( new PlainText(plaintext + ":" + sig) );
+			String sealedData = ESAPI.encoder().encodeForBase64(ciphertext.asPortableSerializedByteArray(), false);
+			return sealedData;
+		} catch( EncryptionException e ) {
+			throw new IntegrityException( e.getUserMessage(), e.getLogMessage(), e );
+		}
+	}
+
+	/**
+	* {@inheritDoc}
+	*/
+	public String unseal(String seal) throws EncryptionException {
+		PlainText plaintext = null;
+		try {
+		    byte[] encryptedBytes = ESAPI.encoder().decodeFromBase64(seal);
+		    CipherText cipherText = null;
+		    try {
+		        cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes);
+		    } catch( AssertionError e) {
+	            // Some of the tests in EncryptorTest.testVerifySeal() are examples of
+		        // this if assertions are enabled.
+		        throw new EncryptionException("Invalid seal",
+	                                          "Seal passed garbarge data resulting in AssertionError: " + e);
+	        }
+			plaintext = this.decrypt(cipherText);
+
+			String[] parts = plaintext.toString().split(":");
+			if (parts.length != 4) {
+				throw new EncryptionException("Invalid seal", "Seal was not formatted properly.");
+			}
+	
+			String timestring = parts[0];
+			long now = new Date().getTime();
+			long expiration = Long.parseLong(timestring);
+			if (now > expiration) {
+				throw new EncryptionException("Invalid seal", "Seal expiration date of " + new Date(expiration) + " has past.");
+			}
+			String nonce = parts[1];
+			String b64data = parts[2];
+			String sig = parts[3];
+			if (!this.verifySignature(sig, timestring + ":" + nonce + ":" + b64data ) ) {
+				throw new EncryptionException("Invalid seal", "Seal integrity check failed");
+			}	
+			return new String(ESAPI.encoder().decodeFromBase64(b64data), "UTF-8");
+		} catch (EncryptionException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new EncryptionException("Invalid seal", "Invalid seal:" + e.getMessage(), e);
+		}
+	}
+
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public boolean verifySeal( String seal ) {
+		try {
+			unseal( seal );
+			return true;
+		} catch( EncryptionException e ) {
+			return false;
+		}
+	}
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public long getTimeStamp() {
+		return new Date().getTime();
+	}
+
+	/**
+	* {@inheritDoc}
+	*/
+	public long getRelativeTimeStamp( long offset ) {
+		return new Date().getTime() + offset;
+	}
+
+	// DISCUSS: Why experimental? Would have to be added to Encryptor interface
+	//			but only 3 things I saw wrong with this was 1) it used HMacMD5 instead
+	//			of HMacSHA1 (see discussion below), 2) that the HMac key is the
+	//			same one used for encryption (also see comments), and 3) it caught
+	//			overly broad exceptions. Here it is with these specific areas
+	//			addressed, but no unit testing has been done at this point. -kww
+   /**
+    * Compute an HMAC for a String.  Experimental.
+    * @param input	The input for which to compute the HMac.
+    */
+/********************
+	public String computeHMAC( String input ) throws EncryptionException {
+		try {
+			Mac hmac = Mac.getInstance("HMacSHA1"); // DISCUSS: Changed to HMacSHA1. MD5 *badly* broken
+												   //          SHA1 should really be avoided, but using
+												   //		   for HMAC-SHA1 is acceptable for now. Plan
+												   //		   to migrate to SHA-256 or NIST replacement for
+												   //		   SHA1 in not too distant future.
+			// DISCUSS: Also not recommended that the HMac key is the same as the one
+			//			used for encryption (namely, Encryptor.MasterKey). If anything it
+			//			would be better to use Encryptor.MasterSalt for the HMac key, or
+			//			perhaps a derived key based on the master salt. (One could use
+			//			KeyDerivationFunction.computeDerivedKey().)
+			//
+			byte[] salt = ESAPI.securityConfiguration().getMasterSalt();
+			hmac.init( new SecretKeySpec(salt, "HMacSHA1") );	// Was:	hmac.init(secretKeySpec)	
+			byte[] inBytes;
+			try {
+				inBytes = input.getBytes("UTF-8");
+			} catch (UnsupportedEncodingException e) {
+				logger.warning(Logger.SECURITY_FAILURE, "computeHMAC(): Can't find UTF-8 encoding; using default encoding", e);
+				inBytes = input.getBytes();
+			}
+			byte[] bytes = hmac.doFinal( inBytes );
+			return ESAPI.encoder().encodeForBase64(bytes, false);
+		} catch (InvalidKeyException ike) {
+			throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike);
+	    } catch (NoSuchAlgorithmException e) {
+	    	throw new EncryptionException("Could not compute HMAC", "Can't find HMacSHA1 algorithm. " +
+	    															"Problem computing HMAC for " + input, e );
+	    }
+	}
+********************/
+
+    /**
+     * Log a security warning every Nth time one of the deprecated encrypt or
+     * decrypt methods are called. ('N' is hard-coded to be 25 by default, but
+     * may be changed via the system property
+     * {@code ESAPI.Encryptor.warnEveryNthUse}.) In other words, we nag
+     * them until the give in and change it. ;-)
+     * 
+     * @param where The string "encrypt" or "decrypt", corresponding to the
+     *              method that is being logged.
+     * @param msg   The message to log.
+     */
+    private void logWarning(String where, String msg) {
+        int counter = 0;
+        if ( where.equals("encrypt") ) {
+            counter = encryptCounter++;
+            where = "JavaEncryptor.encrypt(): [count=" + counter +"]";
+        } else if ( where.equals("decrypt") ) {
+            counter = decryptCounter++;
+            where = "JavaEncryptor.decrypt(): [count=" + counter +"]";
+        } else {
+            where = "JavaEncryptor: Unknown method: ";
+        }
+        // We log the very first time (note the use of post-increment on the
+        // counters) and then every Nth time thereafter. Logging every single
+        // time is likely to be way too much logging.
+        if ( (counter % logEveryNthUse) == 0 ) {
+            logger.warning(Logger.SECURITY_FAILURE, where + msg);
+        }
+    }
+    
+    private KeyDerivationFunction.PRF_ALGORITHMS getPRF(String name) {    	
+		String prfName = null;
+		if ( name == null ) {
+			prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		} else {
+			prfName = name;
+		}
+		KeyDerivationFunction.PRF_ALGORITHMS prf = KeyDerivationFunction.convertNameToPRF(prfName);
+		return prf;
+    }
+    
+    private KeyDerivationFunction.PRF_ALGORITHMS getDefaultPRF() {
+		String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		return getPRF(prfName);
+    }
+    
+    // Private interface to call ESAPI's KDF to get key for encryption or authenticity.
+    private SecretKey computeDerivedKey(int kdfVersion, KeyDerivationFunction.PRF_ALGORITHMS prf,
+    									SecretKey kdk, int keySize, String purpose)
+    	throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException
+    {
+    	// These really should be turned into actual runtime checks and an
+    	// IllegalArgumentException should be thrown if they are violated.
+    	// But this should be OK since this is a private method. Also, this method will
+    	// be called quite often so assertions are a big win as they can be disabled or
+    	// enabled at will.
+    	assert prf != null : "Pseudo Random Function for KDF cannot be null";
+    	assert kdk != null : "Key derivation key cannot be null.";
+    	// We would choose a larger minimum key size, but we want to be
+    	// able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that,
+    	// we print warning.
+    	assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
+    	assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
+    	assert purpose != null : "Purpose cannot be null. Should be 'encryption' or 'authenticity'.";
+    	assert purpose.equals("encryption") || purpose.equals("authenticity") :
+    		"Purpose must be \"encryption\" or \"authenticity\".";
+
+    	KeyDerivationFunction kdf = new KeyDerivationFunction(prf);
+    	if ( kdfVersion != 0 ) {
+    		kdf.setVersion(kdfVersion);
+    	}
+    	return kdf.computeDerivedKey(kdk, keySize, purpose);
+    }
+
+    // Get all the algorithms we will be using from ESAPI.properties.
+    private static void setupAlgorithms() {
+        // setup algorithms
+        encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm();
+        signatureAlgorithm = ESAPI.securityConfiguration().getDigitalSignatureAlgorithm();
+        randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm();
+        hashAlgorithm = ESAPI.securityConfiguration().getHashAlgorithm();
+        hashIterations = ESAPI.securityConfiguration().getHashIterations();
+        encoding = ESAPI.securityConfiguration().getCharacterEncoding();
+        encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength();
+        signatureKeyLength = ESAPI.securityConfiguration().getDigitalSignatureKeyLength();
+    }
+    
+    // Set up signing key pair using the master password and salt. Called (once)
+    // from the JavaEncryptor CTOR.
+    private static void initKeyPair(SecureRandom prng) throws NoSuchAlgorithmException {
+        String sigAlg = signatureAlgorithm.toLowerCase();
+        if ( sigAlg.endsWith("withdsa") ) {
+            //
+            // Admittedly, this is a kludge. However for Sun JCE, even though
+            // "SHA1withDSA" is a valid signature algorithm name, if one calls
+            //      KeyPairGenerator kpg = KeyPairGenerator.getInstance("SHA1withDSA");
+            // that will throw a NoSuchAlgorithmException with an exception
+            // message of "SHA1withDSA KeyPairGenerator not available". Since
+            // SHA1withDSA and DSA keys should be identical, we use "DSA"
+            // in the case that SHA1withDSA or SHAwithDSA was specified. This is
+            // all just to make these 2 work as expected. Sigh. (Note:
+            // this was tested with JDK 1.6.0_21, but likely fails with earlier
+            // versions of the JDK as well.)
+            //
+            sigAlg = "DSA";
+        } else if ( sigAlg.endsWith("withrsa") ) {
+            // Ditto for RSA.
+            sigAlg = "RSA";
+        }
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance(sigAlg);
+        keyGen.initialize(signatureKeyLength, prng);
+        KeyPair pair = keyGen.generateKeyPair();
+        privateKey = pair.getPrivate();
+        publicKey = pair.getPublic();
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/ReferenceEncryptedProperties.java.svn-base b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/ReferenceEncryptedProperties.java.svn-base
new file mode 100644
index 0000000..cffc149
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/ReferenceEncryptedProperties.java.svn-base
@@ -0,0 +1,293 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncryptedProperties;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionRuntimeException;
+
+/**
+ * Reference implementation of the {@code EncryptedProperties} interface. This
+ * implementation wraps a normal properties file, and creates surrogates for the
+ * {@code getProperty} and {@code setProperty} methods that perform encryption
+ * and decryption based on {@code Encryptor}.
+ * <p>
+ * This implementation differs from {@code DefaultEncryptedProperties} in that
+ * it actually extends from {@code java.util.Properties} for applications that need an
+ * instance of that class. In order to do so, the {@code getProperty} and
+ * {@code setProperty} methods were modified to throw {@code EncryptionRuntimeException}
+ * instead of {@code EncryptionException}.
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @author kevin.w.wall at gmail.com
+ * @since October 8, 2010
+ * @see org.owasp.esapi.EncryptedProperties
+ * @see org.owasp.esapi.reference.crypto.DefaultEncryptedProperties
+ */
+public class ReferenceEncryptedProperties extends java.util.Properties implements EncryptedProperties {
+
+	/**
+	 * serverVersionUID; use format of YYYYMMDD.
+	 */
+	private static final long serialVersionUID = 20120718L;
+
+	/** The logger. */
+	private final Logger logger = ESAPI.getLogger(this.getClass());
+
+	private static final String[] GET_ERROR_MESSAGES = new String[]{
+		": failed decoding from base64",
+		": failed to deserialize properly",
+		": failed to decrypt properly"
+	};
+
+	private static final String[] SET_ERROR_MESSAGES = new String[]{
+		": failed to encrypt properly",
+		": failed to serialize correctly",
+		": failed to base64-encode properly",
+		": failed to set base64-encoded value as property. Illegal key name?"
+	};
+
+	/**
+	 * Instantiates a new encrypted properties.
+	 */
+	public ReferenceEncryptedProperties() {
+		super();
+	}
+
+	public ReferenceEncryptedProperties(Properties defaults) {
+		super();
+
+		for (Object oKey : defaults.keySet()) {
+			String key		= (oKey instanceof String) ? (String)oKey : oKey.toString();
+			String value	= defaults.getProperty(key);
+
+			this.setProperty(key, value);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws EncryptionRuntimeException Thrown if decryption fails.
+	 */
+	@Override
+	public synchronized String getProperty(String key) throws EncryptionRuntimeException {
+	    int progressMark = 0;
+	    try {
+	        String encryptedValue = super.getProperty(key);
+
+	        if(encryptedValue==null)
+	            return null;
+
+	        progressMark = 0;
+	        byte[] serializedCiphertext   = ESAPI.encoder().decodeFromBase64(encryptedValue);
+	        progressMark++;
+	        CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(serializedCiphertext);
+	        progressMark++;
+	        PlainText plaintext           = ESAPI.encryptor().decrypt(restoredCipherText);
+
+	        return plaintext.toString();
+		} catch (Exception e) {
+			throw new EncryptionRuntimeException("Property retrieval failure",
+					                             "Couldn't retrieve encrypted property for property " + key +
+												 GET_ERROR_MESSAGES[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws EncryptionRuntimeException Thrown if decryption fails.
+	 */
+	@Override
+	public String getProperty(String key, String defaultValue) throws EncryptionRuntimeException {
+		String value = getProperty(key);
+
+		if (value == null) return defaultValue;
+
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws EncryptionRuntimeException Thrown if encryption fails.
+	 */
+	@Override
+	public synchronized String setProperty(String key, String value) throws EncryptionRuntimeException {
+	    int progressMark = 0;
+	    try {
+	        if ( key == null ) {
+	            throw new NullPointerException("Property name may not be null.");
+	        }
+	        if ( value == null ) {
+	            throw new NullPointerException("Property value may not be null.");
+	        }
+	        // NOTE: Not backward compatible w/ ESAPI 1.4.
+	        PlainText pt = new PlainText(value);
+	        CipherText ct = ESAPI.encryptor().encrypt(pt);
+	        progressMark++;
+	        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+	        progressMark++;
+	        String b64str = ESAPI.encoder().encodeForBase64(serializedCiphertext, false);
+	        progressMark++;
+	        return (String)super.put(key, b64str);
+	    } catch (Exception e) {
+	        throw new EncryptionRuntimeException("Property setting failure",
+	                                      "Couldn't set encrypted property " + key +
+	                                      SET_ERROR_MESSAGES[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @throws IOException Thrown if input stream invalid or does not
+	 * 					   correspond to Java properties file format.
+	 */
+	@Override
+	public void load(InputStream in) throws IOException {
+		super.load(in);
+		logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * For JDK 1.5 compatibility, this method has been overridden convert the Reader
+	 * into an InputStream and call the superclass constructor.
+	 * 
+	 * @throws IOException Thrown if {@code Reader} input stream invalid or does not
+	 * 					   correspond to Java properties file format.
+	 */
+	public void load(Reader in) throws IOException {
+
+		if (in == null) return;
+
+		//read from the reader into a StringBuffer
+		char[] cbuf				= new char[65536];
+		BufferedReader buff		= new BufferedReader(in);
+		StringBuilder contents	= new StringBuilder();
+
+		int read_this_time = 0;
+		while (read_this_time != -1) {
+			read_this_time = buff.read(cbuf, 0, 65536);
+			if (read_this_time > 0) contents.append(cbuf, 0, read_this_time);
+		}
+
+		//create a new InputStream from the StringBuffer
+		InputStream is = new ByteArrayInputStream(contents.toString().getBytes());
+
+		super.load(is);
+		logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@Override
+	public void list(PrintStream out) {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@Override
+	public void list(PrintWriter out) {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	@Override
+	public Collection values() {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	@Override
+	public Set entrySet() {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	@Override
+	public Enumeration elements() {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to only accept Strings for key and value, and to encrypt
+	 * those Strings before storing them. Outside classes should always use {@code setProperty}
+	 * to add values to the Properties map. If an outside class does erroneously call this method 
+	 * with non-String parameters an {@code IllegalArgumentException} will be thrown.
+	 *
+	 * @param key	A String key to add
+	 * @param value A String value to add
+	 * @return		The old value associated with the specified key, or {@code null}
+     *				if the key did not exist.
+	 */
+	@Override
+	public synchronized Object put(Object key, Object value) {
+		//if java.util.Properties is calling this method, just forward to the implementation in
+		//the superclass (java.util.Hashtable)
+		Throwable t = new Throwable();
+		for (StackTraceElement trace : t.getStackTrace()) {
+			if ("java.util.Properties".equals(trace.getClassName()) ) return super.put(key, value);
+		}
+
+		//otherwise, if both arguments are Strings, encrypt and store them
+		if (key instanceof String && value instanceof String) return setProperty((String)key, (String)value);
+
+		//other Object types are not allowed
+		throw new IllegalArgumentException("This method has been overridden to only accept Strings for key and value.");
+	}
+
+	/**
+	 * This method has been overridden to not print out the keys and values stored in this properties file.
+	 *
+	 * @return The minimal String representation of this class, as per java.lang.Object.
+	 */
+	@Override
+	public String toString() {
+		return getClass().getName() + "@" + Integer.toHexString(hashCode());
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..9114070
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/.svn/text-base/package.html.svn-base
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body bgcolor="white">
+This package contains the reference implementation for some of
+the ESAPI cryptography-related classes used throughout ESAPI.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/DefaultEncryptedProperties.java b/src/main/java/org/owasp/esapi/reference/crypto/DefaultEncryptedProperties.java
new file mode 100644
index 0000000..4f6d1a3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/DefaultEncryptedProperties.java
@@ -0,0 +1,214 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Reference implementation of the {@code EncryptedProperties} interface. This
+ * implementation wraps a normal properties file, and creates surrogates for the
+ * {@code getProperty} and {@code setProperty} methods that perform encryption
+ * and decryption based on {@code Encryptor}.
+ * <p>
+ * A very simple main program is provided that can be used to create an
+ * encrypted properties file. A better approach would be to allow unencrypted
+ * properties in the file and to encrypt them the first time the file is
+ * accessed.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @since June 1, 2007
+ * @see org.owasp.esapi.EncryptedProperties
+ * @see org.owasp.esapi.reference.crypto.ReferenceEncryptedProperties
+ */
+public class DefaultEncryptedProperties implements org.owasp.esapi.EncryptedProperties {
+
+	/** The properties. */
+	private final Properties properties = new Properties();
+
+	/** The logger. */
+	private final Logger logger = ESAPI.getLogger("EncryptedProperties");
+
+	/**
+	 * Instantiates a new encrypted properties.
+	 */
+	public DefaultEncryptedProperties() {
+		// hidden
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public synchronized String getProperty(String key) throws EncryptionException {
+	    String[] errorMsgs = new String[] {
+	            ": failed decoding from base64",
+	            ": failed to deserialize properly",
+	            ": failed to decrypt properly"
+	        };
+
+	    int progressMark = 0;
+	    try {
+	        String encryptedValue = properties.getProperty(key);
+
+	        if(encryptedValue==null)
+	            return null;
+
+	        progressMark = 0;
+	        byte[] serializedCiphertext   = ESAPI.encoder().decodeFromBase64(encryptedValue);
+	        progressMark++;
+	        CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(serializedCiphertext);
+	        progressMark++;
+	        PlainText plaintext           = ESAPI.encryptor().decrypt(restoredCipherText);
+	        
+	        return plaintext.toString();
+	    } catch (Exception e) {
+	        throw new EncryptionException("Property retrieval failure",
+	                                      "Couldn't retrieve encrypted property for property " + key +
+	                                      errorMsgs[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public synchronized String setProperty(String key, String value) throws EncryptionException {
+	    String[] errorMsgs = new String[] {
+	            ": failed to encrypt properly",
+	            ": failed to serialize correctly",
+	            ": failed to base64-encode properly",
+	            ": failed to set base64-encoded value as property. Illegal key name?"
+	    };
+
+	    int progressMark = 0;
+	    try {
+	        if ( key == null ) {
+	            throw new NullPointerException("Property name may not be null.");
+	        }
+	        if ( value == null ) {
+	            throw new NullPointerException("Property value may not be null.");
+	        }
+	        // NOTE: Not backward compatible w/ ESAPI 1.4.
+	        PlainText pt = new PlainText(value);
+	        CipherText ct = ESAPI.encryptor().encrypt(pt);
+	        progressMark++;
+	        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+	        progressMark++;
+	        String b64str = ESAPI.encoder().encodeForBase64(serializedCiphertext, false);
+	        progressMark++;
+	        String encryptedValue = (String)properties.setProperty(key, b64str);
+	        progressMark++;
+	        return encryptedValue;
+	    } catch (Exception e) {
+	        throw new EncryptionException("Property setting failure",
+	                                      "Couldn't set encrypted property " + key +
+	                                      errorMsgs[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Set<?> keySet() {
+		return properties.keySet();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void load(InputStream in) throws IOException {
+		properties.load(in);
+		logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void store(OutputStream out, String comments) throws IOException {
+		properties.store(out, comments);
+	}
+
+	/**
+	 * Loads encrypted properties file based on the location passed in args then prompts the 
+	 * user to input key-value pairs.  When the user enters a null or blank key, the values 
+	 * are stored to the properties file.
+	 * 
+	 * @param args
+	 *            the location of the properties file to load and write to
+	 * 
+	 * @throws Exception
+	 *             Any exception thrown
+	 * @deprecated Use {@code EncryptedPropertiesUtils} instead, which allows creating, reading,
+	 *			   and writing encrypted properties.
+	 */
+	public static void main(String[] args) throws Exception {
+		File f = new File(args[0]);
+		ESAPI.getLogger( "EncryptedProperties.main" ).debug(Logger.SECURITY_SUCCESS, "Loading encrypted properties from " + f.getAbsolutePath() );
+		if ( !f.exists() ) throw new IOException( "Properties file not found: " + f.getAbsolutePath() );
+		ESAPI.getLogger( "EncryptedProperties.main" ).debug(Logger.SECURITY_SUCCESS, "Encrypted properties found in " + f.getAbsolutePath() );
+		DefaultEncryptedProperties ep = new DefaultEncryptedProperties();
+
+		FileInputStream in = null;
+		FileOutputStream out = null;
+		try {
+    		in = new FileInputStream(f);
+            out = new FileOutputStream(f);
+
+            ep.load(in);   
+    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+    		String key = null;
+    		do {
+    			System.out.print("Enter key: ");
+    			key = br.readLine();
+    			System.out.print("Enter value: ");
+    			String value = br.readLine();
+    			if (key != null && key.length() > 0 && value != null && value.length() > 0) {
+    				ep.setProperty(key, value);
+    			}
+    		} while (key != null && key.length() > 0);
+    		ep.store(out, "Encrypted Properties File");
+		} finally {
+		    // FindBugs and PMD both complain about these next lines, that they may
+		    // ignore thrown exceptions. Really!!! That's the whole point.
+    		try { if ( in != null ) in.close(); } catch( Exception e ) {}
+    		try { if ( out != null ) out.close(); } catch( Exception e ) {}
+		}
+		
+		Iterator<?> i = ep.keySet().iterator();
+		while (i.hasNext()) {
+			String k = (String) i.next();
+			String value = ep.getProperty(k);
+			System.out.println("   " + k + "=" + value);
+		}
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtils.java b/src/main/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtils.java
new file mode 100644
index 0000000..cb1d512
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtils.java
@@ -0,0 +1,212 @@
+package org.owasp.esapi.reference.crypto;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import org.owasp.esapi.EncryptedProperties;
+
+/**
+ * Command line utilities for reading, writing and creating encrypted properties files.
+ * <p>
+ * Usage:<br/>
+ * <code>
+ *    java org.owasp.esapi.reference.crypto.EncryptedPropertiesUtils [--in file] [--out file] 
+ *			[--in-encrypted true|false] [--verbose true|false]
+ * </code>
+ * <p>
+ * Command line parameters:<br/>
+ * <ul>
+ * <li><b>--in</b> (Optional) Encrypted or plaintext file to read from. If no input file is specified, a new properties file will be created.</li>
+ * <li><b>--out</b> (Optional) Encrypted file to output to. Default: Overwrite input file</li>
+ * <li><b>--in-encrypted</b> (Optional) True if the input file is encrypted. Default: true</li>
+ * <li><b>--verbose</b> (Optional) If true, output (potentially unencrypted) information to the terminal. Default: false</li>
+ * </ul>
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ * @see org.owasp.esapi.EncryptedProperties
+ */
+public class EncryptedPropertiesUtils {
+
+	/**
+	 * Loads encrypted or plaintext properties file based on the location passed in args
+	 * then prompts the user to input key-value pairs.  When the user enters a null or
+	 * blank key, the values are stored to the properties file.
+	 *
+	 * @throws Exception Any exception thrown
+	 */
+	public static void main(String[] args) throws Exception {
+
+		//command line options
+		String inFile = null;
+		String outFile = null;
+		boolean inFileEncrypted = true;
+		boolean verbose = false;
+
+		//parse command line params
+		for (int i = 0; i < args.length; i = i + 2) {
+			String paramType = args[i];
+
+			if ("--in".equals(paramType) && args.length >= i + 1) {
+				inFile = args[i + 1];
+
+			} else if ("--out".equals(paramType) && args.length >= i + 1) {
+				outFile = args[i + 1];
+
+			} else if ("--in-encrypted".equals(paramType) && args.length >= i + 1) {
+				inFileEncrypted = Boolean.valueOf(args[i + 1]);
+
+			} else if ("--verbose".equals(paramType) && args.length >= i + 1) {
+				verbose = Boolean.valueOf(args[i + 1]);
+			}
+
+		}
+
+		if (outFile == null) {
+			outFile = inFile; //if no output file is specified we will overwrite the input file
+		}
+		if (outFile == null) {
+			//no input or output file specified. Can't continue.
+			System.out.println("You must specify an input file or output file");
+			System.exit(1);
+		}
+
+		//load in existing properties from a file
+		Properties props = loadProperties(inFile, inFileEncrypted);
+
+		//read user input and add keys and values to the property file
+		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+		String key = null;
+		do {
+			System.out.print("Enter key: ");
+			key = br.readLine();
+
+			if (props.containsKey(key)) {
+				System.out.print("Key already exists. Replace? ");
+				String confirm = br.readLine();
+				if (!("y".equals(confirm) || "yes".equals(confirm))) {
+					continue;
+				}
+			}
+
+			System.out.print("Enter value: ");
+			String value = br.readLine();
+
+			addProperty(props, key, value);
+
+		} while (key != null && key.length() > 0);
+
+		//save output file
+		storeProperties(outFile, props,
+				"Encrypted Properties File generated by org.owasp.esapi.reference.crypto.EncryptedPropertiesUtils");
+
+		System.out.println("Encrypted Properties file output to " + outFile);
+
+		if (verbose) {
+			for (Object oKey : props.keySet()) {
+				String sKey = (String) oKey;
+				String value = props.getProperty(sKey);
+				System.out.println("   " + sKey + "=" + value);
+			}
+		}
+
+	}
+
+	/**
+	 * Loads a Properties file from a filename. If the filename is unspecified
+	 * or the file could not be found, a new Properties is returned.
+	 *
+	 * @param inFile Filename to load Properties from.
+	 * @param inFileEncrypted If true, the input file is assumed to be already encrypted. Default true.
+	 * @return Either the loaded Properties object or a new one if the file could not be found.
+	 * @throws IOException
+	 */
+	public static Properties loadProperties(String inFile, Boolean inFileEncrypted) throws IOException {
+
+		if (inFileEncrypted == null) inFileEncrypted = true;
+
+		Properties props;
+
+		if (inFile != null) {
+
+			File f = new File(inFile);
+			if (!f.exists()) {
+				System.out.println("Input properties file not found. Creating new.");
+				props = new ReferenceEncryptedProperties();
+
+			} else {
+				String encrypted = inFileEncrypted ? "Encrypted" : "Plaintext";
+				System.out.println(encrypted + " properties found in " + f.getAbsolutePath());
+
+				Properties inProperties;
+				if (inFileEncrypted) {
+					inProperties = new ReferenceEncryptedProperties();
+				} else {
+					inProperties = new Properties();
+				}
+
+				InputStream in = null;
+				try {
+					in = new FileInputStream(f);
+					inProperties.load(in);
+				} finally {
+					try {
+						if (in != null) in.close(); //quietly close the InputStream
+					} catch (Exception e) {}
+				}
+
+				//Use the existing properties
+				props = new ReferenceEncryptedProperties(inProperties);
+			}
+		} else {
+			System.out.println("Input properties file not found. Creating new.");
+			props = new ReferenceEncryptedProperties();
+		}
+
+		return props;
+	}
+
+	/**
+	 * Stores a Properties object to a file.
+	 *
+	 * @param outFile Filename to store to
+	 * @param props Properties to store
+	 * @param message A message to add to the comments in the stored file
+	 * @throws Exception
+	 */
+	public static void storeProperties(String outFile, Properties props, String message) throws Exception {
+		OutputStream out = null;
+		try {
+			out = new FileOutputStream(new File(outFile));
+			props.store(out, message);
+		} finally {
+			try {
+				if (out != null) out.close();  //quietly close OutputStream
+			} catch (Exception e) {}
+		}
+	}
+
+	/**
+	 * Adds a new key-value property to the passed Properties object
+	 *
+	 * @param props The Properties object to add to
+	 * @param key The key to add
+	 * @param value The value to set
+	 * @return The previous value of the property, or null if it is newly added.
+	 */
+	public static Object addProperty(Properties props, String key, String value) {
+		if (props != null && key != null && key.length() > 0 && value != null && value.length() > 0) {
+			return props.setProperty(key, value);
+		}
+		return null;
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java
new file mode 100644
index 0000000..a20c612
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java
@@ -0,0 +1,1050 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+// import javax.crypto.Mac;			// Uncomment if computeHMAC() is included.
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherSpec;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.crypto.KeyDerivationFunction;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.crypto.SecurityProviderLoader;
+import org.owasp.esapi.errors.ConfigurationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.reference.DefaultSecurityConfiguration;
+
+/**
+ * Reference implementation of the {@code Encryptor} interface. This implementation
+ * layers on the JCE provided cryptographic package. Algorithms used are
+ * configurable in the {@code ESAPI.properties} file. The main property
+ * controlling the selection of this class is {@code ESAPI.Encryptor}. Most of
+ * the other encryption related properties have property names that start with
+ * the string "Encryptor.".
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author kevin.w.wall at gmail.com
+ * @author Chris Schmidt (chrisisbeef .at. gmail.com)
+ * @since June 1, 2007; some methods since ESAPI Java 2.0
+ * @see org.owasp.esapi.Encryptor
+ */
+public final class JavaEncryptor implements Encryptor {
+    private static volatile Encryptor singletonInstance;
+
+    // Note: This double-check pattern only works because singletonInstance
+    //       is declared to be volatile.  Usually this method is called
+    //       via ESAPI.encryptor() rather than directly.
+    public static Encryptor getInstance() throws EncryptionException {
+        if ( singletonInstance == null ) {
+            synchronized ( JavaEncryptor.class ) {
+                if ( singletonInstance == null ) {
+                    singletonInstance = new JavaEncryptor();
+                }
+            }
+        }
+        return singletonInstance;
+    }
+
+    private static boolean initialized = false;
+    
+    // encryption
+    private static SecretKeySpec secretKeySpec = null; // DISCUSS: Why static? Implies one key?!?
+    private static String encryptAlgorithm = "AES";
+    private static String encoding = "UTF-8"; 
+    private static int encryptionKeyLength = 128;
+    
+    // digital signatures
+    private static PrivateKey privateKey = null;
+	private static PublicKey publicKey = null;
+	private static String signatureAlgorithm = "SHA1withDSA";
+    private static String randomAlgorithm = "SHA1PRNG";
+	private static int signatureKeyLength = 1024;
+	
+	// hashing
+	private static String hashAlgorithm = "SHA-512";
+	private static int hashIterations = 1024;
+	
+	// Logging - DISCUSS: This "sticks" us with a specific logger to whatever it was when
+	//					  this class is first loaded. Is this a big limitation? Since there
+	//                    is no method to reset it, we may has well make it 'final' also.
+	private static Logger logger = ESAPI.getLogger("JavaEncryptor");
+	    // Used to print out warnings about deprecated methods.
+	private static int encryptCounter = 0;
+	private static int decryptCounter = 0;
+        // DISCUSS: OK to not have a property for this to set the frequency?
+        //          The desire is to persuade people to move away from these
+	    //          two deprecated encrypt(String) / decrypt(String) methods,
+        //          so perhaps the annoyance factor of not being able to
+        //          change it will help. For now, it is just hard-coded here.
+        //          We could be mean and just print a warning *every* time.
+	private static final int logEveryNthUse = 25;
+	
+    // *Only* use this string for user messages for EncryptionException when
+    // decryption fails. This is to prevent information leakage that may be
+    // valuable in various forms of ciphertext attacks, such as the
+	// Padded Oracle attack described by Rizzo and Duong.
+    private static final String DECRYPTION_FAILED =
+        "Decryption failed; see logs for details.";
+
+    // # of seconds that all failed decryption attempts will take. Used to
+    // help prevent side-channel timing attacks.
+    private static int N_SECS = 2;
+
+	// Load the preferred JCE provider if one has been specified.
+	static {
+	    try {
+            SecurityProviderLoader.loadESAPIPreferredJCEProvider();
+        } catch (NoSuchProviderException ex) {
+        	// Note that audit logging is done elsewhere in called method.
+            logger.fatal(Logger.SECURITY_FAILURE,
+                         "JavaEncryptor failed to load preferred JCE provider.", ex);
+            throw new ExceptionInInitializerError(ex);
+        }
+        setupAlgorithms();
+	}
+	
+    /**
+     * Generates a new strongly random secret key and salt that can be
+     * copy and pasted in the <b>ESAPI.properties</b> file.
+     * 
+     * @param args Set first argument to "-print" to display available algorithms on standard output.
+     * @throws java.lang.Exception	To cover a multitude of sins, mostly in configuring ESAPI.properties.
+     */
+    public static void main( String[] args ) throws Exception {
+		System.out.println( "Generating a new secret master key" );
+		
+		// print out available ciphers
+		if ( args.length == 1 && args[0].equalsIgnoreCase("-print" ) ) {
+			System.out.println( "AVAILABLE ALGORITHMS" );
+
+			Provider[] providers = Security.getProviders();
+			TreeMap<String, String> tm = new TreeMap<String, String>();
+			// DISCUSS: Note: We go through multiple providers, yet nowhere do I
+			//			see where we print out the PROVIDER NAME. Not all providers
+			//			will implement the same algorithms and some "partner" with
+			//			whom we are exchanging different cryptographic messages may
+			//			have _different_ providers in their java.security file. So
+			//			it would be useful to know the provider name where each
+			//			algorithm is implemented. Might be good to prepend the provider
+			//			name to the 'key' with something like "providerName: ". Thoughts?
+			for (int i = 0; i != providers.length; i++) {
+				// DISCUSS: Print security provider name here???
+					// Note: For some odd reason, Provider.keySet() returns
+					//		 Set<Object> of the property keys (which are Strings)
+					//		 contained in this provider, but Set<String> seems
+					//		 more appropriate. But that's why we need the cast below.
+	            System.out.println("===== Provider " + i + ":" + providers[i].getName() + " ======");
+				Iterator<Object> it = providers[i].keySet().iterator();
+				while (it.hasNext()) {
+					String key = (String)it.next();
+		            String value = providers[i].getProperty( key );
+		            tm.put(key, value);
+	                System.out.println("\t\t   " + key + " -> "+ value );
+				}
+			}
+
+			Set< Entry<String,String> > keyValueSet = tm.entrySet();
+			Iterator<Entry<String, String>> it = keyValueSet.iterator();
+			while( it.hasNext() ) {
+				Map.Entry<String,String> entry = it.next();
+				String key = entry.getKey();
+				String value = entry.getValue();
+	        	System.out.println( "   " + key + " -> "+ value );
+			}
+		} else {
+				// Used to print a similar line to use '-print' even when it was specified.
+			System.out.println( "\tuse '-print' to also show available crypto algorithms from all the security providers" );
+		}
+		
+        // setup algorithms -- Each of these have defaults if not set, although
+		//					   someone could set them to something invalid. If
+		//					   so a suitable exception will be thrown and displayed.
+        encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm();
+		encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength();
+		randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm();
+
+		SecureRandom random = SecureRandom.getInstance(randomAlgorithm);
+		SecretKey secretKey = CryptoHelper.generateSecretKey(encryptAlgorithm, encryptionKeyLength);
+        byte[] raw = secretKey.getEncoded();
+        byte[] salt = new byte[20];	// Or 160-bits; big enough for SHA1, but not SHA-256 or SHA-512.
+        random.nextBytes( salt );
+        String eol = System.getProperty("line.separator", "\n"); // So it works on Windows too.
+        System.out.println( eol + "Copy and paste these lines into your ESAPI.properties" + eol);
+        System.out.println( "#==============================================================");
+        System.out.println( "Encryptor.MasterKey=" + ESAPI.encoder().encodeForBase64(raw, false) );
+        System.out.println( "Encryptor.MasterSalt=" + ESAPI.encoder().encodeForBase64(salt, false) );
+        System.out.println( "#==============================================================" + eol);
+    }
+	
+    
+    /**
+     * Private CTOR for {@code JavaEncryptor}, called by {@code getInstance()}.
+     * @throws EncryptionException if can't construct this object for some reason.
+     * 					Original exception will be attached as the 'cause'.
+     */
+    private JavaEncryptor() throws EncryptionException {
+        byte[] salt = ESAPI.securityConfiguration().getMasterSalt();
+        byte[] skey = ESAPI.securityConfiguration().getMasterKey();
+
+        assert salt != null : "Can't obtain master salt, Encryptor.MasterSalt";
+        assert salt.length >= 16 : "Encryptor.MasterSalt must be at least 16 bytes. " +
+                                   "Length is: " + salt.length + " bytes.";
+        assert skey != null : "Can't obtain master key, Encryptor.MasterKey";
+        assert skey.length >= 7 : "Encryptor.MasterKey must be at least 7 bytes. " +
+                                  "Length is: " + skey.length + " bytes.";
+        
+        // Set up secretKeySpec for use for symmetric encryption and decryption,
+        // and set up the public/private keys for asymmetric encryption /
+        // decryption.
+        // TODO: Note: If we dump ESAPI 1.4 crypto backward compatibility,
+        //       then we probably will ditch the Encryptor.EncryptionAlgorithm
+        //       property. If so, encryptAlgorithm should probably use
+        //       Encryptor.CipherTransformation and just pull off the cipher
+        //       algorithm name so we can use it here.
+        synchronized(JavaEncryptor.class) {
+            if ( ! initialized ) {
+                //
+                // For symmetric encryption
+                //
+                //      NOTE: FindBugs complains about this
+                //            (ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD) but
+                //            it should be OK since it is synchronized and only
+                //            done once. While we could separate this out and
+                //            handle in a static initializer, it just seems to
+                //            fit better here.
+                secretKeySpec = new SecretKeySpec(skey, encryptAlgorithm );
+                
+                //
+                // For asymmetric encryption (i.e., public/private key)
+                //
+                try {
+                    SecureRandom prng = SecureRandom.getInstance(randomAlgorithm);
+
+                    // Because hash() is not static (but it could be were in not
+                    // for the interface method specification in Encryptor), we
+                    // cannot do this initialization in a static method or static
+                    // initializer.
+                    byte[] seed = hash(new String(skey, encoding),new String(salt, encoding)).getBytes(encoding);
+                    prng.setSeed(seed);
+                    initKeyPair(prng);
+                } catch (Exception e) {
+                    throw new EncryptionException("Encryption failure", "Error creating Encryptor", e);
+                }             
+                
+                // Mark everything as initialized.
+                initialized = true;
+            }
+        }
+    }
+     
+
+
+	/**
+     * {@inheritDoc}
+     * 
+	 * Hashes the data with the supplied salt and the number of iterations specified in
+	 * the ESAPI SecurityConfiguration.
+	 */
+	public String hash(String plaintext, String salt) throws EncryptionException {
+		return hash( plaintext, salt, hashIterations );
+	}
+	
+	/**
+     * {@inheritDoc}
+     * 
+	 * Hashes the data using the specified algorithm and the Java MessageDigest class. This method
+	 * first adds the salt, a separator (":"), and the data, and then rehashes the specified number of iterations
+	 * in order to help strengthen weak passwords.
+	 */
+	public String hash(String plaintext, String salt, int iterations) throws EncryptionException {
+		byte[] bytes = null;
+		try {
+			MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);
+			digest.reset();
+			digest.update(ESAPI.securityConfiguration().getMasterSalt());
+			digest.update(salt.getBytes(encoding));
+			digest.update(plaintext.getBytes(encoding));
+
+			// rehash a number of times to help strengthen weak passwords
+			bytes = digest.digest();
+			for (int i = 0; i < iterations; i++) {
+				digest.reset();
+				bytes = digest.digest(bytes);
+			}
+			String encoded = ESAPI.encoder().encodeForBase64(bytes,false);
+			return encoded;
+		} catch (NoSuchAlgorithmException e) {
+			throw new EncryptionException("Internal error", "Can't find hash algorithm " + hashAlgorithm, e);
+		} catch (UnsupportedEncodingException ex) {
+			throw new EncryptionException("Internal error", "Can't find encoding for " + encoding, ex);
+		}
+	}
+
+	/**
+	* {@inheritDoc}
+	*/
+	 public CipherText encrypt(PlainText plaintext) throws EncryptionException {
+		 // Now more of a convenience function for using the master key.
+		 return encrypt(secretKeySpec, plaintext);
+	 }
+	 
+	 /**
+	  * {@inheritDoc}
+	  */
+	 public CipherText encrypt(SecretKey key, PlainText plain)
+	 			throws EncryptionException
+	 {
+		 if ( key == null ) {
+			 throw new IllegalArgumentException("(Master) encryption key arg may not be null. Is Encryptor.MasterKey set?");
+		 }
+		 if ( plain == null ) {
+			 throw new IllegalArgumentException("PlainText may arg not be null");
+		 }
+		 byte[] plaintext = plain.asBytes();
+		 boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText();
+
+		 boolean success = false;	// Used in 'finally' clause.
+		 String xform = null;
+		 int keySize = key.getEncoded().length * 8;	// Convert to # bits
+
+		try {
+			 xform = ESAPI.securityConfiguration().getCipherTransformation();
+             String[] parts = xform.split("/");
+             assert parts.length == 3 : "Malformed cipher transformation: " + xform;
+             String cipherMode = parts[1];
+             
+             // This way we can prevent modes like OFB and CFB where the IV should never
+             // be repeated with the same encryption key (at least until we support
+             // Encryptor.ChooseIVMethod=specified and allow us to specify some mechanism
+             // to ensure the IV will never be repeated (such as a time stamp or other
+             // monotonically increasing function).
+             // DISCUSS: Should we include the permitted cipher modes in the exception msg?
+             if ( ! CryptoHelper.isAllowedCipherMode(cipherMode) ) {
+                 throw new EncryptionException("Encryption failure: invalid cipher mode ( " + cipherMode + ") for encryption",
+                             "Encryption failure: Cipher transformation " + xform + " specifies invalid " +
+                             "cipher mode " + cipherMode);
+             }
+             
+			 // Note - Cipher is not thread-safe so we create one locally
+			 //        Also, we need to change this eventually so other algorithms can
+			 //        be supported. Eventually, there will be an encrypt() method that
+			 //        takes a (new class) CryptoControls, as something like this:
+			 //          public CipherText encrypt(CryptoControls ctrl, SecretKey skey, PlainText plaintext)
+			 //        and this method will just call that one.
+			 Cipher encrypter = Cipher.getInstance(xform);
+			 String cipherAlg = encrypter.getAlgorithm();
+			 int keyLen = ESAPI.securityConfiguration().getEncryptionKeyLength();
+
+			 // DISCUSS: OK, what do we want to do here if keyLen != keySize? If use keyLen, encryption
+			 //		     could fail with an exception, but perhaps that's what we want. Or we may just be
+			 //			 OK with silently using keySize as long as keySize >= keyLen, which then interprets
+			 //			 ESAPI.EncryptionKeyLength as the *minimum* key size, but as long as we have something
+			 //			 stronger it's OK to use it. For now, I am just going to log warning if different, but use
+			 //			 keySize unless keySize is SMALLER than ESAPI.EncryptionKeyLength, in which case I'm going
+			 //			 to log an error.
+			 //
+			 //			 IMPORTANT NOTE:	When we generate key sizes for both DES and DESede the result of
+			 //								SecretKey.getEncoding().length includes the TRUE key size (i.e.,
+			 //								*with* the even parity bits) rather than the EFFECTIVE key size
+			 //								(which incidentally is what KeyGenerator.init() expects for DES
+			 //								and DESede; duh! Nothing like being consistent). This leads to
+			 //								the following dilemma:
+			 //
+			 //													EFFECTIVE Key Size		TRUE Key Size
+			 //													(KeyGenerator.init())	(SecretKey.getEncoding().length)
+			 //									========================================================================
+			 //									For DES:			56 bits					64 bits
+			 //									For DESede:			112 bits / 168 bits		192 bits (always)
+			 //
+			 //								We are trying to automatically determine the key size from SecretKey
+			 //								based on 8 * SecretKey.getEncoding().length, but as you can see, the
+			 //								2 key 3DES and the 3 key 3DES both use the same key size (192 bits)
+			 //								regardless of what is passed to KeyGenerator.init(). There are no advertised
+			 //								methods to get the key size specified by the init() method so I'm not sure how
+			 //								this is actually working internally. However, it does present a problem if we
+			 //								wish to communicate the 3DES key size to a recipient for later decryption as
+			 //								they would not be able to distinguish 2 key 3DES from 3 key 3DES.
+			 //
+			 //								The only workaround I know is to pass the explicit key size down. However, if
+			 //								we are going to do that, I'd propose passing in a CipherSpec object so we could
+			 //								tell what cipher transformation to use as well instead of just the key size. Then
+			 //								we would extract keySize from the CipherSpec object of from the SecretKey object.
+			 //
+			 if ( keySize != keyLen ) {
+				 // DISCUSS: Technically this is not a security "failure" per se, but not really a "success" either.
+				 logger.warning(Logger.SECURITY_FAILURE, "Encryption key length mismatch. ESAPI.EncryptionKeyLength is " +
+						 keyLen + " bits, but length of actual encryption key is " + keySize +
+				 		" bits.  Did you remember to regenerate your master key (if that is what you are using)???");
+			 }
+			 // DISCUSS: Reconsider these warnings. If thousands of encryptions are done in tight loop, no one needs
+			 //          more than 1 warning. Should we do something more intelligent here?
+			 if ( keySize < keyLen ) {
+				 // ESAPI.EncryptionKeyLength defaults to 128, but that means that we could not use DES (as weak as it
+				 // is), even for legacy code. Therefore, this has been changed to simple log a warning rather than
+				 //	throw the following exception.
+				 //				 throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " +
+				 //						  "encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits.");
+				 logger.warning(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN specified " +
+						 "encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits with cipher algorithm " + cipherAlg);
+			 }
+			 if ( keySize < 112 ) {		// NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030.
+				 						// Note that 112 bits 'just happens' to be size of 2-key Triple DES!
+				 logger.warning(Logger.SECURITY_FAILURE, "Potentially unsecure encryption. Key size of " + keySize + "bits " +
+				                "not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " +
+				                "of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57.");
+			 }
+			 // Check if algorithm mentioned in SecretKey is same as that being used for Cipher object.
+			 // They should be the same. If they are different, things could fail. (E.g., DES and DESede
+			 // require keys with even parity. Even if key was sufficient size, if it didn't have the correct
+			 // parity it could fail.)
+			 //
+			 String skeyAlg = key.getAlgorithm();
+			 if ( !( cipherAlg.startsWith( skeyAlg + "/" ) || cipherAlg.equals( skeyAlg ) ) ) {
+				 // DISCUSS: Should we thrown a ConfigurationException here or just log a warning??? I'm game for
+				 //			 either, but personally I'd prefer the squeaky wheel to the annoying throwing of
+				 //			 a ConfigurationException (which is a RuntimeException). Less likely to upset
+				 //			 the development community.
+				 logger.warning(Logger.SECURITY_FAILURE, "Encryption mismatch between cipher algorithm (" +
+						 cipherAlg + ") and SecretKey algorithm (" + skeyAlg + "). Cipher will use algorithm " + cipherAlg);
+			 }
+
+			 byte[] ivBytes = null;
+			 CipherSpec cipherSpec = new CipherSpec(encrypter, keySize);	// Could pass the ACTUAL (intended) key size
+			 
+             // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then
+             // use the specified SecretKey as-is rather than computing a derived key from it. We also
+             // don't expect a separate MAC in the specified CipherText object so therefore don't try
+             // to validate it.
+             boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( cipherMode );
+             SecretKey encKey = null;
+			 if ( preferredCipherMode ) {
+			     encKey = key;
+			 } else {
+			     encKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(),
+			    		 				    key, keySize, "encryption");
+			 }
+			 
+			 if ( cipherSpec.requiresIV() ) {
+				 String ivType = ESAPI.securityConfiguration().getIVType();
+				 IvParameterSpec ivSpec = null;
+				 if ( ivType.equalsIgnoreCase("random") ) {
+					 ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize());
+				 } else if ( ivType.equalsIgnoreCase("fixed") ) {
+					 String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV();
+					 ivBytes = Hex.decode(fixedIVAsHex);
+					 /* FUTURE		 } else if ( ivType.equalsIgnoreCase("specified")) {
+					 		// FUTURE - TODO  - Create instance of specified class to use for IV generation and
+					 		//					 use it to create the ivBytes. (The intent is to make sure that
+					 		//				     1) IVs are never repeated for cipher modes like OFB and CFB, and
+					 		//					 2) to screen for weak IVs for the particular cipher algorithm.
+					 		//		In meantime, use 'random' for block cipher in feedback mode. Unlikely they will
+					 		//		be repeated unless you are salting SecureRandom with same value each time. Anything
+					 		//		monotonically increasing should be suitable, like a counter, but need to remember
+					 		//		it across JVM restarts. Was thinking of using System.currentTimeMillis(). While
+					 		//		it's not perfect it probably is good enough. Could even all (advanced) developers
+					 		//      to define their own class to create a unique IV to allow them some choice, but
+					 		//      definitely need to provide a safe, default implementation.
+					  */
+				 } else {
+					 // TODO: Update to add 'specified' once that is supported and added above.
+					 throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'");
+				 }
+				 ivSpec = new IvParameterSpec(ivBytes);
+				 cipherSpec.setIV(ivBytes);
+				 encrypter.init(Cipher.ENCRYPT_MODE, encKey, ivSpec);
+			 } else {
+				 encrypter.init(Cipher.ENCRYPT_MODE, encKey);
+			 }
+			 logger.debug(Logger.EVENT_SUCCESS, "Encrypting with " + cipherSpec);
+			 byte[] raw = encrypter.doFinal(plaintext);
+                 // Convert to CipherText.
+             CipherText ciphertext = new CipherText(cipherSpec, raw);
+			 
+			 // If we are using a "preferred" cipher mode--i.e., one that supports *both* confidentiality and
+			 // authenticity, there is no point to store a separate MAC in the CipherText object. Thus we only
+             // do this when we are not using such a cipher mode.
+			 if ( !preferredCipherMode ) {
+			     // Compute derived key, and then use it to compute and store separate MAC in CipherText object.
+			     SecretKey authKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(),
+			    		 							   key, keySize, "authenticity");
+			     ciphertext.computeAndStoreMAC(  authKey );
+			 }
+			 logger.debug(Logger.EVENT_SUCCESS, "JavaEncryptor.encrypt(SecretKey,byte[],boolean,boolean) -- success!");
+			 success = true;	// W00t!!!
+			 return ciphertext;
+		} catch (InvalidKeyException ike) {
+			 throw new EncryptionException("Encryption failure: Invalid key exception.",
+					 "Requested key size: " + keySize + "bits greater than 128 bits. Must install unlimited strength crypto extension from Sun: " +
+					 ike.getMessage(), ike);
+		 } catch (ConfigurationException cex) {
+			 throw new EncryptionException("Encryption failure: Configuration error. Details in log.", "Key size mismatch or unsupported IV method. " +
+					 "Check encryption key size vs. ESAPI.EncryptionKeyLength or Encryptor.ChooseIVMethod property.", cex);
+		 } catch (InvalidAlgorithmParameterException e) {
+			 throw new EncryptionException("Encryption failure (invalid IV)",
+					 "Encryption problem: Invalid IV spec: " + e.getMessage(), e);
+		 } catch (IllegalBlockSizeException e) {
+			 throw new EncryptionException("Encryption failure (no padding used; invalid input size)",
+					 "Encryption problem: Invalid input size without padding (" + xform + "). " + e.getMessage(), e);
+		 } catch (BadPaddingException e) {
+			 throw new EncryptionException("Encryption failure",
+					 "[Note: Should NEVER happen in encryption mode.] Encryption problem: " + e.getMessage(), e);
+		 } catch (NoSuchAlgorithmException e) {
+			 throw new EncryptionException("Encryption failure (unavailable cipher requested)",
+					 "Encryption problem: specified algorithm in cipher xform " + xform + " not available: " + e.getMessage(), e);
+		 } catch (NoSuchPaddingException e) {
+			 throw new EncryptionException("Encryption failure (unavailable padding scheme requested)",
+					 "Encryption problem: specified padding scheme in cipher xform " + xform + " not available: " + e.getMessage(), e);
+		 } finally {
+			 // Don't overwrite anything in the case of exceptions because they may wish to retry.
+			 if ( success && overwritePlaintext ) {
+				 plain.overwrite();		// Note: Same as overwriting 'plaintext' byte array.
+		}
+	}
+	 }
+
+	/**
+	* {@inheritDoc}
+	*/
+	public PlainText decrypt(CipherText ciphertext) throws EncryptionException {
+		 // Now more of a convenience function for using the master key.
+		 return decrypt(secretKeySpec, ciphertext);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public PlainText decrypt(SecretKey key, CipherText ciphertext)
+	    throws EncryptionException, IllegalArgumentException
+	{
+	    long start = System.nanoTime();  // Current time in nanosecs; used to prevent timing attacks
+	    if ( key == null ) {
+	        throw new IllegalArgumentException("SecretKey arg may not be null");
+	    }
+	    if ( ciphertext == null ) {
+	        throw new IllegalArgumentException("Ciphertext may arg not be null");
+	    }
+
+	    if ( ! CryptoHelper.isAllowedCipherMode(ciphertext.getCipherMode()) ) {
+	        // This really should be an illegal argument exception, but it could
+	        // mean that a partner encrypted something using a cipher mode that
+	        // you do not accept, so it's a bit more complex than that. Also
+	        // throwing an IllegalArgumentException doesn't allow us to provide
+	        // the two separate error messages or automatically log it.
+	        throw new EncryptionException(DECRYPTION_FAILED,
+	                "Invalid cipher mode " + ciphertext.getCipherMode() +
+	        " not permitted for decryption or encryption operations.");
+	    }
+	    logger.debug(Logger.EVENT_SUCCESS,
+	            "Args valid for JavaEncryptor.decrypt(SecretKey,CipherText): " +
+	            ciphertext);
+
+	    PlainText plaintext = null;
+	    boolean caughtException = false;
+	    int progressMark = 0;
+	    try {
+	        // First we validate the MAC.
+	        boolean valid = CryptoHelper.isCipherTextMACvalid(key, ciphertext);
+	        if ( !valid ) {
+	            try {
+	                // This is going to fail, but we want the same processing
+	                // to occur as much as possible so as to prevent timing
+	                // attacks. We _could_ just be satisfied by the additional
+	                // sleep in the 'finally' clause, but an attacker on the
+	                // same server who can run something like 'ps' can tell
+	                // CPU time versus when the process is sleeping. Hence we
+	                // try to make this as close as possible. Since we know
+	                // it is going to fail, we ignore the result and ignore
+	                // the (expected) exception.
+	                handleDecryption(key, ciphertext); // Ignore return (should fail).
+	            } catch(Exception ex) {
+	                ;   // Ignore
+	            }
+	            throw new EncryptionException(DECRYPTION_FAILED,
+	                    "Decryption failed because MAC invalid for " +
+	                    ciphertext);
+	        }
+	        progressMark++;
+	        // The decryption only counts if the MAC was valid.
+	        plaintext = handleDecryption(key, ciphertext);
+	        progressMark++;
+	    } catch(EncryptionException ex) {
+	        caughtException = true;
+	        String logMsg = null;
+	        switch( progressMark ) {
+	        case 1:
+	            logMsg = "Decryption failed because MAC invalid. See logged exception for details.";
+	            break;
+	        case 2:
+	            logMsg = "Decryption failed because handleDecryption() failed. See logged exception for details.";
+	            break;
+	        default:
+	            logMsg = "Programming error: unexpected progress mark == " + progressMark;
+	        break;
+	        }
+	        logger.error(Logger.SECURITY_FAILURE, logMsg);
+	        throw ex;           // Re-throw
+	    }
+	    finally {
+	        if ( caughtException ) {
+	            // The rest of this code is to try to account for any minute differences
+	            // in the time it might take for the various reasons that decryption fails
+	            // in order to prevent any other possible timing attacks. Perhaps it is
+	            // going overboard. If nothing else, if N_SECS is large enough, it might
+	            // deter attempted repeated attacks by making them take much longer.
+	            long now = System.nanoTime();
+	            long elapsed = now - start;
+	            final long NANOSECS_IN_SEC = 1000000000L; // nanosec is 10**-9 sec
+	            long nSecs = N_SECS * NANOSECS_IN_SEC;  // N seconds in nano seconds
+	            if ( elapsed < nSecs ) {
+	                // Want to sleep so total time taken is N seconds.
+	                long extraSleep = nSecs - elapsed;
+
+	                // 'extraSleep' is in nanoseconds. Need to convert to a millisec
+	                // part and nanosec part. Nanosec is 10**-9, millsec is
+	                // 10**-3, so divide by (10**-9 / 10**-3), or 10**6 to
+	                // convert to from nanoseconds to milliseconds.
+	                long millis = extraSleep / 1000000L;
+	                long nanos  = (extraSleep - (millis * 1000000L));
+	                assert nanos >= 0 && nanos <= Integer.MAX_VALUE :
+                            "Nanosecs out of bounds; nanos = " + nanos;
+	                try {
+	                    Thread.sleep(millis, (int)nanos);
+	                } catch(InterruptedException ex) {
+	                    ;   // Ignore
+	                }
+	            } // Else ... time already exceeds N_SECS sec, so do not sleep.
+	        }
+	    }
+	    return plaintext;
+	}
+
+    // Handle the actual decryption portion. At this point it is assumed that
+    // any MAC has already been validated. (But see "DISCUSS" issue, below.)
+    private PlainText handleDecryption(SecretKey key, CipherText ciphertext)
+        throws EncryptionException
+    {
+        int keySize = 0;
+        try {
+            Cipher decrypter = Cipher.getInstance(ciphertext.getCipherTransformation());
+            keySize = key.getEncoded().length * 8;  // Convert to # bits
+
+            // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then
+            // use the specified SecretKey as-is rather than computing a derived key from it. We also
+            // don't expect a separate MAC in the specified CipherText object so therefore don't try
+            // to validate it.
+            boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( ciphertext.getCipherMode() );
+            SecretKey encKey = null;
+            if ( preferredCipherMode ) {
+                encKey = key;
+            } else {
+                // TODO: PERFORMANCE: Calculate avg time this takes and consider caching for very short interval
+                //       (e.g., 2 to 5 sec tops). Otherwise doing lots of encryptions in a loop could take a LOT longer.
+                //       But remember Jon Bentley's "Rule #1 on performance: First make it right, then make it fast."
+            	//		 This would be a security trade-off as it would leave keys in memory a bit longer, so it
+            	//		 should probably be off by default and controlled via a property.
+            	//
+            	// TODO: Feed in some additional parms here to use as the 'context' for the
+            	//		 KeyDerivationFunction...especially the KDF version. We would have to
+            	//		 store that in the CipherText object. We *possibly* could make it
+            	//		 transient so it would not be serialized with the CipherText object,
+            	//		 otherwise we would have to implement readObject() and writeObject()
+            	//		 methods there to support backward compatibility. Anyhow the intent
+            	//		 is to prevent down grade attacks when we finally re-design and
+            	//		 re-implement the MAC. Think about this in version 2.1.1.
+                encKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(),
+                		                    key, keySize, "encryption");
+            }
+            if ( ciphertext.requiresIV() ) {
+                decrypter.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(ciphertext.getIV()));
+            } else {
+                decrypter.init(Cipher.DECRYPT_MODE, encKey);
+            }
+            byte[] output = decrypter.doFinal(ciphertext.getRawCipherText());
+            return new PlainText(output);
+
+        } catch (InvalidKeyException ike) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Must install JCE Unlimited Strength Jurisdiction Policy Files from Sun", ike);
+        } catch (NoSuchAlgorithmException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Invalid algorithm for available JCE providers - " +
+                    ciphertext.getCipherTransformation() + ": " + e.getMessage(), e);
+        } catch (NoSuchPaddingException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Invalid padding scheme (" +
+                    ciphertext.getPaddingScheme() + ") for cipher transformation " + ciphertext.getCipherTransformation() +
+                    ": " + e.getMessage(), e);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
+        } catch (IllegalBlockSizeException e) {
+            throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
+        } catch (BadPaddingException e) {
+            //DISCUSS: This needs fixed. Already validated MAC in CryptoHelper.isCipherTextMACvalid() above.
+            //So only way we could get a padding exception is if invalid padding were used originally by
+            //the party doing the encryption. (This might happen with a buggy padding scheme for instance.)
+            //It *seems* harmless though, so will leave it for now, and technically, we need to either catch it
+            //or declare it in a throws class. Clearly we don't want to do the later. This should be discussed
+            //during a code inspection.
+            SecretKey authKey;
+            try {
+                authKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(),
+                		                     key, keySize, "authenticity");
+            } catch (Exception e1) {
+                throw new EncryptionException(DECRYPTION_FAILED,
+                        "Decryption problem -- failed to compute derived key for authenticity: " + e1.getMessage(), e1);
+            }
+            boolean success = ciphertext.validateMAC( authKey );
+            if ( success ) {
+                throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
+            } else {
+                throw new EncryptionException(DECRYPTION_FAILED,
+                        "Decryption problem: WARNING: Adversary may have tampered with " +
+                        "CipherText object orCipherText object mangled in transit: " + e.getMessage(), e);
+            }
+        }
+    }
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public String sign(String data) throws EncryptionException {
+		try {
+			Signature signer = Signature.getInstance(signatureAlgorithm);
+			signer.initSign(privateKey);
+			signer.update(data.getBytes(encoding));
+			byte[] bytes = signer.sign();
+			return ESAPI.encoder().encodeForBase64(bytes, false);
+		} catch (InvalidKeyException ike) {
+			throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike);
+		} catch (Exception e) {
+			throw new EncryptionException("Signature failure", "Can't find signature algorithm " + signatureAlgorithm, e);
+		}
+	}
+		
+	/**
+	* {@inheritDoc}
+	*/
+	public boolean verifySignature(String signature, String data) {
+		try {
+			byte[] bytes = ESAPI.encoder().decodeFromBase64(signature);
+			Signature signer = Signature.getInstance(signatureAlgorithm);
+			signer.initVerify(publicKey);
+			signer.update(data.getBytes(encoding));
+			return signer.verify(bytes);
+		} catch (Exception e) {
+		    // NOTE: EncryptionException constructed *only* for side-effect of causing logging.
+		    // FindBugs complains about this and since it examines byte-code, there's no way to
+		    // shut it up.
+			new EncryptionException("Invalid signature", "Problem verifying signature: " + e.getMessage(), e);
+			return false;
+		}
+	}
+
+	/**
+	* {@inheritDoc}
+     *
+     * @param expiration
+     * @throws IntegrityException
+     */
+	public String seal(String data, long expiration) throws IntegrityException {
+	    if ( data == null ) {
+	        throw new IllegalArgumentException("Data to be sealed may not be null.");
+	    }
+	    
+		try {
+		    String b64data = null;
+            try {
+                b64data = ESAPI.encoder().encodeForBase64(data.getBytes("UTF-8"), false);
+            } catch (UnsupportedEncodingException e) {
+                ; // Ignore; should never happen since UTF-8 built into rt.jar
+            }
+			// mix in some random data so even identical data and timestamp produces different seals
+			String nonce = ESAPI.randomizer().getRandomString(10, EncoderConstants.CHAR_ALPHANUMERICS);
+			String plaintext = expiration + ":" + nonce + ":" + b64data;
+			// add integrity check; signature is already base64 encoded.
+			String sig = this.sign( plaintext );
+			CipherText ciphertext = this.encrypt( new PlainText(plaintext + ":" + sig) );
+			String sealedData = ESAPI.encoder().encodeForBase64(ciphertext.asPortableSerializedByteArray(), false);
+			return sealedData;
+		} catch( EncryptionException e ) {
+			throw new IntegrityException( e.getUserMessage(), e.getLogMessage(), e );
+		}
+	}
+
+	/**
+	* {@inheritDoc}
+	*/
+	public String unseal(String seal) throws EncryptionException {
+		PlainText plaintext = null;
+		try {
+		    byte[] encryptedBytes = ESAPI.encoder().decodeFromBase64(seal);
+		    CipherText cipherText = null;
+		    try {
+		        cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes);
+		    } catch( AssertionError e) {
+	            // Some of the tests in EncryptorTest.testVerifySeal() are examples of
+		        // this if assertions are enabled.
+		        throw new EncryptionException("Invalid seal",
+	                                          "Seal passed garbarge data resulting in AssertionError: " + e);
+	        }
+			plaintext = this.decrypt(cipherText);
+
+			String[] parts = plaintext.toString().split(":");
+			if (parts.length != 4) {
+				throw new EncryptionException("Invalid seal", "Seal was not formatted properly.");
+			}
+	
+			String timestring = parts[0];
+			long now = new Date().getTime();
+			long expiration = Long.parseLong(timestring);
+			if (now > expiration) {
+				throw new EncryptionException("Invalid seal", "Seal expiration date of " + new Date(expiration) + " has past.");
+			}
+			String nonce = parts[1];
+			String b64data = parts[2];
+			String sig = parts[3];
+			if (!this.verifySignature(sig, timestring + ":" + nonce + ":" + b64data ) ) {
+				throw new EncryptionException("Invalid seal", "Seal integrity check failed");
+			}	
+			return new String(ESAPI.encoder().decodeFromBase64(b64data), "UTF-8");
+		} catch (EncryptionException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new EncryptionException("Invalid seal", "Invalid seal:" + e.getMessage(), e);
+		}
+	}
+
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public boolean verifySeal( String seal ) {
+		try {
+			unseal( seal );
+			return true;
+		} catch( EncryptionException e ) {
+			return false;
+		}
+	}
+	
+	/**
+	* {@inheritDoc}
+	*/
+	public long getTimeStamp() {
+		return new Date().getTime();
+	}
+
+	/**
+	* {@inheritDoc}
+	*/
+	public long getRelativeTimeStamp( long offset ) {
+		return new Date().getTime() + offset;
+	}
+
+	// DISCUSS: Why experimental? Would have to be added to Encryptor interface
+	//			but only 3 things I saw wrong with this was 1) it used HMacMD5 instead
+	//			of HMacSHA1 (see discussion below), 2) that the HMac key is the
+	//			same one used for encryption (also see comments), and 3) it caught
+	//			overly broad exceptions. Here it is with these specific areas
+	//			addressed, but no unit testing has been done at this point. -kww
+   /**
+    * Compute an HMAC for a String.  Experimental.
+    * @param input	The input for which to compute the HMac.
+    */
+/********************
+	public String computeHMAC( String input ) throws EncryptionException {
+		try {
+			Mac hmac = Mac.getInstance("HMacSHA1"); // DISCUSS: Changed to HMacSHA1. MD5 *badly* broken
+												   //          SHA1 should really be avoided, but using
+												   //		   for HMAC-SHA1 is acceptable for now. Plan
+												   //		   to migrate to SHA-256 or NIST replacement for
+												   //		   SHA1 in not too distant future.
+			// DISCUSS: Also not recommended that the HMac key is the same as the one
+			//			used for encryption (namely, Encryptor.MasterKey). If anything it
+			//			would be better to use Encryptor.MasterSalt for the HMac key, or
+			//			perhaps a derived key based on the master salt. (One could use
+			//			KeyDerivationFunction.computeDerivedKey().)
+			//
+			byte[] salt = ESAPI.securityConfiguration().getMasterSalt();
+			hmac.init( new SecretKeySpec(salt, "HMacSHA1") );	// Was:	hmac.init(secretKeySpec)	
+			byte[] inBytes;
+			try {
+				inBytes = input.getBytes("UTF-8");
+			} catch (UnsupportedEncodingException e) {
+				logger.warning(Logger.SECURITY_FAILURE, "computeHMAC(): Can't find UTF-8 encoding; using default encoding", e);
+				inBytes = input.getBytes();
+			}
+			byte[] bytes = hmac.doFinal( inBytes );
+			return ESAPI.encoder().encodeForBase64(bytes, false);
+		} catch (InvalidKeyException ike) {
+			throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike);
+	    } catch (NoSuchAlgorithmException e) {
+	    	throw new EncryptionException("Could not compute HMAC", "Can't find HMacSHA1 algorithm. " +
+	    															"Problem computing HMAC for " + input, e );
+	    }
+	}
+********************/
+
+    /**
+     * Log a security warning every Nth time one of the deprecated encrypt or
+     * decrypt methods are called. ('N' is hard-coded to be 25 by default, but
+     * may be changed via the system property
+     * {@code ESAPI.Encryptor.warnEveryNthUse}.) In other words, we nag
+     * them until the give in and change it. ;-)
+     * 
+     * @param where The string "encrypt" or "decrypt", corresponding to the
+     *              method that is being logged.
+     * @param msg   The message to log.
+     */
+    private void logWarning(String where, String msg) {
+        int counter = 0;
+        if ( where.equals("encrypt") ) {
+            counter = encryptCounter++;
+            where = "JavaEncryptor.encrypt(): [count=" + counter +"]";
+        } else if ( where.equals("decrypt") ) {
+            counter = decryptCounter++;
+            where = "JavaEncryptor.decrypt(): [count=" + counter +"]";
+        } else {
+            where = "JavaEncryptor: Unknown method: ";
+        }
+        // We log the very first time (note the use of post-increment on the
+        // counters) and then every Nth time thereafter. Logging every single
+        // time is likely to be way too much logging.
+        if ( (counter % logEveryNthUse) == 0 ) {
+            logger.warning(Logger.SECURITY_FAILURE, where + msg);
+        }
+    }
+    
+    private KeyDerivationFunction.PRF_ALGORITHMS getPRF(String name) {    	
+		String prfName = null;
+		if ( name == null ) {
+			prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		} else {
+			prfName = name;
+		}
+		KeyDerivationFunction.PRF_ALGORITHMS prf = KeyDerivationFunction.convertNameToPRF(prfName);
+		return prf;
+    }
+    
+    private KeyDerivationFunction.PRF_ALGORITHMS getDefaultPRF() {
+		String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
+		return getPRF(prfName);
+    }
+    
+    // Private interface to call ESAPI's KDF to get key for encryption or authenticity.
+    private SecretKey computeDerivedKey(int kdfVersion, KeyDerivationFunction.PRF_ALGORITHMS prf,
+    									SecretKey kdk, int keySize, String purpose)
+    	throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException
+    {
+    	// These really should be turned into actual runtime checks and an
+    	// IllegalArgumentException should be thrown if they are violated.
+    	// But this should be OK since this is a private method. Also, this method will
+    	// be called quite often so assertions are a big win as they can be disabled or
+    	// enabled at will.
+    	assert prf != null : "Pseudo Random Function for KDF cannot be null";
+    	assert kdk != null : "Key derivation key cannot be null.";
+    	// We would choose a larger minimum key size, but we want to be
+    	// able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that,
+    	// we print warning.
+    	assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
+    	assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
+    	assert purpose != null : "Purpose cannot be null. Should be 'encryption' or 'authenticity'.";
+    	assert purpose.equals("encryption") || purpose.equals("authenticity") :
+    		"Purpose must be \"encryption\" or \"authenticity\".";
+
+    	KeyDerivationFunction kdf = new KeyDerivationFunction(prf);
+    	if ( kdfVersion != 0 ) {
+    		kdf.setVersion(kdfVersion);
+    	}
+    	return kdf.computeDerivedKey(kdk, keySize, purpose);
+    }
+
+    // Get all the algorithms we will be using from ESAPI.properties.
+    private static void setupAlgorithms() {
+        // setup algorithms
+        encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm();
+        signatureAlgorithm = ESAPI.securityConfiguration().getDigitalSignatureAlgorithm();
+        randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm();
+        hashAlgorithm = ESAPI.securityConfiguration().getHashAlgorithm();
+        hashIterations = ESAPI.securityConfiguration().getHashIterations();
+        encoding = ESAPI.securityConfiguration().getCharacterEncoding();
+        encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength();
+        signatureKeyLength = ESAPI.securityConfiguration().getDigitalSignatureKeyLength();
+    }
+    
+    // Set up signing key pair using the master password and salt. Called (once)
+    // from the JavaEncryptor CTOR.
+    private static void initKeyPair(SecureRandom prng) throws NoSuchAlgorithmException {
+        String sigAlg = signatureAlgorithm.toLowerCase();
+        if ( sigAlg.endsWith("withdsa") ) {
+            //
+            // Admittedly, this is a kludge. However for Sun JCE, even though
+            // "SHA1withDSA" is a valid signature algorithm name, if one calls
+            //      KeyPairGenerator kpg = KeyPairGenerator.getInstance("SHA1withDSA");
+            // that will throw a NoSuchAlgorithmException with an exception
+            // message of "SHA1withDSA KeyPairGenerator not available". Since
+            // SHA1withDSA and DSA keys should be identical, we use "DSA"
+            // in the case that SHA1withDSA or SHAwithDSA was specified. This is
+            // all just to make these 2 work as expected. Sigh. (Note:
+            // this was tested with JDK 1.6.0_21, but likely fails with earlier
+            // versions of the JDK as well.)
+            //
+            sigAlg = "DSA";
+        } else if ( sigAlg.endsWith("withrsa") ) {
+            // Ditto for RSA.
+            sigAlg = "RSA";
+        }
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance(sigAlg);
+        keyGen.initialize(signatureKeyLength, prng);
+        KeyPair pair = keyGen.generateKeyPair();
+        privateKey = pair.getPrivate();
+        publicKey = pair.getPublic();
+    }
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java b/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java
new file mode 100644
index 0000000..cffc149
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java
@@ -0,0 +1,293 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncryptedProperties;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionRuntimeException;
+
+/**
+ * Reference implementation of the {@code EncryptedProperties} interface. This
+ * implementation wraps a normal properties file, and creates surrogates for the
+ * {@code getProperty} and {@code setProperty} methods that perform encryption
+ * and decryption based on {@code Encryptor}.
+ * <p>
+ * This implementation differs from {@code DefaultEncryptedProperties} in that
+ * it actually extends from {@code java.util.Properties} for applications that need an
+ * instance of that class. In order to do so, the {@code getProperty} and
+ * {@code setProperty} methods were modified to throw {@code EncryptionRuntimeException}
+ * instead of {@code EncryptionException}.
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @author kevin.w.wall at gmail.com
+ * @since October 8, 2010
+ * @see org.owasp.esapi.EncryptedProperties
+ * @see org.owasp.esapi.reference.crypto.DefaultEncryptedProperties
+ */
+public class ReferenceEncryptedProperties extends java.util.Properties implements EncryptedProperties {
+
+	/**
+	 * serverVersionUID; use format of YYYYMMDD.
+	 */
+	private static final long serialVersionUID = 20120718L;
+
+	/** The logger. */
+	private final Logger logger = ESAPI.getLogger(this.getClass());
+
+	private static final String[] GET_ERROR_MESSAGES = new String[]{
+		": failed decoding from base64",
+		": failed to deserialize properly",
+		": failed to decrypt properly"
+	};
+
+	private static final String[] SET_ERROR_MESSAGES = new String[]{
+		": failed to encrypt properly",
+		": failed to serialize correctly",
+		": failed to base64-encode properly",
+		": failed to set base64-encoded value as property. Illegal key name?"
+	};
+
+	/**
+	 * Instantiates a new encrypted properties.
+	 */
+	public ReferenceEncryptedProperties() {
+		super();
+	}
+
+	public ReferenceEncryptedProperties(Properties defaults) {
+		super();
+
+		for (Object oKey : defaults.keySet()) {
+			String key		= (oKey instanceof String) ? (String)oKey : oKey.toString();
+			String value	= defaults.getProperty(key);
+
+			this.setProperty(key, value);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws EncryptionRuntimeException Thrown if decryption fails.
+	 */
+	@Override
+	public synchronized String getProperty(String key) throws EncryptionRuntimeException {
+	    int progressMark = 0;
+	    try {
+	        String encryptedValue = super.getProperty(key);
+
+	        if(encryptedValue==null)
+	            return null;
+
+	        progressMark = 0;
+	        byte[] serializedCiphertext   = ESAPI.encoder().decodeFromBase64(encryptedValue);
+	        progressMark++;
+	        CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(serializedCiphertext);
+	        progressMark++;
+	        PlainText plaintext           = ESAPI.encryptor().decrypt(restoredCipherText);
+
+	        return plaintext.toString();
+		} catch (Exception e) {
+			throw new EncryptionRuntimeException("Property retrieval failure",
+					                             "Couldn't retrieve encrypted property for property " + key +
+												 GET_ERROR_MESSAGES[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws EncryptionRuntimeException Thrown if decryption fails.
+	 */
+	@Override
+	public String getProperty(String key, String defaultValue) throws EncryptionRuntimeException {
+		String value = getProperty(key);
+
+		if (value == null) return defaultValue;
+
+		return value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws EncryptionRuntimeException Thrown if encryption fails.
+	 */
+	@Override
+	public synchronized String setProperty(String key, String value) throws EncryptionRuntimeException {
+	    int progressMark = 0;
+	    try {
+	        if ( key == null ) {
+	            throw new NullPointerException("Property name may not be null.");
+	        }
+	        if ( value == null ) {
+	            throw new NullPointerException("Property value may not be null.");
+	        }
+	        // NOTE: Not backward compatible w/ ESAPI 1.4.
+	        PlainText pt = new PlainText(value);
+	        CipherText ct = ESAPI.encryptor().encrypt(pt);
+	        progressMark++;
+	        byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
+	        progressMark++;
+	        String b64str = ESAPI.encoder().encodeForBase64(serializedCiphertext, false);
+	        progressMark++;
+	        return (String)super.put(key, b64str);
+	    } catch (Exception e) {
+	        throw new EncryptionRuntimeException("Property setting failure",
+	                                      "Couldn't set encrypted property " + key +
+	                                      SET_ERROR_MESSAGES[progressMark], e);
+	    }
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @throws IOException Thrown if input stream invalid or does not
+	 * 					   correspond to Java properties file format.
+	 */
+	@Override
+	public void load(InputStream in) throws IOException {
+		super.load(in);
+		logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * For JDK 1.5 compatibility, this method has been overridden convert the Reader
+	 * into an InputStream and call the superclass constructor.
+	 * 
+	 * @throws IOException Thrown if {@code Reader} input stream invalid or does not
+	 * 					   correspond to Java properties file format.
+	 */
+	public void load(Reader in) throws IOException {
+
+		if (in == null) return;
+
+		//read from the reader into a StringBuffer
+		char[] cbuf				= new char[65536];
+		BufferedReader buff		= new BufferedReader(in);
+		StringBuilder contents	= new StringBuilder();
+
+		int read_this_time = 0;
+		while (read_this_time != -1) {
+			read_this_time = buff.read(cbuf, 0, 65536);
+			if (read_this_time > 0) contents.append(cbuf, 0, read_this_time);
+		}
+
+		//create a new InputStream from the StringBuffer
+		InputStream is = new ByteArrayInputStream(contents.toString().getBytes());
+
+		super.load(is);
+		logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@Override
+	public void list(PrintStream out) {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@Override
+	public void list(PrintWriter out) {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	@Override
+	public Collection values() {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	@Override
+	public Set entrySet() {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to throw an {@code UnsupportedOperationException}
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	@Override
+	public Enumeration elements() {
+		throw new UnsupportedOperationException("This method has been removed for security.");
+	}
+
+	/**
+	 * This method has been overridden to only accept Strings for key and value, and to encrypt
+	 * those Strings before storing them. Outside classes should always use {@code setProperty}
+	 * to add values to the Properties map. If an outside class does erroneously call this method 
+	 * with non-String parameters an {@code IllegalArgumentException} will be thrown.
+	 *
+	 * @param key	A String key to add
+	 * @param value A String value to add
+	 * @return		The old value associated with the specified key, or {@code null}
+     *				if the key did not exist.
+	 */
+	@Override
+	public synchronized Object put(Object key, Object value) {
+		//if java.util.Properties is calling this method, just forward to the implementation in
+		//the superclass (java.util.Hashtable)
+		Throwable t = new Throwable();
+		for (StackTraceElement trace : t.getStackTrace()) {
+			if ("java.util.Properties".equals(trace.getClassName()) ) return super.put(key, value);
+		}
+
+		//otherwise, if both arguments are Strings, encrypt and store them
+		if (key instanceof String && value instanceof String) return setProperty((String)key, (String)value);
+
+		//other Object types are not allowed
+		throw new IllegalArgumentException("This method has been overridden to only accept Strings for key and value.");
+	}
+
+	/**
+	 * This method has been overridden to not print out the keys and values stored in this properties file.
+	 *
+	 * @return The minimal String representation of this class, as per java.lang.Object.
+	 */
+	@Override
+	public String toString() {
+		return getClass().getName() + "@" + Integer.toHexString(hashCode());
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/reference/crypto/package.html b/src/main/java/org/owasp/esapi/reference/crypto/package.html
new file mode 100644
index 0000000..9114070
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/crypto/package.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body bgcolor="white">
+This package contains the reference implementation for some of
+the ESAPI cryptography-related classes used throughout ESAPI.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/package.html b/src/main/java/org/owasp/esapi/reference/package.html
new file mode 100644
index 0000000..0e110cb
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/package.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains reference implementations of the ESAPI interfaces. These are intended to
+serve as examples of how your enterprise might implement these functions. The reference implementations
+are high quality and pass all of the ESAPI test cases. Many of the reference implementations are
+likely to be useful in your enterprise without change (Validator, Encoder, Encryptor, etc...).
+Implementing other classes (Authenticator, User, AccessController, Logger, etc...) will likely need to
+be customized for your enterprise, to integrate with your backend systems and policies.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/all-wcprops b/src/main/java/org/owasp/esapi/reference/validation/.svn/all-wcprops
new file mode 100644
index 0000000..8b618ba
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/all-wcprops
@@ -0,0 +1,53 @@
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation
+END
+StringValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java
+END
+NumberValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 112
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/NumberValidationRule.java
+END
+BaseValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 110
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/package.html
+END
+IntegerValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/IntegerValidationRule.java
+END
+DateValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 110
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/DateValidationRule.java
+END
+CreditCardValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/CreditCardValidationRule.java
+END
+HTMLValidationRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 110
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java
+END
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/entries b/src/main/java/org/owasp/esapi/reference/validation/.svn/entries
new file mode 100644
index 0000000..99dae56
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/entries
@@ -0,0 +1,300 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/reference/validation
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-03-22T22:12:04.412631Z
+1736
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+CreditCardValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+f1bffb9d6b9311e2f6d10cd0fe4bb6d2
+2009-12-05T03:08:44.297728Z
+854
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4821
+

+HTMLValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+6fd8981191959be9b7b93d34600fa1c9
+2011-03-22T22:12:04.412631Z
+1736
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4367
+

+StringValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+83b60aba577db6c3c60e3cf7df871955
+2010-09-03T23:15:54.919682Z
+1512
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10663
+

+NumberValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+a8b14df135afe80bf27c30f85b61e5a5
+2011-02-23T23:35:02.398077Z
+1725
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5599
+

+BaseValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+3a1baac3507ef882e83895c8da5b6802
+2009-11-09T04:39:12.162255Z
+761
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5334
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+7039f48ce07159efae81b996a9a98fa0
+2009-11-29T23:14:48.308067Z
+842
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+202
+

+IntegerValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+f0eb209f5e0d70397d69ce02f40d386a
+2010-03-26T09:58:32.453711Z
+1224
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3697
+

+DateValidationRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.841964Z
+c901531a1b9bd7febf9902f72bb2c366
+2011-03-22T02:16:39.985954Z
+1731
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3337
+

diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/BaseValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/BaseValidationRule.java.svn-base
new file mode 100644
index 0000000..1929ff0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/BaseValidationRule.java.svn-base
@@ -0,0 +1,198 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.ValidationRule;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * A ValidationRule performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public abstract class BaseValidationRule implements ValidationRule {
+
+	private String typeName = null;
+	protected boolean allowNull = false;
+	protected Encoder encoder = null;
+	
+	private BaseValidationRule() {
+		// prevent use of no-arg constructor
+	}
+	
+	public BaseValidationRule( String typeName ) {
+		this();
+		setEncoder( ESAPI.encoder() );
+		setTypeName( typeName );
+	}
+	
+	public BaseValidationRule( String typeName, Encoder encoder ) {
+		this();
+		setEncoder( encoder );
+		setTypeName( typeName );
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public void setAllowNull( boolean flag ) {
+		allowNull = flag;
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public String getTypeName() {
+		return typeName;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public final void setTypeName( String typeName ) {
+		this.typeName = typeName;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public final void setEncoder( Encoder encoder ) {
+		this.encoder = encoder;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public void assertValid( String context, String input ) throws ValidationException {
+		getValid( context, input, null );
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public Object getValid( String context, String input, ValidationErrorList errorList ) throws ValidationException {
+		Object valid = null;
+		try {
+			valid = getValid( context, input );
+		} catch (ValidationException e) {
+			errorList.addError(context, e);
+		}
+		return valid;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public Object getSafe( String context, String input ) {
+		Object valid = null;
+		try {
+			valid = getValid( context, input );
+		} catch ( ValidationException e ) {
+			return sanitize( context, input );
+		}
+		return valid;
+	}
+
+	/**
+	 * The method is similar to ValidationRuile.getSafe except that it returns a
+	 * harmless object that <b>may or may not have any similarity to the original
+	 * input (in some cases you may not care)</b>. In most cases this should be the
+	 * same as the getSafe method only instead of throwing an exception, return
+	 * some default value.
+	 * 
+	 * @param context
+	 * @param input
+	 * @return a parsed version of the input or a default value.
+	 */
+	protected abstract Object sanitize( String context, String input );
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public boolean isValid( String context, String input ) {
+		boolean valid = false;
+		try {
+			getValid( context, input );
+			valid = true;
+		} catch( Exception e ) {
+			valid = false;
+		}
+		
+		return valid;
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public String whitelist( String input, char[] whitelist) {
+		return whitelist(input, charArrayToSet(whitelist));
+	}
+	
+	/**
+	 * Removes characters that aren't in the whitelist from the input String.  
+	 * O(input.length) whitelist performance
+	 * @param input String to be sanitized
+	 * @param whitelist allowed characters
+	 * @return input stripped of all chars that aren't in the whitelist 
+	 */
+	public String whitelist( String input, Set<Character> whitelist) {
+		StringBuilder stripped = new StringBuilder();
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			if (whitelist.contains(c)) {
+				stripped.append(c);
+			}
+		}
+		return stripped.toString();
+	}
+	
+	// CHECKME should be moved to some utility class (Would potentially be used by new EncoderConstants class)
+	// Is there a standard way to convert an array of primitives to a Collection
+	/**
+	 * Convert an array of characters to a {@code Set<Character>} (so duplicates
+	 * are removed).
+	 * @param array The character array.
+	 * @return A {@code Set<Character>} of the unique characters from {@code array}
+	 *         is returned.
+	 */
+	public static Set<Character> charArrayToSet(char[] array) {
+		Set<Character> toReturn = new HashSet<Character>(array.length);
+		for (char c : array) {
+			toReturn.add(c);
+		}
+		return toReturn;
+	}
+	
+	public boolean isAllowNull() {
+		return allowNull;
+	}
+
+	public Encoder getEncoder() {
+		return encoder;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/CreditCardValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/CreditCardValidationRule.java.svn-base
new file mode 100644
index 0000000..86f7d4c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/CreditCardValidationRule.java.svn-base
@@ -0,0 +1,165 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.util.regex.Pattern;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * A validator performs syntax and possibly semantic validation of Credit Card
+ * String from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class CreditCardValidationRule extends BaseValidationRule {
+	private int maxCardLength = 19;
+	
+	/**
+	 * Key used to pull out encoder in configuration.  Prefixed with "Validator."
+	 */
+	protected static final String CREDIT_CARD_VALIDATOR_KEY = "CreditCard";
+	
+	private StringValidationRule ccrule = null; 
+	
+	/**
+	 * Creates a CreditCardValidator using the rule found in security configuration
+	 * @param typeName a description of the type of card being validated
+	 * @param encoder
+	 */
+	public CreditCardValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+		ccrule = readDefaultCreditCardRule();
+	}
+	
+	public CreditCardValidationRule( String typeName, Encoder encoder, StringValidationRule validationRule ) {
+		super( typeName, encoder );
+		ccrule = validationRule;
+	}
+
+	private StringValidationRule readDefaultCreditCardRule() {
+		Pattern p = ESAPI.securityConfiguration().getValidationPattern( CREDIT_CARD_VALIDATOR_KEY );
+		StringValidationRule ccr = new StringValidationRule( "ccrule", encoder, p.pattern() );
+		ccr.setMaximumLength(getMaxCardLength());
+		ccr.setAllowNull( false );
+		return ccr;
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	public String getValid( String context, String input ) throws ValidationException {
+		// CHECKME should this allow empty Strings? "   " us IsBlank instead?
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + ": Input credit card required", "Input credit card required: context=" + context + ", input=" + input, context );
+	    }
+	    
+	    String canonical = ccrule.getValid( context, input );
+
+		if( ! validCreditCardFormat(canonical)) {
+			throw new ValidationException( context + ": Invalid credit card input", "Invalid credit card input: context=" + context, context );
+		}
+		
+		return canonical;	    
+	}
+
+	/**
+	 * Performs additional validation on the card nummber.
+	 * This implementation performs Luhn algorithm checking
+	 * @param ccNum number to be validated
+	 * @return true if the ccNum passes the Luhn Algorithm
+	 */
+	protected boolean validCreditCardFormat(String ccNum) {
+		
+	    StringBuilder digitsOnly = new StringBuilder();
+		char c;
+		for (int i = 0; i < ccNum.length(); i++) {
+			c = ccNum.charAt(i);
+			if (Character.isDigit(c)) {
+				digitsOnly.append(c);
+			}
+		}
+	
+		int sum = 0;
+		int digit = 0;
+		int addend = 0;
+		boolean timesTwo = false;
+	
+		for (int i = digitsOnly.length() - 1; i >= 0; i--) {
+			// guaranteed to be an int
+			digit = Integer.valueOf(digitsOnly.substring(i, i + 1));
+			if (timesTwo) {
+				addend = digit * 2;
+				if (addend > 9) {
+					addend -= 9;
+				}
+			} else {
+				addend = digit;
+			}
+			sum += addend;
+			timesTwo = !timesTwo;
+		}
+
+		return sum % 10 == 0; 
+	
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public String sanitize( String context, String input ) {
+		return whitelist( input, EncoderConstants.CHAR_DIGITS );
+	}
+
+	/**
+	 * @param ccrule the ccrule to set
+	 */
+	public void setStringValidatorRule(StringValidationRule ccrule) {
+		this.ccrule = ccrule;
+	}
+
+	/**
+	 * @return the ccrule
+	 */
+	public StringValidationRule getStringValidatorRule() {
+		return ccrule;
+	}
+
+	/**
+	 * @param maxCardLength the maxCardLength to set
+	 */
+	public void setMaxCardLength(int maxCardLength) {
+		this.maxCardLength = maxCardLength;
+	}
+
+	/**
+	 * @return the maxCardLength
+	 */
+	public int getMaxCardLength() {
+		return maxCardLength;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/DateValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/DateValidationRule.java.svn-base
new file mode 100644
index 0000000..7e66b20
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/DateValidationRule.java.svn-base
@@ -0,0 +1,106 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class DateValidationRule extends BaseValidationRule {
+	private DateFormat format = DateFormat.getDateInstance();
+	
+	public DateValidationRule( String typeName, Encoder encoder, DateFormat newFormat ) {
+		super( typeName, encoder );      
+		setDateFormat( newFormat );
+	}
+	
+    public final void setDateFormat( DateFormat newFormat ) {
+        if (newFormat == null) {
+			throw new IllegalArgumentException("DateValidationRule.setDateFormat requires a non-null DateFormat");
+		}
+    	// CHECKME fail fast?
+/*		
+  		try {
+			newFormat.parse(new Date());
+		} catch (ParseException e) {
+			throw new IllegalArgumentException(e);
+		}
+*/
+        this.format = newFormat;
+        this.format.setLenient( ESAPI.securityConfiguration().getLenientDatesAccepted() );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+	public Date getValid( String context, String input ) throws ValidationException {
+		return safelyParse(context, input);
+	}
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Calls sanitize(String, String, DateFormat) with DateFormat.getInstance()
+     */
+	@Override
+	public Date sanitize( String context, String input )  {
+		Date date = new Date(0);
+		try {
+			date = safelyParse(context, input);
+		} catch (ValidationException e) {
+			// do nothing
+	    }
+		return date;
+	}
+	    
+	private Date safelyParse(String context, String input)
+			throws ValidationException {
+		// CHECKME should this allow empty Strings? "   " use IsBlank instead?
+		if (StringUtilities.isEmpty(input)) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException(context + ": Input date required",
+					"Input date required: context=" + context + ", input="
+							+ input, context);
+		}
+
+	    String canonical = encoder.canonicalize(input);
+
+		try {
+			return format.parse(canonical);
+		} catch (Exception e) {
+			throw new ValidationException(context
+					+ ": Invalid date must follow the "
+					+ format.getNumberFormat() + " format",
+					"Invalid date: context=" + context + ", format=" + format
+							+ ", input=" + input, e, context);
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/HTMLValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/HTMLValidationRule.java.svn-base
new file mode 100644
index 0000000..e08e2b0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/HTMLValidationRule.java.svn-base
@@ -0,0 +1,129 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.owasp.esapi.errors.ConfigurationException;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.validator.html.AntiSamy;
+import org.owasp.validator.html.CleanResults;
+import org.owasp.validator.html.Policy;
+import org.owasp.validator.html.PolicyException;
+import org.owasp.validator.html.ScanException;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class HTMLValidationRule extends StringValidationRule {
+	
+	/** OWASP AntiSamy markup verification policy */
+	private static Policy antiSamyPolicy = null;
+	private static final Logger LOGGER = ESAPI.getLogger( "HTMLValidationRule" ); 
+	
+	static {
+        InputStream resourceStream = null;
+		try {
+			resourceStream = ESAPI.securityConfiguration().getResourceStream("antisamy-esapi.xml");
+		} catch (IOException e) {
+			throw new ConfigurationException("Couldn't find antisamy-esapi.xml", e);
+	            }
+        if (resourceStream != null) {
+        	try {
+				antiSamyPolicy = Policy.getInstance(resourceStream);
+			} catch (PolicyException e) {
+				throw new ConfigurationException("Couldn't parse antisamy policy", e);
+		        }
+			}
+		}
+
+	public HTMLValidationRule( String typeName ) {
+		super( typeName );
+	}
+	
+	public HTMLValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public HTMLValidationRule( String typeName, Encoder encoder, String whitelistPattern ) {
+		super( typeName, encoder, whitelistPattern );
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public String getValid( String context, String input ) throws ValidationException {
+		return invokeAntiSamy( context, input );
+	}
+		
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public String sanitize( String context, String input ) {
+		String safe = "";
+		try {
+			safe = invokeAntiSamy( context, input );
+		} catch( ValidationException e ) {
+			// just return safe
+		}
+		return safe;
+	}
+
+	private String invokeAntiSamy( String context, String input ) throws ValidationException {
+		// CHECKME should this allow empty Strings? "   " us IsBlank instead?
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + " is required", "AntiSamy validation error: context=" + context + ", input=" + input, context );
+	    }
+	    
+		String canonical = super.getValid( context, input );
+
+		try {
+			AntiSamy as = new AntiSamy();
+			CleanResults test = as.scan(canonical, antiSamyPolicy);
+			
+			List<String> errors = test.getErrorMessages();
+			if ( !errors.isEmpty() ) {
+				LOGGER.info( Logger.SECURITY_FAILURE, "Cleaned up invalid HTML input: " + errors );
+			}
+			
+			return test.getCleanHTML().trim();
+			
+		} catch (ScanException e) {
+			throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input: context=" + context + " error=" + e.getMessage(), e, context );
+		} catch (PolicyException e) {
+			throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " error=" + e.getMessage(), e, context );
+		}
+	}
+}
+
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/IntegerValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/IntegerValidationRule.java.svn-base
new file mode 100644
index 0000000..fe011cf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/IntegerValidationRule.java.svn-base
@@ -0,0 +1,95 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class IntegerValidationRule extends BaseValidationRule {
+	
+	private int minValue = Integer.MIN_VALUE;
+	private int maxValue = Integer.MAX_VALUE;
+	
+	public IntegerValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public IntegerValidationRule( String typeName, Encoder encoder, int minValue, int maxValue ) {
+		super( typeName, encoder );
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+	}
+
+	public Integer getValid( String context, String input ) throws ValidationException {
+		return safelyParse(context, input);
+	}
+
+	private Integer safelyParse(String context, String input) throws ValidationException {
+		// do not allow empty Strings such as "   " - so trim to ensure 
+		// isEmpty catches "    "
+		if (input != null) input = input.trim();
+		
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + ": Input number required", "Input number required: context=" + context + ", input=" + input, context );
+	    }
+	    
+	    // canonicalize
+	    String canonical = encoder.canonicalize( input );
+
+		if (minValue > maxValue) {
+			throw new ValidationException( context + ": Invalid number input: context", "Validation parameter error for number: maxValue ( " + maxValue + ") must be greater than minValue ( " + minValue + ") for " + context, context );
+		}
+		
+		// validate min and max
+		try {
+			int i = Integer.valueOf(canonical);
+			if (i < minValue) {
+				throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+			}
+			if (i > maxValue) {
+				throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+			}			
+			return i;
+		} catch (NumberFormatException e) {
+			throw new ValidationException( context + ": Invalid number input", "Invalid number input format: context=" + context + ", input=" + input, e, context);
+		}
+	}
+
+	@Override
+	public Integer sanitize( String context, String input ) {
+		Integer toReturn = Integer.valueOf( 0 );
+		try {
+			toReturn = safelyParse(context, input);
+		} catch (ValidationException e ) {
+			// do nothing
+		}
+		return toReturn;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/NumberValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/NumberValidationRule.java.svn-base
new file mode 100644
index 0000000..d6cd8ff
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/NumberValidationRule.java.svn-base
@@ -0,0 +1,146 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.math.BigDecimal;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class NumberValidationRule extends BaseValidationRule {
+	
+	private double minValue = Double.NEGATIVE_INFINITY;
+	private double maxValue = Double.POSITIVE_INFINITY;
+	
+	public NumberValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public NumberValidationRule( String typeName, Encoder encoder, double minValue, double maxValue ) {
+		super( typeName, encoder );
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+	public Double getValid( String context, String input ) throws ValidationException {
+		return safelyParse(context, input);
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public Double sanitize( String context, String input ) {
+		Double toReturn = Double.valueOf(0);
+		try {
+			toReturn = safelyParse(context, input);
+		} catch (ValidationException e) {
+			// do nothing
+		}
+		return toReturn;
+	}
+	//
+	// These statics needed to detect double parsing DOS bug in Java
+	//
+	private static BigDecimal bigBad;
+	private static BigDecimal smallBad;
+
+	static {
+		
+		BigDecimal one = new BigDecimal(1);
+		BigDecimal two = new BigDecimal(2);
+		
+		BigDecimal tiny = one.divide(two.pow(1022));
+		
+		// 2^(-1022) ­ 2^(-1076)
+		bigBad = tiny.subtract(one.divide(two.pow(1076)));
+		//2^(-1022) ­ 2^(-1075)
+		smallBad = tiny.subtract(one.divide(two.pow(1075)));
+	}	
+
+	private Double safelyParse(String context, String input) throws ValidationException {
+
+		// CHECKME should this allow empty Strings? "   " us IsBlank instead?
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + ": Input number required", "Input number required: context=" + context + ", input=" + input, context );
+	    }
+	    
+	    // canonicalize
+	    String canonical = encoder.canonicalize( input );
+
+	    //if MinValue is greater than maxValue then programmer is likely calling this wrong
+		if (minValue > maxValue) {
+			throw new ValidationException( context + ": Invalid number input: context", "Validation parameter error for number: maxValue ( " + maxValue + ") must be greater than minValue ( " + minValue + ") for " + context, context );
+		}
+		
+		//convert to BigDecimal so we can safely parse dangerous numbers to 
+		//check if the number may DOS the double parser
+		BigDecimal bd;
+		try {
+			bd = new BigDecimal(canonical);
+		} catch (NumberFormatException e) {
+			throw new ValidationException( context + ": Invalid number input", "Invalid number input format: context=" + context + ", input=" + input, e, context);
+		}
+		
+		// Thanks to Brian Chess for this suggestion
+		// Check if string input is in the "dangerous" double parsing range
+		if (bd.compareTo(smallBad) >= 0 && bd.compareTo(bigBad) <= 0) {
+			// if you get here you know you're looking at a bad value. The final
+			// value for any double in this range is supposed to be the following safe #			
+			return new Double("2.2250738585072014E-308");
+		}
+		
+		// the number is safe to parseDouble
+		Double d;
+		// validate min and max
+		try {
+			d = Double.valueOf(Double.parseDouble( canonical ));
+		} catch (NumberFormatException e) {
+			throw new ValidationException( context + ": Invalid number input", "Invalid number input format: context=" + context + ", input=" + input, e, context);
+		}
+	
+		if (d.isInfinite()) {
+			throw new ValidationException( "Invalid number input: context=" + context, "Invalid double input is infinite: context=" + context + ", input=" + input, context );
+	}
+		if (d.isNaN()) {
+			throw new ValidationException( "Invalid number input: context=" + context, "Invalid double input is not a number: context=" + context + ", input=" + input, context );
+		}
+		if (d.doubleValue() < minValue) {
+			throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+		}
+		if (d.doubleValue() > maxValue) {
+			throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+		}			
+		return d;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/StringValidationRule.java.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/StringValidationRule.java.svn-base
new file mode 100644
index 0000000..9024ac5
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/StringValidationRule.java.svn-base
@@ -0,0 +1,324 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.util.NullSafe;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ * 
+ * http://en.wikipedia.org/wiki/Whitelist
+ */
+public class StringValidationRule extends BaseValidationRule {
+
+	protected List<Pattern> whitelistPatterns = new ArrayList<Pattern>();
+	protected List<Pattern> blacklistPatterns = new ArrayList<Pattern>();
+	protected int minLength = 0;
+	protected int maxLength = Integer.MAX_VALUE;
+	protected boolean validateInputAndCanonical = true;
+
+	public StringValidationRule( String typeName ) {
+		super( typeName );
+	}
+
+	public StringValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public StringValidationRule( String typeName, Encoder encoder, String whitelistPattern ) {
+		super( typeName, encoder );
+		addWhitelistPattern( whitelistPattern );
+	}
+
+	/**
+	 * @throws IllegalArgumentException if pattern is null
+	 */
+	public void addWhitelistPattern( String pattern ) {
+		if (pattern == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		try {
+			whitelistPatterns.add( Pattern.compile( pattern ) );
+		} catch( PatternSyntaxException e ) {
+			throw new IllegalArgumentException( "Validation misconfiguration, problem with specified pattern: " + pattern, e );
+		}
+	}
+
+	/**
+	 * @throws IllegalArgumentException if p is null
+	 */
+	public void addWhitelistPattern( Pattern p ) {
+		if (p == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		whitelistPatterns.add( p );
+	}
+
+	/**
+	 * @throws IllegalArgumentException if pattern is null
+	 */
+	public void addBlacklistPattern( String pattern ) {
+		if (pattern == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		try {
+			blacklistPatterns.add( Pattern.compile( pattern ) );
+		} catch( PatternSyntaxException e ) {
+			throw new IllegalArgumentException( "Validation misconfiguration, problem with specified pattern: " + pattern, e );
+		}
+	}
+
+	/**
+	 * @throws IllegalArgumentException if p is null
+	 */
+	public void addBlacklistPattern( Pattern p ) {
+		if (p == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		blacklistPatterns.add( p );
+	}
+
+	public void setMinimumLength( int length ) {
+		minLength = length;
+	}
+
+
+	public void setMaximumLength( int length ) {
+		maxLength = length;
+	}
+
+	/**
+	 * Set the flag which determines whether the in input itself is
+	 * checked as well as the canonical form of the input.
+	 * @param flag The value to set
+	 */
+	public void setValidateInputAndCanonical(boolean flag)
+	{
+		validateInputAndCanonical = flag;
+	}
+
+	/**
+	 * checks input against whitelists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkWhitelist(String context, String input, String orig) throws ValidationException
+	{
+		// check whitelist patterns
+		for (Pattern p : whitelistPatterns) {
+			if ( !p.matcher(input).matches() ) {
+				throw new ValidationException( context + ": Invalid input. Please conform to regex " + p.pattern() + ( maxLength == Integer.MAX_VALUE ? "" : " with a maximum length of " + maxLength ), "Invalid input: context=" + context + ", type(" + getTypeName() + ")=" + p.pattern() + ", input=" + input + (NullSafe.equals(orig,input) ? "" : ", orig=" + orig), context );
+			}
+		}
+
+		return input;
+	}
+
+	/**
+	 * checks input against whitelists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkWhitelist(String context, String input) throws ValidationException
+	{
+		return checkWhitelist(context, input, input);
+	}
+
+	/**
+	 * checks input against blacklists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkBlacklist(String context, String input, String orig) throws ValidationException
+	{
+		// check blacklist patterns
+		for (Pattern p : blacklistPatterns) {
+			if ( p.matcher(input).matches() ) {
+				throw new ValidationException( context + ": Invalid input. Dangerous input matching " + p.pattern() + " detected.", "Dangerous input: context=" + context + ", type(" + getTypeName() + ")=" + p.pattern() + ", input=" + input + (NullSafe.equals(orig,input) ? "" : ", orig=" + orig), context );
+			}
+		}
+
+		return input;
+	}
+
+	/**
+	 * checks input against blacklists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkBlacklist(String context, String input) throws ValidationException
+	{
+		return checkBlacklist(context, input, input);
+	}
+
+	/**
+	 * checks input lengths
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkLength(String context, String input, String orig) throws ValidationException
+	{
+		if (input.length() < minLength) {
+			throw new ValidationException( context + ": Invalid input. The minimum length of " + minLength + " characters was not met.", "Input does not meet the minimum length of " + minLength + " by " + (minLength - input.length()) + " characters: context=" + context + ", type=" + getTypeName() + "), input=" + input + (NullSafe.equals(input,orig) ? "" : ", orig=" + orig), context );
+		}
+
+		if (input.length() > maxLength) {
+			throw new ValidationException( context + ": Invalid input. The maximum length of " + maxLength + " characters was exceeded.", "Input exceeds maximum allowed length of " + maxLength + " by " + (input.length()-maxLength) + " characters: context=" + context + ", type=" + getTypeName() + ", orig=" + orig +", input=" + input, context );
+		}
+
+		return input;
+	}
+
+	/**
+	 * checks input lengths
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkLength(String context, String input) throws ValidationException
+	{
+		return checkLength(context, input, input);
+	}
+
+	/**
+	 * checks input emptiness
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkEmpty(String context, String input, String orig) throws ValidationException
+	{
+		if(!StringUtilities.isEmpty(input))
+			return input;
+		if(allowNull)
+			return null;
+		throw new ValidationException( context + ": Input required.", "Input required: context=" + context + "), input=" + input + (NullSafe.equals(input,orig) ? "" : ", orig=" + orig), context );
+	}
+
+	/**
+	 * checks input emptiness
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkEmpty(String context, String input) throws ValidationException
+	{
+		return checkEmpty(context, input, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValid( String context, String input ) throws ValidationException
+	{
+		String data = null;
+
+		// checks on input itself
+
+		// check for empty/null
+		if(checkEmpty(context, input) == null)
+			return null;
+
+		if (validateInputAndCanonical)
+		{
+			//first validate pre-canonicalized data
+			
+			// check length
+			checkLength(context, input);
+
+			// check whitelist patterns
+			checkWhitelist(context, input);
+
+			// check blacklist patterns
+			checkBlacklist(context, input);
+			
+			// canonicalize
+			data = encoder.canonicalize( input );
+			
+		} else {
+			
+			//skip canonicalization
+			data = input;			
+		}
+
+		// check for empty/null
+		if(checkEmpty(context, data, input) == null)
+			return null;
+
+		// check length
+		checkLength(context, data, input);
+
+		// check whitelist patterns
+		checkWhitelist(context, data, input);
+
+		// check blacklist patterns
+		checkBlacklist(context, data, input);
+
+		// validation passed
+		return data;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+		public String sanitize( String context, String input ) {
+			return whitelist( input, EncoderConstants.CHAR_ALPHANUMERICS );
+		}
+
+}
+
diff --git a/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..050397a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/.svn/text-base/package.html.svn-base
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains data format-specific validation rule functions.
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java
new file mode 100644
index 0000000..1929ff0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java
@@ -0,0 +1,198 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.ValidationRule;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * A ValidationRule performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public abstract class BaseValidationRule implements ValidationRule {
+
+	private String typeName = null;
+	protected boolean allowNull = false;
+	protected Encoder encoder = null;
+	
+	private BaseValidationRule() {
+		// prevent use of no-arg constructor
+	}
+	
+	public BaseValidationRule( String typeName ) {
+		this();
+		setEncoder( ESAPI.encoder() );
+		setTypeName( typeName );
+	}
+	
+	public BaseValidationRule( String typeName, Encoder encoder ) {
+		this();
+		setEncoder( encoder );
+		setTypeName( typeName );
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public void setAllowNull( boolean flag ) {
+		allowNull = flag;
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public String getTypeName() {
+		return typeName;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public final void setTypeName( String typeName ) {
+		this.typeName = typeName;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public final void setEncoder( Encoder encoder ) {
+		this.encoder = encoder;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public void assertValid( String context, String input ) throws ValidationException {
+		getValid( context, input, null );
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public Object getValid( String context, String input, ValidationErrorList errorList ) throws ValidationException {
+		Object valid = null;
+		try {
+			valid = getValid( context, input );
+		} catch (ValidationException e) {
+			errorList.addError(context, e);
+		}
+		return valid;
+	}
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public Object getSafe( String context, String input ) {
+		Object valid = null;
+		try {
+			valid = getValid( context, input );
+		} catch ( ValidationException e ) {
+			return sanitize( context, input );
+		}
+		return valid;
+	}
+
+	/**
+	 * The method is similar to ValidationRuile.getSafe except that it returns a
+	 * harmless object that <b>may or may not have any similarity to the original
+	 * input (in some cases you may not care)</b>. In most cases this should be the
+	 * same as the getSafe method only instead of throwing an exception, return
+	 * some default value.
+	 * 
+	 * @param context
+	 * @param input
+	 * @return a parsed version of the input or a default value.
+	 */
+	protected abstract Object sanitize( String context, String input );
+	
+    /**
+     * {@inheritDoc}
+	 */
+	public boolean isValid( String context, String input ) {
+		boolean valid = false;
+		try {
+			getValid( context, input );
+			valid = true;
+		} catch( Exception e ) {
+			valid = false;
+		}
+		
+		return valid;
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public String whitelist( String input, char[] whitelist) {
+		return whitelist(input, charArrayToSet(whitelist));
+	}
+	
+	/**
+	 * Removes characters that aren't in the whitelist from the input String.  
+	 * O(input.length) whitelist performance
+	 * @param input String to be sanitized
+	 * @param whitelist allowed characters
+	 * @return input stripped of all chars that aren't in the whitelist 
+	 */
+	public String whitelist( String input, Set<Character> whitelist) {
+		StringBuilder stripped = new StringBuilder();
+		for (int i = 0; i < input.length(); i++) {
+			char c = input.charAt(i);
+			if (whitelist.contains(c)) {
+				stripped.append(c);
+			}
+		}
+		return stripped.toString();
+	}
+	
+	// CHECKME should be moved to some utility class (Would potentially be used by new EncoderConstants class)
+	// Is there a standard way to convert an array of primitives to a Collection
+	/**
+	 * Convert an array of characters to a {@code Set<Character>} (so duplicates
+	 * are removed).
+	 * @param array The character array.
+	 * @return A {@code Set<Character>} of the unique characters from {@code array}
+	 *         is returned.
+	 */
+	public static Set<Character> charArrayToSet(char[] array) {
+		Set<Character> toReturn = new HashSet<Character>(array.length);
+		for (char c : array) {
+			toReturn.add(c);
+		}
+		return toReturn;
+	}
+	
+	public boolean isAllowNull() {
+		return allowNull;
+	}
+
+	public Encoder getEncoder() {
+		return encoder;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/CreditCardValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/CreditCardValidationRule.java
new file mode 100644
index 0000000..86f7d4c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/CreditCardValidationRule.java
@@ -0,0 +1,165 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.util.regex.Pattern;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * A validator performs syntax and possibly semantic validation of Credit Card
+ * String from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class CreditCardValidationRule extends BaseValidationRule {
+	private int maxCardLength = 19;
+	
+	/**
+	 * Key used to pull out encoder in configuration.  Prefixed with "Validator."
+	 */
+	protected static final String CREDIT_CARD_VALIDATOR_KEY = "CreditCard";
+	
+	private StringValidationRule ccrule = null; 
+	
+	/**
+	 * Creates a CreditCardValidator using the rule found in security configuration
+	 * @param typeName a description of the type of card being validated
+	 * @param encoder
+	 */
+	public CreditCardValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+		ccrule = readDefaultCreditCardRule();
+	}
+	
+	public CreditCardValidationRule( String typeName, Encoder encoder, StringValidationRule validationRule ) {
+		super( typeName, encoder );
+		ccrule = validationRule;
+	}
+
+	private StringValidationRule readDefaultCreditCardRule() {
+		Pattern p = ESAPI.securityConfiguration().getValidationPattern( CREDIT_CARD_VALIDATOR_KEY );
+		StringValidationRule ccr = new StringValidationRule( "ccrule", encoder, p.pattern() );
+		ccr.setMaximumLength(getMaxCardLength());
+		ccr.setAllowNull( false );
+		return ccr;
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	public String getValid( String context, String input ) throws ValidationException {
+		// CHECKME should this allow empty Strings? "   " us IsBlank instead?
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + ": Input credit card required", "Input credit card required: context=" + context + ", input=" + input, context );
+	    }
+	    
+	    String canonical = ccrule.getValid( context, input );
+
+		if( ! validCreditCardFormat(canonical)) {
+			throw new ValidationException( context + ": Invalid credit card input", "Invalid credit card input: context=" + context, context );
+		}
+		
+		return canonical;	    
+	}
+
+	/**
+	 * Performs additional validation on the card nummber.
+	 * This implementation performs Luhn algorithm checking
+	 * @param ccNum number to be validated
+	 * @return true if the ccNum passes the Luhn Algorithm
+	 */
+	protected boolean validCreditCardFormat(String ccNum) {
+		
+	    StringBuilder digitsOnly = new StringBuilder();
+		char c;
+		for (int i = 0; i < ccNum.length(); i++) {
+			c = ccNum.charAt(i);
+			if (Character.isDigit(c)) {
+				digitsOnly.append(c);
+			}
+		}
+	
+		int sum = 0;
+		int digit = 0;
+		int addend = 0;
+		boolean timesTwo = false;
+	
+		for (int i = digitsOnly.length() - 1; i >= 0; i--) {
+			// guaranteed to be an int
+			digit = Integer.valueOf(digitsOnly.substring(i, i + 1));
+			if (timesTwo) {
+				addend = digit * 2;
+				if (addend > 9) {
+					addend -= 9;
+				}
+			} else {
+				addend = digit;
+			}
+			sum += addend;
+			timesTwo = !timesTwo;
+		}
+
+		return sum % 10 == 0; 
+	
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public String sanitize( String context, String input ) {
+		return whitelist( input, EncoderConstants.CHAR_DIGITS );
+	}
+
+	/**
+	 * @param ccrule the ccrule to set
+	 */
+	public void setStringValidatorRule(StringValidationRule ccrule) {
+		this.ccrule = ccrule;
+	}
+
+	/**
+	 * @return the ccrule
+	 */
+	public StringValidationRule getStringValidatorRule() {
+		return ccrule;
+	}
+
+	/**
+	 * @param maxCardLength the maxCardLength to set
+	 */
+	public void setMaxCardLength(int maxCardLength) {
+		this.maxCardLength = maxCardLength;
+	}
+
+	/**
+	 * @return the maxCardLength
+	 */
+	public int getMaxCardLength() {
+		return maxCardLength;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/DateValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/DateValidationRule.java
new file mode 100644
index 0000000..7e66b20
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/DateValidationRule.java
@@ -0,0 +1,106 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class DateValidationRule extends BaseValidationRule {
+	private DateFormat format = DateFormat.getDateInstance();
+	
+	public DateValidationRule( String typeName, Encoder encoder, DateFormat newFormat ) {
+		super( typeName, encoder );      
+		setDateFormat( newFormat );
+	}
+	
+    public final void setDateFormat( DateFormat newFormat ) {
+        if (newFormat == null) {
+			throw new IllegalArgumentException("DateValidationRule.setDateFormat requires a non-null DateFormat");
+		}
+    	// CHECKME fail fast?
+/*		
+  		try {
+			newFormat.parse(new Date());
+		} catch (ParseException e) {
+			throw new IllegalArgumentException(e);
+		}
+*/
+        this.format = newFormat;
+        this.format.setLenient( ESAPI.securityConfiguration().getLenientDatesAccepted() );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+	public Date getValid( String context, String input ) throws ValidationException {
+		return safelyParse(context, input);
+	}
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Calls sanitize(String, String, DateFormat) with DateFormat.getInstance()
+     */
+	@Override
+	public Date sanitize( String context, String input )  {
+		Date date = new Date(0);
+		try {
+			date = safelyParse(context, input);
+		} catch (ValidationException e) {
+			// do nothing
+	    }
+		return date;
+	}
+	    
+	private Date safelyParse(String context, String input)
+			throws ValidationException {
+		// CHECKME should this allow empty Strings? "   " use IsBlank instead?
+		if (StringUtilities.isEmpty(input)) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException(context + ": Input date required",
+					"Input date required: context=" + context + ", input="
+							+ input, context);
+		}
+
+	    String canonical = encoder.canonicalize(input);
+
+		try {
+			return format.parse(canonical);
+		} catch (Exception e) {
+			throw new ValidationException(context
+					+ ": Invalid date must follow the "
+					+ format.getNumberFormat() + " format",
+					"Invalid date: context=" + context + ", format=" + format
+							+ ", input=" + input, e, context);
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java
new file mode 100644
index 0000000..e08e2b0
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java
@@ -0,0 +1,129 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.owasp.esapi.errors.ConfigurationException;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.validator.html.AntiSamy;
+import org.owasp.validator.html.CleanResults;
+import org.owasp.validator.html.Policy;
+import org.owasp.validator.html.PolicyException;
+import org.owasp.validator.html.ScanException;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class HTMLValidationRule extends StringValidationRule {
+	
+	/** OWASP AntiSamy markup verification policy */
+	private static Policy antiSamyPolicy = null;
+	private static final Logger LOGGER = ESAPI.getLogger( "HTMLValidationRule" ); 
+	
+	static {
+        InputStream resourceStream = null;
+		try {
+			resourceStream = ESAPI.securityConfiguration().getResourceStream("antisamy-esapi.xml");
+		} catch (IOException e) {
+			throw new ConfigurationException("Couldn't find antisamy-esapi.xml", e);
+	            }
+        if (resourceStream != null) {
+        	try {
+				antiSamyPolicy = Policy.getInstance(resourceStream);
+			} catch (PolicyException e) {
+				throw new ConfigurationException("Couldn't parse antisamy policy", e);
+		        }
+			}
+		}
+
+	public HTMLValidationRule( String typeName ) {
+		super( typeName );
+	}
+	
+	public HTMLValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public HTMLValidationRule( String typeName, Encoder encoder, String whitelistPattern ) {
+		super( typeName, encoder, whitelistPattern );
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public String getValid( String context, String input ) throws ValidationException {
+		return invokeAntiSamy( context, input );
+	}
+		
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public String sanitize( String context, String input ) {
+		String safe = "";
+		try {
+			safe = invokeAntiSamy( context, input );
+		} catch( ValidationException e ) {
+			// just return safe
+		}
+		return safe;
+	}
+
+	private String invokeAntiSamy( String context, String input ) throws ValidationException {
+		// CHECKME should this allow empty Strings? "   " us IsBlank instead?
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + " is required", "AntiSamy validation error: context=" + context + ", input=" + input, context );
+	    }
+	    
+		String canonical = super.getValid( context, input );
+
+		try {
+			AntiSamy as = new AntiSamy();
+			CleanResults test = as.scan(canonical, antiSamyPolicy);
+			
+			List<String> errors = test.getErrorMessages();
+			if ( !errors.isEmpty() ) {
+				LOGGER.info( Logger.SECURITY_FAILURE, "Cleaned up invalid HTML input: " + errors );
+			}
+			
+			return test.getCleanHTML().trim();
+			
+		} catch (ScanException e) {
+			throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input: context=" + context + " error=" + e.getMessage(), e, context );
+		} catch (PolicyException e) {
+			throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " error=" + e.getMessage(), e, context );
+		}
+	}
+}
+
diff --git a/src/main/java/org/owasp/esapi/reference/validation/IntegerValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/IntegerValidationRule.java
new file mode 100644
index 0000000..fe011cf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/IntegerValidationRule.java
@@ -0,0 +1,95 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class IntegerValidationRule extends BaseValidationRule {
+	
+	private int minValue = Integer.MIN_VALUE;
+	private int maxValue = Integer.MAX_VALUE;
+	
+	public IntegerValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public IntegerValidationRule( String typeName, Encoder encoder, int minValue, int maxValue ) {
+		super( typeName, encoder );
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+	}
+
+	public Integer getValid( String context, String input ) throws ValidationException {
+		return safelyParse(context, input);
+	}
+
+	private Integer safelyParse(String context, String input) throws ValidationException {
+		// do not allow empty Strings such as "   " - so trim to ensure 
+		// isEmpty catches "    "
+		if (input != null) input = input.trim();
+		
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + ": Input number required", "Input number required: context=" + context + ", input=" + input, context );
+	    }
+	    
+	    // canonicalize
+	    String canonical = encoder.canonicalize( input );
+
+		if (minValue > maxValue) {
+			throw new ValidationException( context + ": Invalid number input: context", "Validation parameter error for number: maxValue ( " + maxValue + ") must be greater than minValue ( " + minValue + ") for " + context, context );
+		}
+		
+		// validate min and max
+		try {
+			int i = Integer.valueOf(canonical);
+			if (i < minValue) {
+				throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+			}
+			if (i > maxValue) {
+				throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+			}			
+			return i;
+		} catch (NumberFormatException e) {
+			throw new ValidationException( context + ": Invalid number input", "Invalid number input format: context=" + context + ", input=" + input, e, context);
+		}
+	}
+
+	@Override
+	public Integer sanitize( String context, String input ) {
+		Integer toReturn = Integer.valueOf( 0 );
+		try {
+			toReturn = safelyParse(context, input);
+		} catch (ValidationException e ) {
+			// do nothing
+		}
+		return toReturn;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/reference/validation/NumberValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/NumberValidationRule.java
new file mode 100644
index 0000000..d6cd8ff
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/NumberValidationRule.java
@@ -0,0 +1,146 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.math.BigDecimal;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ */
+public class NumberValidationRule extends BaseValidationRule {
+	
+	private double minValue = Double.NEGATIVE_INFINITY;
+	private double maxValue = Double.POSITIVE_INFINITY;
+	
+	public NumberValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public NumberValidationRule( String typeName, Encoder encoder, double minValue, double maxValue ) {
+		super( typeName, encoder );
+		this.minValue = minValue;
+		this.maxValue = maxValue;
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+	public Double getValid( String context, String input ) throws ValidationException {
+		return safelyParse(context, input);
+	}
+	
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+	public Double sanitize( String context, String input ) {
+		Double toReturn = Double.valueOf(0);
+		try {
+			toReturn = safelyParse(context, input);
+		} catch (ValidationException e) {
+			// do nothing
+		}
+		return toReturn;
+	}
+	//
+	// These statics needed to detect double parsing DOS bug in Java
+	//
+	private static BigDecimal bigBad;
+	private static BigDecimal smallBad;
+
+	static {
+		
+		BigDecimal one = new BigDecimal(1);
+		BigDecimal two = new BigDecimal(2);
+		
+		BigDecimal tiny = one.divide(two.pow(1022));
+		
+		// 2^(-1022) ­ 2^(-1076)
+		bigBad = tiny.subtract(one.divide(two.pow(1076)));
+		//2^(-1022) ­ 2^(-1075)
+		smallBad = tiny.subtract(one.divide(two.pow(1075)));
+	}	
+
+	private Double safelyParse(String context, String input) throws ValidationException {
+
+		// CHECKME should this allow empty Strings? "   " us IsBlank instead?
+	    if ( StringUtilities.isEmpty(input) ) {
+			if (allowNull) {
+				return null;
+			}
+			throw new ValidationException( context + ": Input number required", "Input number required: context=" + context + ", input=" + input, context );
+	    }
+	    
+	    // canonicalize
+	    String canonical = encoder.canonicalize( input );
+
+	    //if MinValue is greater than maxValue then programmer is likely calling this wrong
+		if (minValue > maxValue) {
+			throw new ValidationException( context + ": Invalid number input: context", "Validation parameter error for number: maxValue ( " + maxValue + ") must be greater than minValue ( " + minValue + ") for " + context, context );
+		}
+		
+		//convert to BigDecimal so we can safely parse dangerous numbers to 
+		//check if the number may DOS the double parser
+		BigDecimal bd;
+		try {
+			bd = new BigDecimal(canonical);
+		} catch (NumberFormatException e) {
+			throw new ValidationException( context + ": Invalid number input", "Invalid number input format: context=" + context + ", input=" + input, e, context);
+		}
+		
+		// Thanks to Brian Chess for this suggestion
+		// Check if string input is in the "dangerous" double parsing range
+		if (bd.compareTo(smallBad) >= 0 && bd.compareTo(bigBad) <= 0) {
+			// if you get here you know you're looking at a bad value. The final
+			// value for any double in this range is supposed to be the following safe #			
+			return new Double("2.2250738585072014E-308");
+		}
+		
+		// the number is safe to parseDouble
+		Double d;
+		// validate min and max
+		try {
+			d = Double.valueOf(Double.parseDouble( canonical ));
+		} catch (NumberFormatException e) {
+			throw new ValidationException( context + ": Invalid number input", "Invalid number input format: context=" + context + ", input=" + input, e, context);
+		}
+	
+		if (d.isInfinite()) {
+			throw new ValidationException( "Invalid number input: context=" + context, "Invalid double input is infinite: context=" + context + ", input=" + input, context );
+	}
+		if (d.isNaN()) {
+			throw new ValidationException( "Invalid number input: context=" + context, "Invalid double input is not a number: context=" + context + ", input=" + input, context );
+		}
+		if (d.doubleValue() < minValue) {
+			throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+		}
+		if (d.doubleValue() > maxValue) {
+			throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+		}			
+		return d;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java
new file mode 100644
index 0000000..9024ac5
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java
@@ -0,0 +1,324 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.validation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.util.NullSafe;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * 
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ * @see org.owasp.esapi.Validator
+ * 
+ * http://en.wikipedia.org/wiki/Whitelist
+ */
+public class StringValidationRule extends BaseValidationRule {
+
+	protected List<Pattern> whitelistPatterns = new ArrayList<Pattern>();
+	protected List<Pattern> blacklistPatterns = new ArrayList<Pattern>();
+	protected int minLength = 0;
+	protected int maxLength = Integer.MAX_VALUE;
+	protected boolean validateInputAndCanonical = true;
+
+	public StringValidationRule( String typeName ) {
+		super( typeName );
+	}
+
+	public StringValidationRule( String typeName, Encoder encoder ) {
+		super( typeName, encoder );
+	}
+
+	public StringValidationRule( String typeName, Encoder encoder, String whitelistPattern ) {
+		super( typeName, encoder );
+		addWhitelistPattern( whitelistPattern );
+	}
+
+	/**
+	 * @throws IllegalArgumentException if pattern is null
+	 */
+	public void addWhitelistPattern( String pattern ) {
+		if (pattern == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		try {
+			whitelistPatterns.add( Pattern.compile( pattern ) );
+		} catch( PatternSyntaxException e ) {
+			throw new IllegalArgumentException( "Validation misconfiguration, problem with specified pattern: " + pattern, e );
+		}
+	}
+
+	/**
+	 * @throws IllegalArgumentException if p is null
+	 */
+	public void addWhitelistPattern( Pattern p ) {
+		if (p == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		whitelistPatterns.add( p );
+	}
+
+	/**
+	 * @throws IllegalArgumentException if pattern is null
+	 */
+	public void addBlacklistPattern( String pattern ) {
+		if (pattern == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		try {
+			blacklistPatterns.add( Pattern.compile( pattern ) );
+		} catch( PatternSyntaxException e ) {
+			throw new IllegalArgumentException( "Validation misconfiguration, problem with specified pattern: " + pattern, e );
+		}
+	}
+
+	/**
+	 * @throws IllegalArgumentException if p is null
+	 */
+	public void addBlacklistPattern( Pattern p ) {
+		if (p == null) {
+			throw new IllegalArgumentException("Pattern cannot be null");
+		}
+		blacklistPatterns.add( p );
+	}
+
+	public void setMinimumLength( int length ) {
+		minLength = length;
+	}
+
+
+	public void setMaximumLength( int length ) {
+		maxLength = length;
+	}
+
+	/**
+	 * Set the flag which determines whether the in input itself is
+	 * checked as well as the canonical form of the input.
+	 * @param flag The value to set
+	 */
+	public void setValidateInputAndCanonical(boolean flag)
+	{
+		validateInputAndCanonical = flag;
+	}
+
+	/**
+	 * checks input against whitelists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkWhitelist(String context, String input, String orig) throws ValidationException
+	{
+		// check whitelist patterns
+		for (Pattern p : whitelistPatterns) {
+			if ( !p.matcher(input).matches() ) {
+				throw new ValidationException( context + ": Invalid input. Please conform to regex " + p.pattern() + ( maxLength == Integer.MAX_VALUE ? "" : " with a maximum length of " + maxLength ), "Invalid input: context=" + context + ", type(" + getTypeName() + ")=" + p.pattern() + ", input=" + input + (NullSafe.equals(orig,input) ? "" : ", orig=" + orig), context );
+			}
+		}
+
+		return input;
+	}
+
+	/**
+	 * checks input against whitelists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkWhitelist(String context, String input) throws ValidationException
+	{
+		return checkWhitelist(context, input, input);
+	}
+
+	/**
+	 * checks input against blacklists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkBlacklist(String context, String input, String orig) throws ValidationException
+	{
+		// check blacklist patterns
+		for (Pattern p : blacklistPatterns) {
+			if ( p.matcher(input).matches() ) {
+				throw new ValidationException( context + ": Invalid input. Dangerous input matching " + p.pattern() + " detected.", "Dangerous input: context=" + context + ", type(" + getTypeName() + ")=" + p.pattern() + ", input=" + input + (NullSafe.equals(orig,input) ? "" : ", orig=" + orig), context );
+			}
+		}
+
+		return input;
+	}
+
+	/**
+	 * checks input against blacklists.
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkBlacklist(String context, String input) throws ValidationException
+	{
+		return checkBlacklist(context, input, input);
+	}
+
+	/**
+	 * checks input lengths
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkLength(String context, String input, String orig) throws ValidationException
+	{
+		if (input.length() < minLength) {
+			throw new ValidationException( context + ": Invalid input. The minimum length of " + minLength + " characters was not met.", "Input does not meet the minimum length of " + minLength + " by " + (minLength - input.length()) + " characters: context=" + context + ", type=" + getTypeName() + "), input=" + input + (NullSafe.equals(input,orig) ? "" : ", orig=" + orig), context );
+		}
+
+		if (input.length() > maxLength) {
+			throw new ValidationException( context + ": Invalid input. The maximum length of " + maxLength + " characters was exceeded.", "Input exceeds maximum allowed length of " + maxLength + " by " + (input.length()-maxLength) + " characters: context=" + context + ", type=" + getTypeName() + ", orig=" + orig +", input=" + input, context );
+		}
+
+		return input;
+	}
+
+	/**
+	 * checks input lengths
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkLength(String context, String input) throws ValidationException
+	{
+		return checkLength(context, input, input);
+	}
+
+	/**
+	 * checks input emptiness
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @param orig A origional input to include in exception
+	 *	messages. This is not included if it is the same as
+	 *	input.
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkEmpty(String context, String input, String orig) throws ValidationException
+	{
+		if(!StringUtilities.isEmpty(input))
+			return input;
+		if(allowNull)
+			return null;
+		throw new ValidationException( context + ": Input required.", "Input required: context=" + context + "), input=" + input + (NullSafe.equals(input,orig) ? "" : ", orig=" + orig), context );
+	}
+
+	/**
+	 * checks input emptiness
+	 * @param context The context to include in exception messages
+	 * @param input the input to check
+	 * @return input upon a successful check
+	 * @throws ValidationException if the check fails.
+	 */
+	private String checkEmpty(String context, String input) throws ValidationException
+	{
+		return checkEmpty(context, input, input);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getValid( String context, String input ) throws ValidationException
+	{
+		String data = null;
+
+		// checks on input itself
+
+		// check for empty/null
+		if(checkEmpty(context, input) == null)
+			return null;
+
+		if (validateInputAndCanonical)
+		{
+			//first validate pre-canonicalized data
+			
+			// check length
+			checkLength(context, input);
+
+			// check whitelist patterns
+			checkWhitelist(context, input);
+
+			// check blacklist patterns
+			checkBlacklist(context, input);
+			
+			// canonicalize
+			data = encoder.canonicalize( input );
+			
+		} else {
+			
+			//skip canonicalization
+			data = input;			
+		}
+
+		// check for empty/null
+		if(checkEmpty(context, data, input) == null)
+			return null;
+
+		// check length
+		checkLength(context, data, input);
+
+		// check whitelist patterns
+		checkWhitelist(context, data, input);
+
+		// check blacklist patterns
+		checkBlacklist(context, data, input);
+
+		// validation passed
+		return data;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+		public String sanitize( String context, String input ) {
+			return whitelist( input, EncoderConstants.CHAR_ALPHANUMERICS );
+		}
+
+}
+
diff --git a/src/main/java/org/owasp/esapi/reference/validation/package.html b/src/main/java/org/owasp/esapi/reference/validation/package.html
new file mode 100644
index 0000000..050397a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/reference/validation/package.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains data format-specific validation rule functions.
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/all-wcprops b/src/main/java/org/owasp/esapi/tags/.svn/all-wcprops
new file mode 100644
index 0000000..a44a764
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/all-wcprops
@@ -0,0 +1,83 @@
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags
+END
+EncodeForVBScriptTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForVBScriptTag.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/package.html
+END
+EncodeForXMLTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForXMLTag.java
+END
+EncodeForURLTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForURLTag.java
+END
+BaseEncodeTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/BaseEncodeTag.java
+END
+EncodeForHTMLTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForHTMLTag.java
+END
+EncodeForXMLAttributeTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForXMLAttributeTag.java
+END
+EncodeForXPathTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForXPathTag.java
+END
+EncodeForBase64Tag.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForBase64Tag.java
+END
+EncodeForJavaScriptTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForJavaScriptTag.java
+END
+ELEncodeFunctions.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/ELEncodeFunctions.java
+END
+EncodeForHTMLAttributeTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForHTMLAttributeTag.java
+END
+EncodeForCSSTag.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags/EncodeForCSSTag.java
+END
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/entries b/src/main/java/org/owasp/esapi/tags/.svn/entries
new file mode 100644
index 0000000..ff15056
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/entries
@@ -0,0 +1,470 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/tags
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-11-04T20:06:56.849239Z
+749
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+EncodeForVBScriptTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+917b66584d5ce3bdac1f19a941d02328
+2009-11-04T19:32:57.034029Z
+745
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1220
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+e171b00f6136641cd9c9a5d6a9a677d3
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+256
+

+EncodeForXMLTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+09c0381b1914372a54b5ff292bfe7f3f
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+550
+

+EncodeForURLTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+aadeeb7982986ca4c61ab306b5c1f56f
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+868
+

+BaseEncodeTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+9797d0a83f924d22d009eb761a06f4b0
+2009-11-04T19:32:57.034029Z
+745
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2082
+

+EncodeForHTMLTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+911ac1b01f22fab07d360cfb8363b05b
+2009-11-04T19:32:57.034029Z
+745
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1196
+

+EncodeForXMLAttributeTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+f95aecc0ce642a4619572037510a9db0
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+613
+

+EncodeForXPathTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+aad3b55448d67a8db951e4874d4eefe0
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+562
+

+EncodeForBase64Tag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+0b72254ac827d8c3fc68dc3e264ee655
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1922
+

+EncodeForJavaScriptTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+631435edc3daba5f289b7c0b78db6ce4
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+614
+

+ELEncodeFunctions.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+c6f5ac455a57b345a2650d716e9037fa
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5323
+

+EncodeForHTMLAttributeTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+ee330302b69d84d0703287d10d960144
+2009-11-04T19:32:57.034029Z
+745
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1259
+

+EncodeForCSSTag.java
+file
+
+
+
+
+2014-02-18T16:19:52.553958Z
+b93f31f337cfe55615e4513dc19ad90f
+2009-11-04T19:36:54.189792Z
+746
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+550
+

diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/BaseEncodeTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/BaseEncodeTag.java.svn-base
new file mode 100644
index 0000000..c40a04f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/BaseEncodeTag.java.svn-base
@@ -0,0 +1,70 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspTagException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+
+/** Abstract base class for tags that just encode their bodies with Encoder methods. */
+public abstract class BaseEncodeTag extends BodyTagSupport
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Encode tag's content.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder provided as a convinence.
+	 * @return content encoded by the subclass's implementation.
+	 */
+	protected abstract String encode(String content, Encoder enc) throws JspTagException;
+
+	/**
+	 * After tag body parsing handler. This provides the necessary
+	 * plubming to allow subclasses to just concern themselves with
+	 * encoding a single string.
+	 * @return {@link javax.servlet.jsp.tagext.Tag#SKIP_BODY}
+	 * @throws JspTagException if writing to the bodyContent's
+	 * enclosing writer throws an IOException.
+	 */
+	public int doAfterBody() throws JspTagException
+	{
+		String content;
+		JspWriter out;
+
+		content = bodyContent.getString();
+		out = bodyContent.getEnclosingWriter();
+
+		content = encode(content, ESAPI.encoder());
+		try
+		{
+			out.print(content);
+		}
+		catch (IOException e)
+		{
+			throw new JspTagException("Error writing to body's enclosing JspWriter",e);
+		}
+
+		bodyContent.clearBody();
+		return SKIP_BODY;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/ELEncodeFunctions.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/ELEncodeFunctions.java.svn-base
new file mode 100644
index 0000000..5b74e1e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/ELEncodeFunctions.java.svn-base
@@ -0,0 +1,172 @@
+package org.owasp.esapi.tags;
+
+import java.io.UnsupportedEncodingException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.errors.EncodingException;
+
+/**
+ * Static encoder methods for JSP EL expression functions.
+ */
+public class ELEncodeFunctions
+{
+	private static final String DEFAULT_ENCODING = "UTF-8";
+
+	/**
+	 * Private constructor as this class shouldn't need to be
+	 * instantiated.
+	 */
+	private ELEncodeFunctions()
+	{
+	}
+
+	/**
+	 * Base64 encode a string. UTF-8 is used to encode the string and no line wrapping is performed.
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if UTF-8 is an unsupported character set. This should not happen as UTF-8 is required to be supported by the JVM spec.
+	 */
+	public static String encodeForBase64(String str) throws UnsupportedEncodingException
+	{
+		return encodeForBase64Charset(DEFAULT_ENCODING, str);
+	}
+
+	/**
+	 * Base64 encode a string with line wrapping. UTF-8 is used to encode the string and lines are wrapped at 64 characters..
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if UTF-8 is an unsupported character set. This should not happen as UTF-8 is required to be supported by the JVM spec.
+	 */
+	public static String encodeForBase64Wrap(String str) throws UnsupportedEncodingException
+	{
+		return encodeForBase64CharsetWrap(DEFAULT_ENCODING, str);
+	}
+
+	/**
+	 * Base64 encode a string after converting to bytes using the specified character set. No line wrapping is performed.
+	 * @param charset The character set used to convert str to bytes.
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if charset is an unsupported character set.
+	 */
+	public static String encodeForBase64Charset(String charset, String str) throws UnsupportedEncodingException
+	{
+		return ESAPI.encoder().encodeForBase64(str.getBytes(charset), false);
+	}
+
+	/**
+	 * Base64 encode a string after converting to bytes using the specified character set and wrapping lines. Lines are wrapped at 64 characters.
+	 * @param charset The character set used to convert str to bytes.
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if charset is an unsupported character set.
+	 */
+	public static String encodeForBase64CharsetWrap(String charset, String str) throws UnsupportedEncodingException
+	{
+		return ESAPI.encoder().encodeForBase64(str.getBytes(charset), true);
+	}
+
+	/**
+	 * Encode string for use in CSS.
+	 * @param str The string to encode.
+	 * @return str encoded for use in CSS.
+	 * @see Encoder#encodeForCSS(String)
+	 */
+	public static String encodeForCSS(String str)
+	{
+		return ESAPI.encoder().encodeForCSS(str);
+	}
+
+	/**
+	 * Encode string for use in HTML.
+	 * @param str The string to encode.
+	 * @return str encoded for use in HTML.
+	 * @see Encoder#encodeForHTML(String)
+	 */
+	public static String encodeForHTML(String str)
+	{
+		return ESAPI.encoder().encodeForHTML(str);
+	}
+
+	/**
+	 * Encode string for use in a HTML attribute.
+	 * @param str The string to encode.
+	 * @return str encoded for use in HTML attribute.
+	 * @see Encoder#encodeForHTMLAttribute(String)
+	 */
+	public static String encodeForHTMLAttribute(String str)
+	{
+		return ESAPI.encoder().encodeForHTMLAttribute(str);
+	}
+
+	/**
+	 * Encode string for use in JavaScript.
+	 * @param str The string to encode.
+	 * @return str encoded for use in JavaScript.
+	 * @see Encoder#encodeForJavaScript(String)
+	 */
+	public static String encodeForJavaScript(String str)
+	{
+		return ESAPI.encoder().encodeForJavaScript(str);
+	}
+
+	/**
+	 * Encode string for use in a URL.
+	 * @param str The string to encode.
+	 * @return str encoded for use in a URL.
+	 * @see Encoder#encodeForURL(String)
+	 */
+	public static String encodeForURL(String str) throws EncodingException
+	{
+		return ESAPI.encoder().encodeForURL(str);
+	}
+
+	/**
+	 * Encode string for use in VBScript.
+	 * @param str The string to encode.
+	 * @return str encoded for use in VBScript.
+	 * @see Encoder#encodeForVBScript(String)
+	 */
+	public static String encodeForVBScript(String str)
+	{
+		return ESAPI.encoder().encodeForVBScript(str);
+	}
+
+	/**
+	 * Encode string for use in XML.
+	 * @param str The string to encode.
+	 * @return str encoded for use in XML.
+	 * @see Encoder#encodeForXML(String)
+	 */
+	public static String encodeForXML(String str)
+	{
+		return ESAPI.encoder().encodeForXML(str);
+	}
+
+	/**
+	 * Encode string for use in a XML attribute.
+	 * @param str The string to encode.
+	 * @return str encoded for use in XML attribute.
+	 * @see Encoder#encodeForXMLAttribute(String)
+	 */
+	public static String encodeForXMLAttribute(String str)
+	{
+		return ESAPI.encoder().encodeForXMLAttribute(str);
+	}
+
+	/**
+	 * Encode string for use in XPath.
+	 * @param str The string to encode.
+	 * @return str encoded for use in XPath.
+	 * @see Encoder#encodeForXPath(String)
+	 */
+	public static String encodeForXPath(String str)
+	{
+		return ESAPI.encoder().encodeForXPath(str);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForBase64Tag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForBase64Tag.java.svn-base
new file mode 100644
index 0000000..dbde61a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForBase64Tag.java.svn-base
@@ -0,0 +1,82 @@
+package org.owasp.esapi.tags;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.servlet.jsp.JspTagException;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body using Base64.
+ */
+public class EncodeForBase64Tag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+	/** @serial Flag determining line wrapping */
+	private boolean wrap = false;
+	/**
+	  * @serial Charset to use when converting content from a String
+	  * to byte[].
+	  */
+	private String encoding = "UTF-8";
+
+	/**
+	 * Encode tag's content using Base64.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForBase64(byte[], boolean)}
+	 * @return content encoded in Base64
+	 */
+	protected String encode(String content, Encoder enc) throws JspTagException
+	{
+		try
+		{
+			return enc.encodeForBase64(content.getBytes(encoding), wrap);
+		}
+		catch(UnsupportedEncodingException e)
+		{
+			throw new JspTagException("Unsupported encoding " + enc,e);
+		}
+	}
+
+	/**
+	 * Set the encoding used to convert the content to bytes for
+	 * encoding. This defaults to UTF-8 if not specified.
+	 * @param encoding The encoding passed to {@link String#getBytes(String)}.
+	 */
+	public void setEncoding(String encoding)
+	{
+		this.encoding=encoding;
+	}
+
+	/**
+	 * Get the encoding used to convert the content to bytes for
+	 * encoding.
+	 * @return encoding The encoding passed to
+	 * {@link String#getBytes(String)}.
+	 */
+	public String getEncoding()
+	{
+		return encoding;
+	}
+
+	/**
+	 * Set whether line wrapping at 64 characters is performed. This
+	 * defaults to false.
+	 * @param wrap flag determining wrapping.
+	 */
+	public void setWrap(boolean wrap)
+	{
+		this.wrap=wrap;
+	}
+
+	/**
+	 * Get whether line wrapping at 64 characters is performed. This
+	 * defaults to false.
+	 * @return value of flag determining wrapping.
+	 */
+	public boolean getWrap()
+	{
+		return wrap;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForCSSTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForCSSTag.java.svn-base
new file mode 100644
index 0000000..c4bfebd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForCSSTag.java.svn-base
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in CSS.
+ */
+public class EncodeForCSSTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in CSS.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForCSS(String)}
+	 * @return content encoded for usage in CSS
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForCSS(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForHTMLAttributeTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForHTMLAttributeTag.java.svn-base
new file mode 100644
index 0000000..e407e02
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForHTMLAttributeTag.java.svn-base
@@ -0,0 +1,39 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in a HTML attribute.
+ */
+public class EncodeForHTMLAttributeTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage as a HTML attribute.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForHTMLAttribute(String)}
+	 * @return content encoded for usage as a HTML attribute
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForHTMLAttribute(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForHTMLTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForHTMLTag.java.svn-base
new file mode 100644
index 0000000..d161c98
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForHTMLTag.java.svn-base
@@ -0,0 +1,39 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in HTML.
+ */
+public class EncodeForHTMLTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in HTML.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForHTML(String)}
+	 * @return content encoded for usage in HTML
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForHTML(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForJavaScriptTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForJavaScriptTag.java.svn-base
new file mode 100644
index 0000000..08997d7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForJavaScriptTag.java.svn-base
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in JavaScript.
+ */
+public class EncodeForJavaScriptTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in JavaScript
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForJavaScript(String)}
+	 * @return content encoded for usage in JavaScript
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForJavaScript(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForURLTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForURLTag.java.svn-base
new file mode 100644
index 0000000..d159676
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForURLTag.java.svn-base
@@ -0,0 +1,34 @@
+package org.owasp.esapi.tags;
+
+import javax.servlet.jsp.JspTagException;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.errors.EncodingException;
+
+/**
+ * JSP tag that encode's it's body for use in a URL.
+ */
+public class EncodeForURLTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in a URL.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForURL(String)}
+	 * @return content encoded for usage in a URL
+	 * @throws EncodingException if {@link Encoder#encodeForURL(String)} does.
+	 */
+	protected String encode(String content, Encoder enc) throws JspTagException
+	{
+		try
+		{
+			return enc.encodeForURL(content);
+		}
+		catch(EncodingException e)
+		{
+			throw new JspTagException("Unable to encode to URL encoding", e);
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForVBScriptTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForVBScriptTag.java.svn-base
new file mode 100644
index 0000000..d6b07fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForVBScriptTag.java.svn-base
@@ -0,0 +1,39 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in VBScript.
+ */
+public class EncodeForVBScriptTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in VBScript.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForVBScript(String)}
+	 * @return content encoded for usage in VBScript
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForVBScript(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXMLAttributeTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXMLAttributeTag.java.svn-base
new file mode 100644
index 0000000..c147467
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXMLAttributeTag.java.svn-base
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in a XML attribute.
+ */
+public class EncodeForXMLAttributeTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage as a XML attribute.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForXMLAttribute(String)}
+	 * @return content encoded for usage as a XML attribute
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForXMLAttribute(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXMLTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXMLTag.java.svn-base
new file mode 100644
index 0000000..f86cfe1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXMLTag.java.svn-base
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in XML.
+ */
+public class EncodeForXMLTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in XML.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForXML(String)}
+	 * @return content encoded for usage in XML
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForXML(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXPathTag.java.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXPathTag.java.svn-base
new file mode 100644
index 0000000..064b191
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/EncodeForXPathTag.java.svn-base
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in XPath.
+ */
+public class EncodeForXPathTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in XPath.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForXPath(String)}
+	 * @return content encoded for usage in XPath
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForXPath(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/tags/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..e6df37a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/.svn/text-base/package.html.svn-base
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains sample JSP tags that demonstrate how to use the ESAPI functions
+to protect an application from within a JSP page.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/tags/BaseEncodeTag.java b/src/main/java/org/owasp/esapi/tags/BaseEncodeTag.java
new file mode 100644
index 0000000..c40a04f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/BaseEncodeTag.java
@@ -0,0 +1,70 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspTagException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+
+/** Abstract base class for tags that just encode their bodies with Encoder methods. */
+public abstract class BaseEncodeTag extends BodyTagSupport
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Encode tag's content.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder provided as a convinence.
+	 * @return content encoded by the subclass's implementation.
+	 */
+	protected abstract String encode(String content, Encoder enc) throws JspTagException;
+
+	/**
+	 * After tag body parsing handler. This provides the necessary
+	 * plubming to allow subclasses to just concern themselves with
+	 * encoding a single string.
+	 * @return {@link javax.servlet.jsp.tagext.Tag#SKIP_BODY}
+	 * @throws JspTagException if writing to the bodyContent's
+	 * enclosing writer throws an IOException.
+	 */
+	public int doAfterBody() throws JspTagException
+	{
+		String content;
+		JspWriter out;
+
+		content = bodyContent.getString();
+		out = bodyContent.getEnclosingWriter();
+
+		content = encode(content, ESAPI.encoder());
+		try
+		{
+			out.print(content);
+		}
+		catch (IOException e)
+		{
+			throw new JspTagException("Error writing to body's enclosing JspWriter",e);
+		}
+
+		bodyContent.clearBody();
+		return SKIP_BODY;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/ELEncodeFunctions.java b/src/main/java/org/owasp/esapi/tags/ELEncodeFunctions.java
new file mode 100644
index 0000000..5b74e1e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/ELEncodeFunctions.java
@@ -0,0 +1,172 @@
+package org.owasp.esapi.tags;
+
+import java.io.UnsupportedEncodingException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.errors.EncodingException;
+
+/**
+ * Static encoder methods for JSP EL expression functions.
+ */
+public class ELEncodeFunctions
+{
+	private static final String DEFAULT_ENCODING = "UTF-8";
+
+	/**
+	 * Private constructor as this class shouldn't need to be
+	 * instantiated.
+	 */
+	private ELEncodeFunctions()
+	{
+	}
+
+	/**
+	 * Base64 encode a string. UTF-8 is used to encode the string and no line wrapping is performed.
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if UTF-8 is an unsupported character set. This should not happen as UTF-8 is required to be supported by the JVM spec.
+	 */
+	public static String encodeForBase64(String str) throws UnsupportedEncodingException
+	{
+		return encodeForBase64Charset(DEFAULT_ENCODING, str);
+	}
+
+	/**
+	 * Base64 encode a string with line wrapping. UTF-8 is used to encode the string and lines are wrapped at 64 characters..
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if UTF-8 is an unsupported character set. This should not happen as UTF-8 is required to be supported by the JVM spec.
+	 */
+	public static String encodeForBase64Wrap(String str) throws UnsupportedEncodingException
+	{
+		return encodeForBase64CharsetWrap(DEFAULT_ENCODING, str);
+	}
+
+	/**
+	 * Base64 encode a string after converting to bytes using the specified character set. No line wrapping is performed.
+	 * @param charset The character set used to convert str to bytes.
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if charset is an unsupported character set.
+	 */
+	public static String encodeForBase64Charset(String charset, String str) throws UnsupportedEncodingException
+	{
+		return ESAPI.encoder().encodeForBase64(str.getBytes(charset), false);
+	}
+
+	/**
+	 * Base64 encode a string after converting to bytes using the specified character set and wrapping lines. Lines are wrapped at 64 characters.
+	 * @param charset The character set used to convert str to bytes.
+	 * @param str The string to encode.
+	 * @return The base64 encoded String.
+	 * @see Encoder#encodeForBase64(byte[],boolean)
+	 * @throws UnsupportedEncodingException if charset is an unsupported character set.
+	 */
+	public static String encodeForBase64CharsetWrap(String charset, String str) throws UnsupportedEncodingException
+	{
+		return ESAPI.encoder().encodeForBase64(str.getBytes(charset), true);
+	}
+
+	/**
+	 * Encode string for use in CSS.
+	 * @param str The string to encode.
+	 * @return str encoded for use in CSS.
+	 * @see Encoder#encodeForCSS(String)
+	 */
+	public static String encodeForCSS(String str)
+	{
+		return ESAPI.encoder().encodeForCSS(str);
+	}
+
+	/**
+	 * Encode string for use in HTML.
+	 * @param str The string to encode.
+	 * @return str encoded for use in HTML.
+	 * @see Encoder#encodeForHTML(String)
+	 */
+	public static String encodeForHTML(String str)
+	{
+		return ESAPI.encoder().encodeForHTML(str);
+	}
+
+	/**
+	 * Encode string for use in a HTML attribute.
+	 * @param str The string to encode.
+	 * @return str encoded for use in HTML attribute.
+	 * @see Encoder#encodeForHTMLAttribute(String)
+	 */
+	public static String encodeForHTMLAttribute(String str)
+	{
+		return ESAPI.encoder().encodeForHTMLAttribute(str);
+	}
+
+	/**
+	 * Encode string for use in JavaScript.
+	 * @param str The string to encode.
+	 * @return str encoded for use in JavaScript.
+	 * @see Encoder#encodeForJavaScript(String)
+	 */
+	public static String encodeForJavaScript(String str)
+	{
+		return ESAPI.encoder().encodeForJavaScript(str);
+	}
+
+	/**
+	 * Encode string for use in a URL.
+	 * @param str The string to encode.
+	 * @return str encoded for use in a URL.
+	 * @see Encoder#encodeForURL(String)
+	 */
+	public static String encodeForURL(String str) throws EncodingException
+	{
+		return ESAPI.encoder().encodeForURL(str);
+	}
+
+	/**
+	 * Encode string for use in VBScript.
+	 * @param str The string to encode.
+	 * @return str encoded for use in VBScript.
+	 * @see Encoder#encodeForVBScript(String)
+	 */
+	public static String encodeForVBScript(String str)
+	{
+		return ESAPI.encoder().encodeForVBScript(str);
+	}
+
+	/**
+	 * Encode string for use in XML.
+	 * @param str The string to encode.
+	 * @return str encoded for use in XML.
+	 * @see Encoder#encodeForXML(String)
+	 */
+	public static String encodeForXML(String str)
+	{
+		return ESAPI.encoder().encodeForXML(str);
+	}
+
+	/**
+	 * Encode string for use in a XML attribute.
+	 * @param str The string to encode.
+	 * @return str encoded for use in XML attribute.
+	 * @see Encoder#encodeForXMLAttribute(String)
+	 */
+	public static String encodeForXMLAttribute(String str)
+	{
+		return ESAPI.encoder().encodeForXMLAttribute(str);
+	}
+
+	/**
+	 * Encode string for use in XPath.
+	 * @param str The string to encode.
+	 * @return str encoded for use in XPath.
+	 * @see Encoder#encodeForXPath(String)
+	 */
+	public static String encodeForXPath(String str)
+	{
+		return ESAPI.encoder().encodeForXPath(str);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForBase64Tag.java b/src/main/java/org/owasp/esapi/tags/EncodeForBase64Tag.java
new file mode 100644
index 0000000..dbde61a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForBase64Tag.java
@@ -0,0 +1,82 @@
+package org.owasp.esapi.tags;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.servlet.jsp.JspTagException;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body using Base64.
+ */
+public class EncodeForBase64Tag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+	/** @serial Flag determining line wrapping */
+	private boolean wrap = false;
+	/**
+	  * @serial Charset to use when converting content from a String
+	  * to byte[].
+	  */
+	private String encoding = "UTF-8";
+
+	/**
+	 * Encode tag's content using Base64.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForBase64(byte[], boolean)}
+	 * @return content encoded in Base64
+	 */
+	protected String encode(String content, Encoder enc) throws JspTagException
+	{
+		try
+		{
+			return enc.encodeForBase64(content.getBytes(encoding), wrap);
+		}
+		catch(UnsupportedEncodingException e)
+		{
+			throw new JspTagException("Unsupported encoding " + enc,e);
+		}
+	}
+
+	/**
+	 * Set the encoding used to convert the content to bytes for
+	 * encoding. This defaults to UTF-8 if not specified.
+	 * @param encoding The encoding passed to {@link String#getBytes(String)}.
+	 */
+	public void setEncoding(String encoding)
+	{
+		this.encoding=encoding;
+	}
+
+	/**
+	 * Get the encoding used to convert the content to bytes for
+	 * encoding.
+	 * @return encoding The encoding passed to
+	 * {@link String#getBytes(String)}.
+	 */
+	public String getEncoding()
+	{
+		return encoding;
+	}
+
+	/**
+	 * Set whether line wrapping at 64 characters is performed. This
+	 * defaults to false.
+	 * @param wrap flag determining wrapping.
+	 */
+	public void setWrap(boolean wrap)
+	{
+		this.wrap=wrap;
+	}
+
+	/**
+	 * Get whether line wrapping at 64 characters is performed. This
+	 * defaults to false.
+	 * @return value of flag determining wrapping.
+	 */
+	public boolean getWrap()
+	{
+		return wrap;
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForCSSTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForCSSTag.java
new file mode 100644
index 0000000..c4bfebd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForCSSTag.java
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in CSS.
+ */
+public class EncodeForCSSTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in CSS.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForCSS(String)}
+	 * @return content encoded for usage in CSS
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForCSS(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForHTMLAttributeTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForHTMLAttributeTag.java
new file mode 100644
index 0000000..e407e02
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForHTMLAttributeTag.java
@@ -0,0 +1,39 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in a HTML attribute.
+ */
+public class EncodeForHTMLAttributeTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage as a HTML attribute.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForHTMLAttribute(String)}
+	 * @return content encoded for usage as a HTML attribute
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForHTMLAttribute(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForHTMLTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForHTMLTag.java
new file mode 100644
index 0000000..d161c98
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForHTMLTag.java
@@ -0,0 +1,39 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in HTML.
+ */
+public class EncodeForHTMLTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in HTML.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForHTML(String)}
+	 * @return content encoded for usage in HTML
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForHTML(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForJavaScriptTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForJavaScriptTag.java
new file mode 100644
index 0000000..08997d7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForJavaScriptTag.java
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in JavaScript.
+ */
+public class EncodeForJavaScriptTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in JavaScript
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForJavaScript(String)}
+	 * @return content encoded for usage in JavaScript
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForJavaScript(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForURLTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForURLTag.java
new file mode 100644
index 0000000..d159676
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForURLTag.java
@@ -0,0 +1,34 @@
+package org.owasp.esapi.tags;
+
+import javax.servlet.jsp.JspTagException;
+
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.errors.EncodingException;
+
+/**
+ * JSP tag that encode's it's body for use in a URL.
+ */
+public class EncodeForURLTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in a URL.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForURL(String)}
+	 * @return content encoded for usage in a URL
+	 * @throws EncodingException if {@link Encoder#encodeForURL(String)} does.
+	 */
+	protected String encode(String content, Encoder enc) throws JspTagException
+	{
+		try
+		{
+			return enc.encodeForURL(content);
+		}
+		catch(EncodingException e)
+		{
+			throw new JspTagException("Unable to encode to URL encoding", e);
+		}
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForVBScriptTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForVBScriptTag.java
new file mode 100644
index 0000000..d6b07fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForVBScriptTag.java
@@ -0,0 +1,39 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in VBScript.
+ */
+public class EncodeForVBScriptTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in VBScript.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForVBScript(String)}
+	 * @return content encoded for usage in VBScript
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForVBScript(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForXMLAttributeTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForXMLAttributeTag.java
new file mode 100644
index 0000000..c147467
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForXMLAttributeTag.java
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in a XML attribute.
+ */
+public class EncodeForXMLAttributeTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage as a XML attribute.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForXMLAttribute(String)}
+	 * @return content encoded for usage as a XML attribute
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForXMLAttribute(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForXMLTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForXMLTag.java
new file mode 100644
index 0000000..f86cfe1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForXMLTag.java
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in XML.
+ */
+public class EncodeForXMLTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in XML.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForXML(String)}
+	 * @return content encoded for usage in XML
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForXML(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/EncodeForXPathTag.java b/src/main/java/org/owasp/esapi/tags/EncodeForXPathTag.java
new file mode 100644
index 0000000..064b191
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/EncodeForXPathTag.java
@@ -0,0 +1,23 @@
+package org.owasp.esapi.tags;
+
+import org.owasp.esapi.Encoder;
+
+/**
+ * JSP tag that encode's it's body for use in XPath.
+ */
+public class EncodeForXPathTag extends BaseEncodeTag
+{
+	private static final long serialVersionUID = 3L;
+
+	/**
+	 * Encode tag's content for usage in XPath.
+	 * @param content The tag's content as a String
+	 * @param enc Encoder used to call
+	 * 	{@link Encoder#encodeForXPath(String)}
+	 * @return content encoded for usage in XPath
+	 */
+	protected String encode(String content, Encoder enc)
+	{
+		return enc.encodeForXPath(content);
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/tags/package.html b/src/main/java/org/owasp/esapi/tags/package.html
new file mode 100644
index 0000000..e6df37a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/tags/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains sample JSP tags that demonstrate how to use the ESAPI functions
+to protect an application from within a JSP page.
+
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/util/.svn/all-wcprops b/src/main/java/org/owasp/esapi/util/.svn/all-wcprops
new file mode 100644
index 0000000..d881fe4
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util
+END
+ObjFactory.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util/ObjFactory.java
+END
+DefaultMessageUtil.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util/DefaultMessageUtil.java
+END
+ByteConversionUtil.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util/ByteConversionUtil.java
+END
+NullSafe.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util/NullSafe.java
+END
+CollectionsUtil.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util/CollectionsUtil.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util/package.html
+END
diff --git a/src/main/java/org/owasp/esapi/util/.svn/entries b/src/main/java/org/owasp/esapi/util/.svn/entries
new file mode 100644
index 0000000..5f51455
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/entries
@@ -0,0 +1,232 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/util
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-07-26T04:43:30.557957Z
+1448
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+DefaultMessageUtil.java
+file
+
+
+
+
+2014-02-18T16:19:53.481976Z
+6f8a6c8b7dbebdd61bf9493f00cebd87
+2009-06-06T15:14:24.195945Z
+531
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1531
+

+ByteConversionUtil.java
+file
+
+
+
+
+2014-02-18T16:19:53.481976Z
+b793d6ea5f2d27babf20e5dc489399ea
+2010-01-24T05:35:19.820400Z
+995
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4569
+

+NullSafe.java
+file
+
+
+
+
+2014-02-18T16:19:53.481976Z
+8fde4228c90c604d91c4ee755039a08c
+2009-11-09T04:39:12.162255Z
+761
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1022
+

+CollectionsUtil.java
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+5cc42ddd4c0d0871a26109baabdc94b8
+2009-11-28T05:08:08.098660Z
+839
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2905
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:53.485977Z
+dfd7f82a1560e84cdfd0ad11ba383774
+2009-11-09T04:39:12.162255Z
+761
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+362
+

+ObjFactory.java
+file
+
+
+
+
+2014-02-18T16:19:53.481976Z
+6aa8da8c75f23556a3116fa75e175b8c
+2010-07-26T04:43:30.557957Z
+1448
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6645
+

diff --git a/src/main/java/org/owasp/esapi/util/.svn/prop-base/ByteConversionUtil.java.svn-base b/src/main/java/org/owasp/esapi/util/.svn/prop-base/ByteConversionUtil.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/prop-base/ByteConversionUtil.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/util/.svn/prop-base/ObjFactory.java.svn-base b/src/main/java/org/owasp/esapi/util/.svn/prop-base/ObjFactory.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/prop-base/ObjFactory.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/util/.svn/prop-base/package.html.svn-base b/src/main/java/org/owasp/esapi/util/.svn/prop-base/package.html.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/prop-base/package.html.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/main/java/org/owasp/esapi/util/.svn/text-base/ByteConversionUtil.java.svn-base b/src/main/java/org/owasp/esapi/util/.svn/text-base/ByteConversionUtil.java.svn-base
new file mode 100644
index 0000000..09d19c7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/text-base/ByteConversionUtil.java.svn-base
@@ -0,0 +1,128 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2010 - The OWASP Foundation
+ */
+package org.owasp.esapi.util;
+
+/**
+ * Conversion to/from byte arrays to/from short, int, long. The assumption
+ * is that they byte arrays are in network byte order (i.e., big-endian
+ * ordered).
+ *
+ * @see org.owasp.esapi.crypto.CipherTextSerializer
+ * @author kevin.w.wall at gmail.com
+ */
+public class ByteConversionUtil {
+
+    ////////// Convert from short, int, long to byte array. //////////
+
+    /**
+     * Returns a byte array containing 2 network byte ordered bytes representing
+     * the given {@code short}.
+     *
+     * @param input An {@code short} to convert to a byte array.
+     * @return A byte array representation of an {@code short} in network byte
+     * order (i.e., big-endian order).
+     */
+    public static byte[] fromShort(short input) {
+        byte[] output = new byte[2];
+        output[0] = (byte) (input >> 8);
+        output[1] = (byte) input;
+        return output;
+    }
+
+    /**
+     * Returns a byte array containing 4 network byte-ordered bytes representing the
+     * given {@code int}.
+     *
+     * @param input An {@code int} to convert to a byte array.
+     * @return A byte array representation of an {@code int} in network byte order
+     * (i.e., big-endian order).
+     */
+    public static byte[] fromInt(int input) {
+        byte[] output = new byte[4];
+        output[0] = (byte) (input >> 24);
+        output[1] = (byte) (input >> 16);
+        output[2] = (byte) (input >> 8);
+        output[3] = (byte) input;
+        return output;
+    }
+
+    /**
+     * Returns a byte array containing 8 network byte-ordered bytes representing
+     * the given {@code long}.
+     *
+     * @param input The {@code long} to convert to a {@code byte} array.
+     * @return A byte array representation of a {@code long}.
+     */
+    public static byte[] fromLong(long input) {
+        byte[] output = new byte[8];
+        output[0] = (byte) (input >> 56);
+        output[1] = (byte) (input >> 48);
+        output[2] = (byte) (input >> 40);
+        output[3] = (byte) (input >> 32);
+        output[4] = (byte) (input >> 24);
+        output[5] = (byte) (input >> 16);
+        output[6] = (byte) (input >> 8);
+        output[7] = (byte) input;
+        return output;
+    }
+
+    ////////// Convert from byte array to short, int, long. //////////
+
+    /**
+     * Converts a given byte array to an {@code short}. Bytes are expected in
+     * network byte
+     * order.
+     *
+     * @param input A network byte-ordered representation of an {@code short}.
+     * @return The {@code short} value represented by the input array.
+     */
+    public static short toShort(byte[] input) {
+        assert input.length == 2 : "toShort(): Byte array length must be 2.";
+        short output = 0;
+        output = (short)(((input[0] & 0xff) << 8) | (input[1] & 0xff));
+        return output;
+    }
+
+    /**
+     * Converts a given byte array to an {@code int}. Bytes are expected in
+     * network byte order.
+     *
+     * @param input A network byte-ordered representation of an {@code int}.
+     * @return The {@code int} value represented by the input array.
+     */
+    public static int toInt(byte[] input) {
+        assert input.length == 4 : "toInt(): Byte array length must be 4.";
+        int output = 0;
+        output = ((input[0] & 0xff) << 24) | ((input[1] & 0xff) << 16) |
+                 ((input[2] & 0xff) << 8) | (input[3] & 0xff);
+        return output;
+    }
+
+    /**
+     * Converts a given byte array to a {@code long}. Bytes are expected in
+     * network byte
+     *
+     * @param input A network byte-ordered representation of a {@code long}.
+     * @return The {@code long} value represented by the input array
+     */
+    public static long toLong(byte[] input) {
+        assert input.length == 8 : "toLong(): Byte array length must be 8.";
+        long output = 0;
+        output  = ((long)(input[0] & 0xff) << 56);
+        output |= ((long)(input[1] & 0xff) << 48);
+        output |= ((long)(input[2] & 0xff) << 40);
+        output |= ((long)(input[3] & 0xff) << 32);
+        output |= ((long)(input[4] & 0xff) << 24);
+        output |= ((long)(input[5] & 0xff) << 16);
+        output |= ((long)(input[6] & 0xff) << 8);
+        output |= (input[7] & 0xff);
+        return output;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/util/.svn/text-base/CollectionsUtil.java.svn-base b/src/main/java/org/owasp/esapi/util/.svn/text-base/CollectionsUtil.java.svn-base
new file mode 100644
index 0000000..933da06
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/text-base/CollectionsUtil.java.svn-base
@@ -0,0 +1,115 @@
+/**
+ * 
+ */
+package org.owasp.esapi.util;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Neil Matatall (neil.matatall .at. gmail.com)
+ * 
+ * Are these necessary?  Are there any libraries or java.lang classes to take
+ * care of the conversions?
+ * 
+ * FIXME: we can convert to using this, but it requires that the array be of Character, not char
+ *      new HashSet(Arrays.asList(array))
+ * 
+ */
+public class CollectionsUtil
+{
+	private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+
+	/**
+	 * Converts an array of chars to a Set of Characters. 
+	 * @param array the contents of the new Set
+	 * @return a Set containing the elements in the array
+	 */
+	public static Set<Character> arrayToSet(char...array)
+	{
+		Set<Character> toReturn;
+
+		if(array == null)
+			return new HashSet<Character>();
+		toReturn = new HashSet<Character>(array.length);
+		for (char c : array) {
+			toReturn.add(c);
+		}
+		return toReturn;
+	}
+
+	/**
+	 * Convert a char array to a unmodifiable Set.
+	 * @param array the contents of the new Set
+	 * @return a unmodifiable Set containing the elements in the
+	 * array.
+	 */
+	public static Set<Character> arrayToUnmodifiableSet(char...array)
+	{
+		if(array == null)
+			return Collections.emptySet();
+		if(array.length == 1)
+			return Collections.singleton(array[0]);
+		return Collections.unmodifiableSet(arrayToSet(array));
+	}
+
+	/**
+	 * Convert a String to a char array
+	 * @param str The string to convert
+	 * @return character array containing the characters in str. An
+	 * 	empty array is returned if str is null.
+	 */
+	public static char[] strToChars(String str)
+	{
+		int len;
+		char[] ret;
+
+		if(str == null)
+			return EMPTY_CHAR_ARRAY;
+		len = str.length();
+		ret = new char[len];
+		str.getChars(0,len,ret,0);
+		return ret;
+	}
+
+	/**
+	 * Convert a String to a set of characters.
+	 * @param str The string to convert
+	 * @return A set containing the characters in str. A empty set
+	 * 	is returned if str is null.
+	 */
+	public static Set<Character> strToSet(String str)
+	{
+		Set<Character> set;
+
+		if(str == null)
+			return new HashSet<Character>();
+		set = new HashSet<Character>(str.length());
+		for(int i=0;i<str.length();i++)
+			set.add(str.charAt(i));
+		return set;
+	}
+
+	/**
+	 * Convert a String to a unmodifiable set of characters.
+	 * @param str The string to convert
+	 * @return A set containing the characters in str. A empty set
+	 * 	is returned if str is null.
+	 */
+	public static Set<Character> strToUnmodifiableSet(String str)
+	{
+		if(str == null)
+			return Collections.emptySet();
+		if(str.length() == 1)
+			return Collections.singleton(str.charAt(0));
+		return Collections.unmodifiableSet(strToSet(str));
+	}
+
+	/**
+	 * Private constructor to prevent instantiation.
+	 */
+	private CollectionsUtil()
+	{
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/util/.svn/text-base/DefaultMessageUtil.java.svn-base b/src/main/java/org/owasp/esapi/util/.svn/text-base/DefaultMessageUtil.java.svn-base
new file mode 100644
index 0000000..bb0e21b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/text-base/DefaultMessageUtil.java.svn-base
@@ -0,0 +1,49 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Pawan Singh (pawan.singh at owasp.org) <a href="www.owasp.org">OWASP</a>
+ * @created 2009
+ */
+package org.owasp.esapi.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.owasp.esapi.ESAPI;
+
+/**
+ * @author Pawan Singh (pawan.singh at owasp.org)
+ *
+ */
+public class DefaultMessageUtil {
+
+    private final String DEFAULT_LOCALE_LANG = "en";
+    private final String DEFAULT_LOCALE_LOC = "US";
+    
+    private ResourceBundle messages = null;
+    
+    public void initialize() {
+    	try {
+                messages = ResourceBundle.getBundle("ESAPI", ESAPI.authenticator().getCurrentUser().getLocale());
+        } catch (Exception e) {
+                messages = ResourceBundle.getBundle("ESAPI", new Locale(DEFAULT_LOCALE_LANG,DEFAULT_LOCALE_LOC));
+        }
+    }
+
+
+	public String getMessage(String msgKey, Object[] arguments) {
+		
+		initialize();
+		return MessageFormat.format( messages.getString(msgKey), arguments );
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/util/.svn/text-base/NullSafe.java.svn-base b/src/main/java/org/owasp/esapi/util/.svn/text-base/NullSafe.java.svn-base
new file mode 100644
index 0000000..062c0fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/text-base/NullSafe.java.svn-base
@@ -0,0 +1,52 @@
+package org.owasp.esapi.util;
+
+public class NullSafe
+{
+	/**
+	 * Class should not be instantiated.
+	 */
+	private NullSafe()
+	{
+	}
+
+	/**
+	 * {@link Object#equals(Object)} that safely handles nulls.
+	 * @param a First object
+	 * @param b Second object
+	 * @return true if a == b or a.equals(b). false otherwise.
+	 */
+	public static boolean equals(Object a, Object b)
+	{
+		if(a==b)	// short cut same object
+			return true;
+		if(a == null)
+			return (b == null);
+		if(b == null)
+			return false;
+		return a.equals(b);
+	}
+
+	/**
+	 * {@link Object#hashCode()} of an object.
+	 * @param o Object to get a hashCode for.
+	 * @return 0 if o is null. Otherwise o.hashCode().
+	 */
+	public static int hashCode(Object o)
+	{
+		if(o == null)
+			return 0;
+		return o.hashCode();
+	}
+
+	/**
+	 * {@link Object#toString()} of an object.
+	 * @param o Object to get a String for.
+	 * @return "(null)" o is null. Otherwise o.toString().
+	 */
+	public static String toString(Object o)
+	{
+		if(o == null)
+			return "(null)";
+		return o.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/util/.svn/text-base/ObjFactory.java.svn-base b/src/main/java/org/owasp/esapi/util/.svn/text-base/ObjFactory.java.svn-base
new file mode 100644
index 0000000..fce6afe
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/text-base/ObjFactory.java.svn-base
@@ -0,0 +1,138 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.util;
+
+import org.owasp.esapi.errors.ConfigurationException;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * A generic object factory to create an object of class T. T must be a concrete
+ * class that has a no-argument public constructor or a implementor of the Singleton pattern
+ * that has a no-arg static getInstance method. If the class being created has a getInstance
+ * method, it will be used as a singleton and newInstance() will never be called on the
+ * class no matter how many times it comes through this factory.
+ *
+ * <p>
+ * Typical use is something like:
+ * <pre>
+ * 		import com.example.interfaces.DrinkingEstablishment;
+ * 		import com.example.interfaces.Beer;
+ * 		...
+ * 		// Typically these would be populated from some Java properties file
+ * 		String barName = "com.example.foo.Bar";
+ * 		String beerBrand = "com.example.brewery.Guiness";
+ * 		...
+ * 		DrinkingEstablishment bar = ObjFactory.make(barName, "DrinkingEstablishment");
+ * 		Beer beer = ObjFactory.make(beerBrand, "Beer");
+ *		bar.drink(beer);	// Drink a Guiness beer at the foo Bar. :)
+ *		...
+ * </pre>
+ * </p><p>
+ *  Copyright (c) 2009 - The OWASP Foundation
+ *  </p>
+ * @author kevin.w.wall at gmail.com
+ * @author Chris Schmidt ( chrisisbeef .at. gmail.com )
+ */
+public class ObjFactory {
+
+	/**
+	 * Create an object based on the <code>className</code> parameter.
+	 * 
+	 * @param className	The name of the class to construct. Should be a fully qualified name and
+	 * 					generally the same as type <code>T</code>
+	 * @param typeName	A type name used in error messages / exceptions.
+	 * @return	An object of type <code>className</code>, which is cast to type <code>T</code>.
+	 * @throws	ConfigurationException thrown if class name not found in class path, or does not
+	 * 			have a public, no-argument constructor, or is not a concrete class, or if it is
+	 * 			not a sub-type of <code>T</code> (or <code>T</code> itself). Usually this is
+	 * 			caused by a misconfiguration of the class names specified in the ESAPI.properties
+	 * 			file. Also thrown if the CTOR of the specified <code>className</code> throws
+	 * 			an <code>Exception</code> of some type.
+	 */
+	@SuppressWarnings({ "unchecked" })	// Added because of Eclipse warnings, but ClassCastException IS caught.
+	public static <T> T make(String className, String typeName) throws ConfigurationException {
+		Object obj = null;
+		String errMsg = null;
+		try {
+			if (null == className || "".equals(className) ) {
+				throw new IllegalArgumentException("Classname cannot be null or empty.");
+			}
+			if (null == typeName || "".equals(typeName) ) {
+				// No big deal...just use "[unknown?]" for this as it's only for an err msg.
+				typeName = "[unknown?]";	// CHECKME: Any better suggestions?
+			}
+			
+			Class<?> theClass = Class.forName(className);
+
+            try {
+                Method singleton = theClass.getMethod( "getInstance" );
+
+                // If the implementation class contains a getInstance method that is not static, this is an invalid
+                // object configuration and a ConfigurationException will be thrown.
+                if ( !Modifier.isStatic( singleton.getModifiers() ) )
+                {
+                    throw new ConfigurationException( "Class [" + className + "] contains a non-static getInstance method." );
+                }
+                
+                obj = singleton.invoke( null );
+            } catch (NoSuchMethodException e) {
+                // This is a no-error exception, if this is caught we will continue on assuming the implementation was
+                // not meant to be used as a singleton.
+                obj = theClass.newInstance();
+            } catch (SecurityException e) {
+                // The class is meant to be singleton, however, the SecurityManager restricts us from calling the
+                // getInstance method on the class, thus this is a configuration issue and a ConfigurationException
+                // is thrown
+                throw new ConfigurationException( "The SecurityManager has restricted the object factory from getting a reference to the singleton implementation" +
+                        "of the class [" + className + "]", e );
+            }
+
+			return (T)obj;		// Eclipse warning here if @SupressWarnings omitted.
+			
+            // Issue 66 - Removed System.out calls as we are throwing an exception in each of these cases
+            // anyhow.
+		} catch( IllegalArgumentException ex ) {
+			errMsg = ex.toString() + " " + typeName + " type name cannot be null or empty.";
+			throw new ConfigurationException(errMsg, ex);
+		}catch ( ClassNotFoundException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be in class path.";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( InstantiationException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be concrete.";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( IllegalAccessException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must have a public, no-arg constructor.";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( ClassCastException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be a subtype of T in ObjFactory<T>";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( Exception ex ) {
+			// Because we are using reflection, we want to catch any checked or unchecked Exceptions and
+			// re-throw them in a way we can handle them. Because using reflection to construct the object,
+			// we can't have the compiler notify us of uncaught exceptions. For example, JavaEncryptor()
+			// CTOR can throw [well, now it can] an EncryptionException if something goes wrong. That case
+			// is taken care of here.
+			//
+			// CHECKME: Should we first catch RuntimeExceptions so we just let unchecked Exceptions go through
+			//		    unaltered???
+			//
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") CTOR threw exception.";
+			throw new ConfigurationException(errMsg, ex);
+		}
+		// DISCUSS: Should we also catch ExceptionInInitializerError here? See Google Issue #61 comments.
+	}
+	
+	/**
+	 * Not instantiable
+	 */
+	private ObjFactory() { }
+}
diff --git a/src/main/java/org/owasp/esapi/util/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/util/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..2be7784
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/.svn/text-base/package.html.svn-base
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body bgcolor="white">
+This package contains ESAPI utility classes used throughout the
+reference implementation of ESAPI but may also be directly useful.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/util/ByteConversionUtil.java b/src/main/java/org/owasp/esapi/util/ByteConversionUtil.java
new file mode 100644
index 0000000..09d19c7
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/ByteConversionUtil.java
@@ -0,0 +1,128 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright © 2010 - The OWASP Foundation
+ */
+package org.owasp.esapi.util;
+
+/**
+ * Conversion to/from byte arrays to/from short, int, long. The assumption
+ * is that they byte arrays are in network byte order (i.e., big-endian
+ * ordered).
+ *
+ * @see org.owasp.esapi.crypto.CipherTextSerializer
+ * @author kevin.w.wall at gmail.com
+ */
+public class ByteConversionUtil {
+
+    ////////// Convert from short, int, long to byte array. //////////
+
+    /**
+     * Returns a byte array containing 2 network byte ordered bytes representing
+     * the given {@code short}.
+     *
+     * @param input An {@code short} to convert to a byte array.
+     * @return A byte array representation of an {@code short} in network byte
+     * order (i.e., big-endian order).
+     */
+    public static byte[] fromShort(short input) {
+        byte[] output = new byte[2];
+        output[0] = (byte) (input >> 8);
+        output[1] = (byte) input;
+        return output;
+    }
+
+    /**
+     * Returns a byte array containing 4 network byte-ordered bytes representing the
+     * given {@code int}.
+     *
+     * @param input An {@code int} to convert to a byte array.
+     * @return A byte array representation of an {@code int} in network byte order
+     * (i.e., big-endian order).
+     */
+    public static byte[] fromInt(int input) {
+        byte[] output = new byte[4];
+        output[0] = (byte) (input >> 24);
+        output[1] = (byte) (input >> 16);
+        output[2] = (byte) (input >> 8);
+        output[3] = (byte) input;
+        return output;
+    }
+
+    /**
+     * Returns a byte array containing 8 network byte-ordered bytes representing
+     * the given {@code long}.
+     *
+     * @param input The {@code long} to convert to a {@code byte} array.
+     * @return A byte array representation of a {@code long}.
+     */
+    public static byte[] fromLong(long input) {
+        byte[] output = new byte[8];
+        output[0] = (byte) (input >> 56);
+        output[1] = (byte) (input >> 48);
+        output[2] = (byte) (input >> 40);
+        output[3] = (byte) (input >> 32);
+        output[4] = (byte) (input >> 24);
+        output[5] = (byte) (input >> 16);
+        output[6] = (byte) (input >> 8);
+        output[7] = (byte) input;
+        return output;
+    }
+
+    ////////// Convert from byte array to short, int, long. //////////
+
+    /**
+     * Converts a given byte array to an {@code short}. Bytes are expected in
+     * network byte
+     * order.
+     *
+     * @param input A network byte-ordered representation of an {@code short}.
+     * @return The {@code short} value represented by the input array.
+     */
+    public static short toShort(byte[] input) {
+        assert input.length == 2 : "toShort(): Byte array length must be 2.";
+        short output = 0;
+        output = (short)(((input[0] & 0xff) << 8) | (input[1] & 0xff));
+        return output;
+    }
+
+    /**
+     * Converts a given byte array to an {@code int}. Bytes are expected in
+     * network byte order.
+     *
+     * @param input A network byte-ordered representation of an {@code int}.
+     * @return The {@code int} value represented by the input array.
+     */
+    public static int toInt(byte[] input) {
+        assert input.length == 4 : "toInt(): Byte array length must be 4.";
+        int output = 0;
+        output = ((input[0] & 0xff) << 24) | ((input[1] & 0xff) << 16) |
+                 ((input[2] & 0xff) << 8) | (input[3] & 0xff);
+        return output;
+    }
+
+    /**
+     * Converts a given byte array to a {@code long}. Bytes are expected in
+     * network byte
+     *
+     * @param input A network byte-ordered representation of a {@code long}.
+     * @return The {@code long} value represented by the input array
+     */
+    public static long toLong(byte[] input) {
+        assert input.length == 8 : "toLong(): Byte array length must be 8.";
+        long output = 0;
+        output  = ((long)(input[0] & 0xff) << 56);
+        output |= ((long)(input[1] & 0xff) << 48);
+        output |= ((long)(input[2] & 0xff) << 40);
+        output |= ((long)(input[3] & 0xff) << 32);
+        output |= ((long)(input[4] & 0xff) << 24);
+        output |= ((long)(input[5] & 0xff) << 16);
+        output |= ((long)(input[6] & 0xff) << 8);
+        output |= (input[7] & 0xff);
+        return output;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/util/CollectionsUtil.java b/src/main/java/org/owasp/esapi/util/CollectionsUtil.java
new file mode 100644
index 0000000..933da06
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/CollectionsUtil.java
@@ -0,0 +1,115 @@
+/**
+ * 
+ */
+package org.owasp.esapi.util;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Neil Matatall (neil.matatall .at. gmail.com)
+ * 
+ * Are these necessary?  Are there any libraries or java.lang classes to take
+ * care of the conversions?
+ * 
+ * FIXME: we can convert to using this, but it requires that the array be of Character, not char
+ *      new HashSet(Arrays.asList(array))
+ * 
+ */
+public class CollectionsUtil
+{
+	private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+
+	/**
+	 * Converts an array of chars to a Set of Characters. 
+	 * @param array the contents of the new Set
+	 * @return a Set containing the elements in the array
+	 */
+	public static Set<Character> arrayToSet(char...array)
+	{
+		Set<Character> toReturn;
+
+		if(array == null)
+			return new HashSet<Character>();
+		toReturn = new HashSet<Character>(array.length);
+		for (char c : array) {
+			toReturn.add(c);
+		}
+		return toReturn;
+	}
+
+	/**
+	 * Convert a char array to a unmodifiable Set.
+	 * @param array the contents of the new Set
+	 * @return a unmodifiable Set containing the elements in the
+	 * array.
+	 */
+	public static Set<Character> arrayToUnmodifiableSet(char...array)
+	{
+		if(array == null)
+			return Collections.emptySet();
+		if(array.length == 1)
+			return Collections.singleton(array[0]);
+		return Collections.unmodifiableSet(arrayToSet(array));
+	}
+
+	/**
+	 * Convert a String to a char array
+	 * @param str The string to convert
+	 * @return character array containing the characters in str. An
+	 * 	empty array is returned if str is null.
+	 */
+	public static char[] strToChars(String str)
+	{
+		int len;
+		char[] ret;
+
+		if(str == null)
+			return EMPTY_CHAR_ARRAY;
+		len = str.length();
+		ret = new char[len];
+		str.getChars(0,len,ret,0);
+		return ret;
+	}
+
+	/**
+	 * Convert a String to a set of characters.
+	 * @param str The string to convert
+	 * @return A set containing the characters in str. A empty set
+	 * 	is returned if str is null.
+	 */
+	public static Set<Character> strToSet(String str)
+	{
+		Set<Character> set;
+
+		if(str == null)
+			return new HashSet<Character>();
+		set = new HashSet<Character>(str.length());
+		for(int i=0;i<str.length();i++)
+			set.add(str.charAt(i));
+		return set;
+	}
+
+	/**
+	 * Convert a String to a unmodifiable set of characters.
+	 * @param str The string to convert
+	 * @return A set containing the characters in str. A empty set
+	 * 	is returned if str is null.
+	 */
+	public static Set<Character> strToUnmodifiableSet(String str)
+	{
+		if(str == null)
+			return Collections.emptySet();
+		if(str.length() == 1)
+			return Collections.singleton(str.charAt(0));
+		return Collections.unmodifiableSet(strToSet(str));
+	}
+
+	/**
+	 * Private constructor to prevent instantiation.
+	 */
+	private CollectionsUtil()
+	{
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/util/DefaultMessageUtil.java b/src/main/java/org/owasp/esapi/util/DefaultMessageUtil.java
new file mode 100644
index 0000000..bb0e21b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/DefaultMessageUtil.java
@@ -0,0 +1,49 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Pawan Singh (pawan.singh at owasp.org) <a href="www.owasp.org">OWASP</a>
+ * @created 2009
+ */
+package org.owasp.esapi.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.owasp.esapi.ESAPI;
+
+/**
+ * @author Pawan Singh (pawan.singh at owasp.org)
+ *
+ */
+public class DefaultMessageUtil {
+
+    private final String DEFAULT_LOCALE_LANG = "en";
+    private final String DEFAULT_LOCALE_LOC = "US";
+    
+    private ResourceBundle messages = null;
+    
+    public void initialize() {
+    	try {
+                messages = ResourceBundle.getBundle("ESAPI", ESAPI.authenticator().getCurrentUser().getLocale());
+        } catch (Exception e) {
+                messages = ResourceBundle.getBundle("ESAPI", new Locale(DEFAULT_LOCALE_LANG,DEFAULT_LOCALE_LOC));
+        }
+    }
+
+
+	public String getMessage(String msgKey, Object[] arguments) {
+		
+		initialize();
+		return MessageFormat.format( messages.getString(msgKey), arguments );
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/util/NullSafe.java b/src/main/java/org/owasp/esapi/util/NullSafe.java
new file mode 100644
index 0000000..062c0fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/NullSafe.java
@@ -0,0 +1,52 @@
+package org.owasp.esapi.util;
+
+public class NullSafe
+{
+	/**
+	 * Class should not be instantiated.
+	 */
+	private NullSafe()
+	{
+	}
+
+	/**
+	 * {@link Object#equals(Object)} that safely handles nulls.
+	 * @param a First object
+	 * @param b Second object
+	 * @return true if a == b or a.equals(b). false otherwise.
+	 */
+	public static boolean equals(Object a, Object b)
+	{
+		if(a==b)	// short cut same object
+			return true;
+		if(a == null)
+			return (b == null);
+		if(b == null)
+			return false;
+		return a.equals(b);
+	}
+
+	/**
+	 * {@link Object#hashCode()} of an object.
+	 * @param o Object to get a hashCode for.
+	 * @return 0 if o is null. Otherwise o.hashCode().
+	 */
+	public static int hashCode(Object o)
+	{
+		if(o == null)
+			return 0;
+		return o.hashCode();
+	}
+
+	/**
+	 * {@link Object#toString()} of an object.
+	 * @param o Object to get a String for.
+	 * @return "(null)" o is null. Otherwise o.toString().
+	 */
+	public static String toString(Object o)
+	{
+		if(o == null)
+			return "(null)";
+		return o.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/util/ObjFactory.java b/src/main/java/org/owasp/esapi/util/ObjFactory.java
new file mode 100644
index 0000000..fce6afe
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/ObjFactory.java
@@ -0,0 +1,138 @@
+/*
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ */
+package org.owasp.esapi.util;
+
+import org.owasp.esapi.errors.ConfigurationException;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * A generic object factory to create an object of class T. T must be a concrete
+ * class that has a no-argument public constructor or a implementor of the Singleton pattern
+ * that has a no-arg static getInstance method. If the class being created has a getInstance
+ * method, it will be used as a singleton and newInstance() will never be called on the
+ * class no matter how many times it comes through this factory.
+ *
+ * <p>
+ * Typical use is something like:
+ * <pre>
+ * 		import com.example.interfaces.DrinkingEstablishment;
+ * 		import com.example.interfaces.Beer;
+ * 		...
+ * 		// Typically these would be populated from some Java properties file
+ * 		String barName = "com.example.foo.Bar";
+ * 		String beerBrand = "com.example.brewery.Guiness";
+ * 		...
+ * 		DrinkingEstablishment bar = ObjFactory.make(barName, "DrinkingEstablishment");
+ * 		Beer beer = ObjFactory.make(beerBrand, "Beer");
+ *		bar.drink(beer);	// Drink a Guiness beer at the foo Bar. :)
+ *		...
+ * </pre>
+ * </p><p>
+ *  Copyright (c) 2009 - The OWASP Foundation
+ *  </p>
+ * @author kevin.w.wall at gmail.com
+ * @author Chris Schmidt ( chrisisbeef .at. gmail.com )
+ */
+public class ObjFactory {
+
+	/**
+	 * Create an object based on the <code>className</code> parameter.
+	 * 
+	 * @param className	The name of the class to construct. Should be a fully qualified name and
+	 * 					generally the same as type <code>T</code>
+	 * @param typeName	A type name used in error messages / exceptions.
+	 * @return	An object of type <code>className</code>, which is cast to type <code>T</code>.
+	 * @throws	ConfigurationException thrown if class name not found in class path, or does not
+	 * 			have a public, no-argument constructor, or is not a concrete class, or if it is
+	 * 			not a sub-type of <code>T</code> (or <code>T</code> itself). Usually this is
+	 * 			caused by a misconfiguration of the class names specified in the ESAPI.properties
+	 * 			file. Also thrown if the CTOR of the specified <code>className</code> throws
+	 * 			an <code>Exception</code> of some type.
+	 */
+	@SuppressWarnings({ "unchecked" })	// Added because of Eclipse warnings, but ClassCastException IS caught.
+	public static <T> T make(String className, String typeName) throws ConfigurationException {
+		Object obj = null;
+		String errMsg = null;
+		try {
+			if (null == className || "".equals(className) ) {
+				throw new IllegalArgumentException("Classname cannot be null or empty.");
+			}
+			if (null == typeName || "".equals(typeName) ) {
+				// No big deal...just use "[unknown?]" for this as it's only for an err msg.
+				typeName = "[unknown?]";	// CHECKME: Any better suggestions?
+			}
+			
+			Class<?> theClass = Class.forName(className);
+
+            try {
+                Method singleton = theClass.getMethod( "getInstance" );
+
+                // If the implementation class contains a getInstance method that is not static, this is an invalid
+                // object configuration and a ConfigurationException will be thrown.
+                if ( !Modifier.isStatic( singleton.getModifiers() ) )
+                {
+                    throw new ConfigurationException( "Class [" + className + "] contains a non-static getInstance method." );
+                }
+                
+                obj = singleton.invoke( null );
+            } catch (NoSuchMethodException e) {
+                // This is a no-error exception, if this is caught we will continue on assuming the implementation was
+                // not meant to be used as a singleton.
+                obj = theClass.newInstance();
+            } catch (SecurityException e) {
+                // The class is meant to be singleton, however, the SecurityManager restricts us from calling the
+                // getInstance method on the class, thus this is a configuration issue and a ConfigurationException
+                // is thrown
+                throw new ConfigurationException( "The SecurityManager has restricted the object factory from getting a reference to the singleton implementation" +
+                        "of the class [" + className + "]", e );
+            }
+
+			return (T)obj;		// Eclipse warning here if @SupressWarnings omitted.
+			
+            // Issue 66 - Removed System.out calls as we are throwing an exception in each of these cases
+            // anyhow.
+		} catch( IllegalArgumentException ex ) {
+			errMsg = ex.toString() + " " + typeName + " type name cannot be null or empty.";
+			throw new ConfigurationException(errMsg, ex);
+		}catch ( ClassNotFoundException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be in class path.";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( InstantiationException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be concrete.";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( IllegalAccessException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must have a public, no-arg constructor.";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( ClassCastException ex ) {
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be a subtype of T in ObjFactory<T>";
+			throw new ConfigurationException(errMsg, ex);
+		} catch( Exception ex ) {
+			// Because we are using reflection, we want to catch any checked or unchecked Exceptions and
+			// re-throw them in a way we can handle them. Because using reflection to construct the object,
+			// we can't have the compiler notify us of uncaught exceptions. For example, JavaEncryptor()
+			// CTOR can throw [well, now it can] an EncryptionException if something goes wrong. That case
+			// is taken care of here.
+			//
+			// CHECKME: Should we first catch RuntimeExceptions so we just let unchecked Exceptions go through
+			//		    unaltered???
+			//
+			errMsg = ex.toString() + " " + typeName + " class (" + className + ") CTOR threw exception.";
+			throw new ConfigurationException(errMsg, ex);
+		}
+		// DISCUSS: Should we also catch ExceptionInInitializerError here? See Google Issue #61 comments.
+	}
+	
+	/**
+	 * Not instantiable
+	 */
+	private ObjFactory() { }
+}
diff --git a/src/main/java/org/owasp/esapi/util/package.html b/src/main/java/org/owasp/esapi/util/package.html
new file mode 100644
index 0000000..2be7784
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/util/package.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body bgcolor="white">
+This package contains ESAPI utility classes used throughout the
+reference implementation of ESAPI but may also be directly useful.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/org/owasp/esapi/waf/.svn/all-wcprops b/src/main/java/org/owasp/esapi/waf/.svn/all-wcprops
new file mode 100644
index 0000000..a30a59a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf
+END
+ConfigurationException.java
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/ConfigurationException.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/package.html
+END
+ESAPIWebApplicationFirewallFilter.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java
+END
diff --git a/src/main/java/org/owasp/esapi/waf/.svn/entries b/src/main/java/org/owasp/esapi/waf/.svn/entries
new file mode 100644
index 0000000..61edc4c
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/.svn/entries
@@ -0,0 +1,142 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+actions
+dir
+

+ConfigurationException.java
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+6a75c58ce8f039f89713803c1cb3fbdd
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1237
+

+configuration
+dir
+

+rules
+dir
+

+internal
+dir
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+e24656e41ea42d31699ff65bb49ea50e
+2009-11-09T05:26:38.518570Z
+763
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+466
+

+ESAPIWebApplicationFirewallFilter.java
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+ec8d742430fb40767fb67e17bf7d1dcf
+2010-01-30T02:33:20.444599Z
+1029
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15841
+

diff --git a/src/main/java/org/owasp/esapi/waf/.svn/text-base/ConfigurationException.java.svn-base b/src/main/java/org/owasp/esapi/waf/.svn/text-base/ConfigurationException.java.svn-base
new file mode 100644
index 0000000..9ea04fd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/.svn/text-base/ConfigurationException.java.svn-base
@@ -0,0 +1,40 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import nu.xom.ValidityException;
+
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+
+/**
+ * The Exception to be thrown when there is an error parsing a policy file.
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.configuration.ConfigurationParser
+ *
+ */
+public class ConfigurationException extends EnterpriseSecurityException {
+
+	public ConfigurationException(String userMsg, String logMsg) {
+		super(userMsg,logMsg);
+	}
+
+	public ConfigurationException(String userMsg, String logMsg,
+			Throwable t) {
+		super(userMsg,logMsg,t);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/.svn/text-base/ESAPIWebApplicationFirewallFilter.java.svn-base b/src/main/java/org/owasp/esapi/waf/.svn/text-base/ESAPIWebApplicationFirewallFilter.java.svn-base
new file mode 100644
index 0000000..4563c0d
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/.svn/text-base/ESAPIWebApplicationFirewallFilter.java.svn-base
@@ -0,0 +1,466 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.File;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.BlockAction;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.RedirectAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.configuration.ConfigurationParser;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+import org.owasp.esapi.waf.rules.Rule;
+
+/**
+ * This is the main class for the ESAPI Web Application Firewall (WAF). It is a standard J2EE servlet filter
+ * that, in different methods, invokes the reading of the configuration file and handles the runtime processing
+ * and enforcing of the developer-specified rules.
+ * 
+ * Ideally the filter should be configured to catch all requests (/*) in web.xml. If there are URL segments that
+ * need to be extremely fast and don't require any protection, the pattern may be modified with extreme caution.
+ *  
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class ESAPIWebApplicationFirewallFilter implements Filter {
+
+	private AppGuardianConfiguration appGuardConfig;
+
+	private static final String CONFIGURATION_FILE_PARAM = "configuration";
+	private static final String LOGGING_FILE_PARAM = "log_settings";
+	private static final String POLLING_TIME_PARAM = "polling_time";
+	
+	private static final int DEFAULT_POLLING_TIME = 30000;
+	
+	private String configurationFilename = null;
+
+    private long pollingTime;
+	
+	private long lastConfigReadTime;
+	
+	//private static final String FAUX_SESSION_COOKIE = "FAUXSC";
+	//private static final String SESSION_COOKIE_CANARY = "org.owasp.esapi.waf.canary";
+
+	private FilterConfig fc;
+	
+	private final Logger logger = ESAPI.getLogger(ESAPIWebApplicationFirewallFilter.class);
+
+	/**
+	 * This function is used in testing to dynamically alter the configuration.
+	 * @param policyFilePath The path to the policy file
+	 * @param webRootDir The root directory of the web application.
+     * @throws FileNotFoundException if the policy file cannot be located
+	 */
+	public void setConfiguration( String policyFilePath, String webRootDir ) throws FileNotFoundException {
+		try {
+			appGuardConfig = ConfigurationParser.readConfigurationFile(new FileInputStream(new File(policyFilePath)), webRootDir);
+			lastConfigReadTime = System.currentTimeMillis();
+			configurationFilename = policyFilePath;
+		} catch (ConfigurationException e ) {
+            // TODO: It would be ideal if this method through the ConfigurationException rather than catching it and
+            // writing the error to the console.
+			e.printStackTrace();
+		}
+	}
+	
+	public AppGuardianConfiguration getConfiguration() {
+		return appGuardConfig;
+	}
+	
+	
+	/**
+	 * 
+	 * This function is invoked at application startup and when the configuration file
+	 * polling period has elapsed and a change in the configuration file has been detected.
+	 * 
+	 * It's main purpose is to read the configuration file and establish the configuration
+	 * object model for use at runtime during the <code>doFilter()</code> method. 
+	 */
+	public void init(FilterConfig fc) throws ServletException {
+
+		/*
+		 * This variable is saved so that we can retrieve it later to re-invoke this function.
+		 */
+		this.fc = fc;
+		
+		logger.debug(Logger.EVENT_SUCCESS, ">> Initializing WAF" );
+		/*
+		 * Pull logging file.
+		 */
+
+        // Demoted scope to a local since this is the only place it is referenced
+        String logSettingsFilename = fc.getInitParameter(LOGGING_FILE_PARAM);
+
+		String realLogSettingsFilename = fc.getServletContext().getRealPath(logSettingsFilename);
+		
+		if ( realLogSettingsFilename == null || (! new File(realLogSettingsFilename).exists()) ) {
+			throw new ServletException("[ESAPI WAF] Could not find log file at resolved path: " + realLogSettingsFilename);
+		}
+
+		/*
+		 * Pull main configuration file.
+		 */
+
+		configurationFilename = fc.getInitParameter(CONFIGURATION_FILE_PARAM);
+
+		configurationFilename = fc.getServletContext().getRealPath(configurationFilename);
+		
+		if ( configurationFilename == null || ! new File(configurationFilename).exists() ) {
+			throw new ServletException("[ESAPI WAF] Could not find configuration file at resolved path: " + configurationFilename);
+		}
+
+		/*
+		 * Find out polling time from a parameter. If none is provided, use
+		 * the default (10 seconds).
+		 */
+		
+		String sPollingTime = fc.getInitParameter(POLLING_TIME_PARAM);
+		
+		if ( sPollingTime != null ) {
+			pollingTime = Long.parseLong(sPollingTime);
+		} else {
+			pollingTime = DEFAULT_POLLING_TIME;
+		}
+		
+		/*
+		 * Open up configuration file and populate the AppGuardian configuration object.
+		 */
+
+		try {
+
+			String webRootDir = fc.getServletContext().getRealPath("/");
+			appGuardConfig = ConfigurationParser.readConfigurationFile(new FileInputStream(configurationFilename),webRootDir);
+
+			DOMConfigurator.configure(realLogSettingsFilename);
+
+			lastConfigReadTime = System.currentTimeMillis();
+			
+		} catch (FileNotFoundException e) {
+			throw new ServletException(e);
+		} catch (ConfigurationException e) {
+			throw new ServletException(e);
+		}
+
+	}
+
+
+	/**
+	 * This is the where the main interception and rule-checking logic of the WAF resides.
+	 */
+	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+			FilterChain chain) throws IOException, ServletException {
+
+		/*
+		 * Check to see if polling time has elapsed. If it has, that means
+		 * we should check to see if the config file has been changed. If
+		 * it has, then re-read it.
+		 *
+		 * // TODO: Any reason this logic shouldn't be moved into a polling thread class?
+		 */
+		
+		if ( (System.currentTimeMillis() - lastConfigReadTime) > pollingTime ) {
+			File f = new File(configurationFilename);
+			long lastModified = f.lastModified();
+			if ( lastModified > lastConfigReadTime ) {
+				/*
+				 * The file has been altered since it was
+				 * read in the last time. Must re-read it.
+				 */
+				logger.debug(Logger.EVENT_SUCCESS, ">> Re-reading WAF policy");
+				init(fc);
+			}
+		}
+		
+		logger.debug(Logger.EVENT_SUCCESS, ">>In WAF doFilter");
+
+		HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
+		HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
+
+		InterceptingHTTPServletRequest request = null;
+		InterceptingHTTPServletResponse response = null;
+
+		/*
+		 * First thing to do is create the InterceptingHTTPServletResponse, since
+		 * we'll need that possibly before the InterceptingHTTPServletRequest.
+		 *
+		 * The normal HttpRequest-type objects will suffice us until we get to
+		 * stage 2.
+		 *
+		 * 1st argument = the response to base the instance on
+		 * 2nd argument = should we bother intercepting the egress response?
+		 * 3rd argument = cookie rules because thats where they mostly get acted on
+		 */
+		
+		if ( 	appGuardConfig.getCookieRules().size() + 
+				appGuardConfig.getBeforeResponseRules().size() > 0) {
+			response = new InterceptingHTTPServletResponse(httpResponse, true, appGuardConfig.getCookieRules());
+		}
+		
+		/*
+		 * Stage 1: Rules that do not need the request body.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Starting stage 1" );
+
+		List<Rule> rules = this.appGuardConfig.getBeforeBodyRules();
+
+		for(int i=0;i<rules.size();i++) {
+
+			Rule rule = rules.get(i);
+			logger.debug(Logger.EVENT_SUCCESS,  "  Applying BEFORE rule:  " + rule.getClass().getName() );
+			
+			/*
+			 * The rules execute in check(). The check() method will also log. All we have
+			 * to do is decide what other actions to take.
+			 */
+			Action action = rule.check(httpRequest, response, httpResponse);
+
+			if ( action.isActionNecessary() ) {
+
+				if ( action instanceof BlockAction ) {
+					if ( response != null ) {
+						response.setStatus(appGuardConfig.getDefaultResponseCode());
+					} else {
+						httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+					}
+					return;
+
+				} else if ( action instanceof RedirectAction ) {
+					sendRedirect(response, httpResponse, ((RedirectAction)action).getRedirectURL()); 
+					return;
+
+				} else if ( action instanceof DefaultAction ) {
+
+					switch ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION) {
+						case AppGuardianConfiguration.BLOCK:
+							if ( response != null ) {
+								response.setStatus(appGuardConfig.getDefaultResponseCode());
+							} else {
+								httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+							}
+							return;
+							
+						case AppGuardianConfiguration.REDIRECT:
+							sendRedirect(response, httpResponse);
+							return;
+					}
+				}
+			}
+		}
+
+		/*
+		 * Create the InterceptingHTTPServletRequest.
+		 */
+		
+		try {
+			request = new InterceptingHTTPServletRequest((HttpServletRequest)servletRequest);
+		} catch (FileUploadException fue) {
+			logger.error(Logger.EVENT_SUCCESS,  "Error Wrapping Request", fue );
+		}
+
+		/*
+		 * Stage 2: After the body has been read, but before the the application has gotten it.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Starting Stage 2" );
+
+		rules = this.appGuardConfig.getAfterBodyRules();
+
+		for(int i=0;i<rules.size();i++) {
+
+			Rule rule = rules.get(i);
+			logger.debug(Logger.EVENT_SUCCESS,  "  Applying BEFORE CHAIN rule:  " + rule.getClass().getName() );
+
+			/*
+			 * The rules execute in check(). The check() method will take care of logging. 
+			 * All we have to do is decide what other actions to take.
+			 */
+			Action action = rule.check(request, response, httpResponse);
+
+			if ( action.isActionNecessary() ) {
+
+				if ( action instanceof BlockAction ) {
+					if ( response != null ) {
+						response.setStatus(appGuardConfig.getDefaultResponseCode());
+					} else {
+						httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+					}
+					return;
+
+				} else if ( action instanceof RedirectAction ) {
+					sendRedirect(response, httpResponse, ((RedirectAction)action).getRedirectURL());
+					return;
+
+				} else if ( action instanceof DefaultAction ) {
+
+					switch ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION) {
+						case AppGuardianConfiguration.BLOCK:
+							if ( response != null ) {
+								response.setStatus(appGuardConfig.getDefaultResponseCode());
+							} else {
+								httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+							}
+							return;
+
+						case AppGuardianConfiguration.REDIRECT:
+							sendRedirect(response, httpResponse);
+							return;
+					}
+				}
+			}
+		}
+
+		/*
+		 * In between stages 2 and 3 is the application's processing of the input.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Calling the FilterChain: " + chain );
+		chain.doFilter(request, response != null ? response : httpResponse);
+
+		/*
+		 * Stage 3: Before the response has been sent back to the user.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Starting Stage 3" );
+
+		rules = this.appGuardConfig.getBeforeResponseRules();
+
+		for(int i=0;i<rules.size();i++) {
+
+			Rule rule = rules.get(i);
+			logger.debug(Logger.EVENT_SUCCESS,  "  Applying AFTER CHAIN rule:  " + rule.getClass().getName() );
+
+			/*
+			 * The rules execute in check(). The check() method will also log. All we have
+			 * to do is decide what other actions to take.
+			 */
+			Action action = rule.check(request, response, httpResponse);
+
+			if ( action.isActionNecessary() ) {
+
+				if ( action instanceof BlockAction ) {
+					if ( response != null ) {
+						response.setStatus(appGuardConfig.getDefaultResponseCode());
+					} else {
+						httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+					}
+					return;
+
+				} else if ( action instanceof RedirectAction ) {
+					sendRedirect(response, httpResponse, ((RedirectAction)action).getRedirectURL());
+					return;
+
+				} else if ( action instanceof DefaultAction ) {
+
+					switch ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION) {
+						case AppGuardianConfiguration.BLOCK:
+							if ( response != null ) {
+								response.setStatus(appGuardConfig.getDefaultResponseCode());
+							} else {
+								httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+							}
+							return;
+
+						case AppGuardianConfiguration.REDIRECT:
+							sendRedirect(response, httpResponse);
+							return;
+					}
+				}
+			}
+		}
+
+		/*
+		 * Now that we've run our last set of rules we can allow the response to go through if
+		 * we were intercepting.
+		 */
+		
+		if ( response != null ) {
+			logger.debug(Logger.EVENT_SUCCESS, ">>> committing reponse" );
+			response.commit();
+		}
+	}
+
+	/*
+	 * Utility method to send HTTP redirects that automatically determines which response class to use.
+	 */
+	private void sendRedirect(InterceptingHTTPServletResponse response,
+			HttpServletResponse httpResponse, String redirectURL) throws IOException {
+		
+		if ( response != null ) { // if we've been buffering everything we clean it all out before sending back.
+			response.reset();
+			response.resetBuffer();
+			response.sendRedirect(redirectURL);
+			response.commit();
+		} else {
+			httpResponse.sendRedirect(redirectURL);
+		}
+		
+	}
+
+	public void destroy() {
+		/*
+		 * Any cleanup necessary?
+		 */
+	}
+
+	
+	private void sendRedirect(InterceptingHTTPServletResponse response, HttpServletResponse httpResponse) throws IOException {
+        /* [chrisisbeef] - commented out as this is not currently used. Minor performance tweak.
+		String finalJavaScript = AppGuardianConfiguration.JAVASCRIPT_REDIRECT;
+		finalJavaScript = finalJavaScript.replaceAll(AppGuardianConfiguration.JAVASCRIPT_TARGET_TOKEN, appGuardConfig.getDefaultErrorPage());
+        */
+
+		if ( response != null ) {
+			response.reset();
+			response.resetBuffer();
+			/*
+			response.setStatus(appGuardConfig.getDefaultResponseCode());
+			response.getOutputStream().write(finalJavaScript.getBytes());
+			*/
+			response.sendRedirect(appGuardConfig.getDefaultErrorPage());
+			
+		} else {
+			if ( ! httpResponse.isCommitted() ) {
+				httpResponse.sendRedirect(appGuardConfig.getDefaultErrorPage());
+			} else {
+				/*
+				 * Can't send redirect because response is already committed. I'm not sure 
+				 * how this could happen, but I didn't want to cause IOExceptions in case
+				 * if it ever does. 
+				 */
+			}
+			
+		}
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/waf/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/waf/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..bf23005
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/.svn/text-base/package.html.svn-base
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains the ESAPI Web Application Firewall (WAF). It is an optional feature of ESAPI
+that can be used with or without ESAPI's other security controls in place. It's purpose is to provide
+fast virtual patching capabilities against known vulnerabilities or the enforcement of existing
+security policies where possible.
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/ConfigurationException.java b/src/main/java/org/owasp/esapi/waf/ConfigurationException.java
new file mode 100644
index 0000000..9ea04fd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/ConfigurationException.java
@@ -0,0 +1,40 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import nu.xom.ValidityException;
+
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+
+/**
+ * The Exception to be thrown when there is an error parsing a policy file.
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.configuration.ConfigurationParser
+ *
+ */
+public class ConfigurationException extends EnterpriseSecurityException {
+
+	public ConfigurationException(String userMsg, String logMsg) {
+		super(userMsg,logMsg);
+	}
+
+	public ConfigurationException(String userMsg, String logMsg,
+			Throwable t) {
+		super(userMsg,logMsg,t);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java b/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java
new file mode 100644
index 0000000..4563c0d
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java
@@ -0,0 +1,466 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.File;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.BlockAction;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.RedirectAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.configuration.ConfigurationParser;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+import org.owasp.esapi.waf.rules.Rule;
+
+/**
+ * This is the main class for the ESAPI Web Application Firewall (WAF). It is a standard J2EE servlet filter
+ * that, in different methods, invokes the reading of the configuration file and handles the runtime processing
+ * and enforcing of the developer-specified rules.
+ * 
+ * Ideally the filter should be configured to catch all requests (/*) in web.xml. If there are URL segments that
+ * need to be extremely fast and don't require any protection, the pattern may be modified with extreme caution.
+ *  
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class ESAPIWebApplicationFirewallFilter implements Filter {
+
+	private AppGuardianConfiguration appGuardConfig;
+
+	private static final String CONFIGURATION_FILE_PARAM = "configuration";
+	private static final String LOGGING_FILE_PARAM = "log_settings";
+	private static final String POLLING_TIME_PARAM = "polling_time";
+	
+	private static final int DEFAULT_POLLING_TIME = 30000;
+	
+	private String configurationFilename = null;
+
+    private long pollingTime;
+	
+	private long lastConfigReadTime;
+	
+	//private static final String FAUX_SESSION_COOKIE = "FAUXSC";
+	//private static final String SESSION_COOKIE_CANARY = "org.owasp.esapi.waf.canary";
+
+	private FilterConfig fc;
+	
+	private final Logger logger = ESAPI.getLogger(ESAPIWebApplicationFirewallFilter.class);
+
+	/**
+	 * This function is used in testing to dynamically alter the configuration.
+	 * @param policyFilePath The path to the policy file
+	 * @param webRootDir The root directory of the web application.
+     * @throws FileNotFoundException if the policy file cannot be located
+	 */
+	public void setConfiguration( String policyFilePath, String webRootDir ) throws FileNotFoundException {
+		try {
+			appGuardConfig = ConfigurationParser.readConfigurationFile(new FileInputStream(new File(policyFilePath)), webRootDir);
+			lastConfigReadTime = System.currentTimeMillis();
+			configurationFilename = policyFilePath;
+		} catch (ConfigurationException e ) {
+            // TODO: It would be ideal if this method through the ConfigurationException rather than catching it and
+            // writing the error to the console.
+			e.printStackTrace();
+		}
+	}
+	
+	public AppGuardianConfiguration getConfiguration() {
+		return appGuardConfig;
+	}
+	
+	
+	/**
+	 * 
+	 * This function is invoked at application startup and when the configuration file
+	 * polling period has elapsed and a change in the configuration file has been detected.
+	 * 
+	 * It's main purpose is to read the configuration file and establish the configuration
+	 * object model for use at runtime during the <code>doFilter()</code> method. 
+	 */
+	public void init(FilterConfig fc) throws ServletException {
+
+		/*
+		 * This variable is saved so that we can retrieve it later to re-invoke this function.
+		 */
+		this.fc = fc;
+		
+		logger.debug(Logger.EVENT_SUCCESS, ">> Initializing WAF" );
+		/*
+		 * Pull logging file.
+		 */
+
+        // Demoted scope to a local since this is the only place it is referenced
+        String logSettingsFilename = fc.getInitParameter(LOGGING_FILE_PARAM);
+
+		String realLogSettingsFilename = fc.getServletContext().getRealPath(logSettingsFilename);
+		
+		if ( realLogSettingsFilename == null || (! new File(realLogSettingsFilename).exists()) ) {
+			throw new ServletException("[ESAPI WAF] Could not find log file at resolved path: " + realLogSettingsFilename);
+		}
+
+		/*
+		 * Pull main configuration file.
+		 */
+
+		configurationFilename = fc.getInitParameter(CONFIGURATION_FILE_PARAM);
+
+		configurationFilename = fc.getServletContext().getRealPath(configurationFilename);
+		
+		if ( configurationFilename == null || ! new File(configurationFilename).exists() ) {
+			throw new ServletException("[ESAPI WAF] Could not find configuration file at resolved path: " + configurationFilename);
+		}
+
+		/*
+		 * Find out polling time from a parameter. If none is provided, use
+		 * the default (10 seconds).
+		 */
+		
+		String sPollingTime = fc.getInitParameter(POLLING_TIME_PARAM);
+		
+		if ( sPollingTime != null ) {
+			pollingTime = Long.parseLong(sPollingTime);
+		} else {
+			pollingTime = DEFAULT_POLLING_TIME;
+		}
+		
+		/*
+		 * Open up configuration file and populate the AppGuardian configuration object.
+		 */
+
+		try {
+
+			String webRootDir = fc.getServletContext().getRealPath("/");
+			appGuardConfig = ConfigurationParser.readConfigurationFile(new FileInputStream(configurationFilename),webRootDir);
+
+			DOMConfigurator.configure(realLogSettingsFilename);
+
+			lastConfigReadTime = System.currentTimeMillis();
+			
+		} catch (FileNotFoundException e) {
+			throw new ServletException(e);
+		} catch (ConfigurationException e) {
+			throw new ServletException(e);
+		}
+
+	}
+
+
+	/**
+	 * This is the where the main interception and rule-checking logic of the WAF resides.
+	 */
+	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+			FilterChain chain) throws IOException, ServletException {
+
+		/*
+		 * Check to see if polling time has elapsed. If it has, that means
+		 * we should check to see if the config file has been changed. If
+		 * it has, then re-read it.
+		 *
+		 * // TODO: Any reason this logic shouldn't be moved into a polling thread class?
+		 */
+		
+		if ( (System.currentTimeMillis() - lastConfigReadTime) > pollingTime ) {
+			File f = new File(configurationFilename);
+			long lastModified = f.lastModified();
+			if ( lastModified > lastConfigReadTime ) {
+				/*
+				 * The file has been altered since it was
+				 * read in the last time. Must re-read it.
+				 */
+				logger.debug(Logger.EVENT_SUCCESS, ">> Re-reading WAF policy");
+				init(fc);
+			}
+		}
+		
+		logger.debug(Logger.EVENT_SUCCESS, ">>In WAF doFilter");
+
+		HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
+		HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
+
+		InterceptingHTTPServletRequest request = null;
+		InterceptingHTTPServletResponse response = null;
+
+		/*
+		 * First thing to do is create the InterceptingHTTPServletResponse, since
+		 * we'll need that possibly before the InterceptingHTTPServletRequest.
+		 *
+		 * The normal HttpRequest-type objects will suffice us until we get to
+		 * stage 2.
+		 *
+		 * 1st argument = the response to base the instance on
+		 * 2nd argument = should we bother intercepting the egress response?
+		 * 3rd argument = cookie rules because thats where they mostly get acted on
+		 */
+		
+		if ( 	appGuardConfig.getCookieRules().size() + 
+				appGuardConfig.getBeforeResponseRules().size() > 0) {
+			response = new InterceptingHTTPServletResponse(httpResponse, true, appGuardConfig.getCookieRules());
+		}
+		
+		/*
+		 * Stage 1: Rules that do not need the request body.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Starting stage 1" );
+
+		List<Rule> rules = this.appGuardConfig.getBeforeBodyRules();
+
+		for(int i=0;i<rules.size();i++) {
+
+			Rule rule = rules.get(i);
+			logger.debug(Logger.EVENT_SUCCESS,  "  Applying BEFORE rule:  " + rule.getClass().getName() );
+			
+			/*
+			 * The rules execute in check(). The check() method will also log. All we have
+			 * to do is decide what other actions to take.
+			 */
+			Action action = rule.check(httpRequest, response, httpResponse);
+
+			if ( action.isActionNecessary() ) {
+
+				if ( action instanceof BlockAction ) {
+					if ( response != null ) {
+						response.setStatus(appGuardConfig.getDefaultResponseCode());
+					} else {
+						httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+					}
+					return;
+
+				} else if ( action instanceof RedirectAction ) {
+					sendRedirect(response, httpResponse, ((RedirectAction)action).getRedirectURL()); 
+					return;
+
+				} else if ( action instanceof DefaultAction ) {
+
+					switch ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION) {
+						case AppGuardianConfiguration.BLOCK:
+							if ( response != null ) {
+								response.setStatus(appGuardConfig.getDefaultResponseCode());
+							} else {
+								httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+							}
+							return;
+							
+						case AppGuardianConfiguration.REDIRECT:
+							sendRedirect(response, httpResponse);
+							return;
+					}
+				}
+			}
+		}
+
+		/*
+		 * Create the InterceptingHTTPServletRequest.
+		 */
+		
+		try {
+			request = new InterceptingHTTPServletRequest((HttpServletRequest)servletRequest);
+		} catch (FileUploadException fue) {
+			logger.error(Logger.EVENT_SUCCESS,  "Error Wrapping Request", fue );
+		}
+
+		/*
+		 * Stage 2: After the body has been read, but before the the application has gotten it.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Starting Stage 2" );
+
+		rules = this.appGuardConfig.getAfterBodyRules();
+
+		for(int i=0;i<rules.size();i++) {
+
+			Rule rule = rules.get(i);
+			logger.debug(Logger.EVENT_SUCCESS,  "  Applying BEFORE CHAIN rule:  " + rule.getClass().getName() );
+
+			/*
+			 * The rules execute in check(). The check() method will take care of logging. 
+			 * All we have to do is decide what other actions to take.
+			 */
+			Action action = rule.check(request, response, httpResponse);
+
+			if ( action.isActionNecessary() ) {
+
+				if ( action instanceof BlockAction ) {
+					if ( response != null ) {
+						response.setStatus(appGuardConfig.getDefaultResponseCode());
+					} else {
+						httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+					}
+					return;
+
+				} else if ( action instanceof RedirectAction ) {
+					sendRedirect(response, httpResponse, ((RedirectAction)action).getRedirectURL());
+					return;
+
+				} else if ( action instanceof DefaultAction ) {
+
+					switch ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION) {
+						case AppGuardianConfiguration.BLOCK:
+							if ( response != null ) {
+								response.setStatus(appGuardConfig.getDefaultResponseCode());
+							} else {
+								httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+							}
+							return;
+
+						case AppGuardianConfiguration.REDIRECT:
+							sendRedirect(response, httpResponse);
+							return;
+					}
+				}
+			}
+		}
+
+		/*
+		 * In between stages 2 and 3 is the application's processing of the input.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Calling the FilterChain: " + chain );
+		chain.doFilter(request, response != null ? response : httpResponse);
+
+		/*
+		 * Stage 3: Before the response has been sent back to the user.
+		 */
+		logger.debug(Logger.EVENT_SUCCESS, ">> Starting Stage 3" );
+
+		rules = this.appGuardConfig.getBeforeResponseRules();
+
+		for(int i=0;i<rules.size();i++) {
+
+			Rule rule = rules.get(i);
+			logger.debug(Logger.EVENT_SUCCESS,  "  Applying AFTER CHAIN rule:  " + rule.getClass().getName() );
+
+			/*
+			 * The rules execute in check(). The check() method will also log. All we have
+			 * to do is decide what other actions to take.
+			 */
+			Action action = rule.check(request, response, httpResponse);
+
+			if ( action.isActionNecessary() ) {
+
+				if ( action instanceof BlockAction ) {
+					if ( response != null ) {
+						response.setStatus(appGuardConfig.getDefaultResponseCode());
+					} else {
+						httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+					}
+					return;
+
+				} else if ( action instanceof RedirectAction ) {
+					sendRedirect(response, httpResponse, ((RedirectAction)action).getRedirectURL());
+					return;
+
+				} else if ( action instanceof DefaultAction ) {
+
+					switch ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION) {
+						case AppGuardianConfiguration.BLOCK:
+							if ( response != null ) {
+								response.setStatus(appGuardConfig.getDefaultResponseCode());
+							} else {
+								httpResponse.setStatus(appGuardConfig.getDefaultResponseCode());
+							}
+							return;
+
+						case AppGuardianConfiguration.REDIRECT:
+							sendRedirect(response, httpResponse);
+							return;
+					}
+				}
+			}
+		}
+
+		/*
+		 * Now that we've run our last set of rules we can allow the response to go through if
+		 * we were intercepting.
+		 */
+		
+		if ( response != null ) {
+			logger.debug(Logger.EVENT_SUCCESS, ">>> committing reponse" );
+			response.commit();
+		}
+	}
+
+	/*
+	 * Utility method to send HTTP redirects that automatically determines which response class to use.
+	 */
+	private void sendRedirect(InterceptingHTTPServletResponse response,
+			HttpServletResponse httpResponse, String redirectURL) throws IOException {
+		
+		if ( response != null ) { // if we've been buffering everything we clean it all out before sending back.
+			response.reset();
+			response.resetBuffer();
+			response.sendRedirect(redirectURL);
+			response.commit();
+		} else {
+			httpResponse.sendRedirect(redirectURL);
+		}
+		
+	}
+
+	public void destroy() {
+		/*
+		 * Any cleanup necessary?
+		 */
+	}
+
+	
+	private void sendRedirect(InterceptingHTTPServletResponse response, HttpServletResponse httpResponse) throws IOException {
+        /* [chrisisbeef] - commented out as this is not currently used. Minor performance tweak.
+		String finalJavaScript = AppGuardianConfiguration.JAVASCRIPT_REDIRECT;
+		finalJavaScript = finalJavaScript.replaceAll(AppGuardianConfiguration.JAVASCRIPT_TARGET_TOKEN, appGuardConfig.getDefaultErrorPage());
+        */
+
+		if ( response != null ) {
+			response.reset();
+			response.resetBuffer();
+			/*
+			response.setStatus(appGuardConfig.getDefaultResponseCode());
+			response.getOutputStream().write(finalJavaScript.getBytes());
+			*/
+			response.sendRedirect(appGuardConfig.getDefaultErrorPage());
+			
+		} else {
+			if ( ! httpResponse.isCommitted() ) {
+				httpResponse.sendRedirect(appGuardConfig.getDefaultErrorPage());
+			} else {
+				/*
+				 * Can't send redirect because response is already committed. I'm not sure 
+				 * how this could happen, but I didn't want to cause IOExceptions in case
+				 * if it ever does. 
+				 */
+			}
+			
+		}
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/all-wcprops b/src/main/java/org/owasp/esapi/waf/actions/.svn/all-wcprops
new file mode 100644
index 0000000..de87f97
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions
+END
+DefaultAction.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions/DefaultAction.java
+END
+DoNothingAction.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions/DoNothingAction.java
+END
+BlockAction.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions/BlockAction.java
+END
+Action.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions/Action.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions/package.html
+END
+RedirectAction.java
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions/RedirectAction.java
+END
diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/entries b/src/main/java/org/owasp/esapi/waf/actions/.svn/entries
new file mode 100644
index 0000000..09a5bfe
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/entries
@@ -0,0 +1,232 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/actions
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-11-20T00:44:14.970279Z
+833
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+DoNothingAction.java
+file
+
+
+
+
+2014-02-18T16:19:52.597959Z
+6ed14275a3851c6443e239b5c199529d
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+953
+

+BlockAction.java
+file
+
+
+
+
+2014-02-18T16:19:52.597959Z
+43bfbb39f3a87f570eab16292448fa34
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+994
+

+Action.java
+file
+
+
+
+
+2014-02-18T16:19:52.597959Z
+6da0d134efcae50c2f1af34e70e3f861
+2009-11-20T00:44:14.970279Z
+833
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1219
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.597959Z
+697a24f0bc6c99766ab8c4892e1e3056
+2009-11-09T05:26:38.518570Z
+763
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+224
+

+RedirectAction.java
+file
+
+
+
+
+2014-02-18T16:19:52.597959Z
+1e77fc495d59d7ad8c6506b238fc24c1
+2009-11-20T00:33:25.026048Z
+832
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1081
+

+DefaultAction.java
+file
+
+
+
+
+2014-02-18T16:19:52.597959Z
+f7ff0b46f8573f2ffe15538808daec46
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+979
+

diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/Action.java.svn-base b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/Action.java.svn-base
new file mode 100644
index 0000000..87c3905
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/Action.java.svn-base
@@ -0,0 +1,45 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The base class indicating what is to be done after a rule executes.
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.rules.Rule
+ */
+public abstract class Action {
+
+	protected boolean failed = true;
+	protected boolean actionNecessary = true;
+
+	public void setFailed(boolean didFail) {
+		failed = didFail;
+	}
+
+	public boolean failedRule() {
+		return failed;
+	}
+
+	public boolean isActionNecessary() {
+		return actionNecessary;
+	}
+
+	public void setActionNecessary(boolean b) {
+		this.actionNecessary = b;
+
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/BlockAction.java.svn-base b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/BlockAction.java.svn-base
new file mode 100644
index 0000000..f2ce570
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/BlockAction.java.svn-base
@@ -0,0 +1,35 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates the request processing should be halted and that a blank response
+ * should be returned.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class BlockAction extends Action {
+
+	public boolean failedRule() {
+		return true;
+	}
+
+
+	public boolean isActionNecessary() {
+		return true;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/DefaultAction.java.svn-base b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/DefaultAction.java.svn-base
new file mode 100644
index 0000000..c091564
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/DefaultAction.java.svn-base
@@ -0,0 +1,34 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates the default action as indicated by the policy file
+ * should be executed.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class DefaultAction extends Action {
+
+	public boolean failedRule() {
+		return true;
+	}
+
+	public boolean isActionNecessary() {
+		return true;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/DoNothingAction.java.svn-base b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/DoNothingAction.java.svn-base
new file mode 100644
index 0000000..25dd053
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/DoNothingAction.java.svn-base
@@ -0,0 +1,34 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates that no further action is necessary.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class DoNothingAction extends Action {
+
+	public boolean failedRule() {
+		return this.failed;
+	}
+
+
+	public boolean isActionNecessary() {
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/RedirectAction.java.svn-base b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/RedirectAction.java.svn-base
new file mode 100644
index 0000000..8687510
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/RedirectAction.java.svn-base
@@ -0,0 +1,39 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates the user should be redirected to another location.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class RedirectAction extends Action {
+
+	private String url = null;
+
+	/*
+	 * Setting this overrides the default value read in the config file.
+	 */
+	public void setRedirectURL(String s) {
+		this.url = s;
+	}
+
+	public String getRedirectURL() {
+		return this.url;
+	}
+
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..94adcc8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/.svn/text-base/package.html.svn-base
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains the Action objects that are executed after a Rule subclass executes. 
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/actions/Action.java b/src/main/java/org/owasp/esapi/waf/actions/Action.java
new file mode 100644
index 0000000..87c3905
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/Action.java
@@ -0,0 +1,45 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The base class indicating what is to be done after a rule executes.
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.rules.Rule
+ */
+public abstract class Action {
+
+	protected boolean failed = true;
+	protected boolean actionNecessary = true;
+
+	public void setFailed(boolean didFail) {
+		failed = didFail;
+	}
+
+	public boolean failedRule() {
+		return failed;
+	}
+
+	public boolean isActionNecessary() {
+		return actionNecessary;
+	}
+
+	public void setActionNecessary(boolean b) {
+		this.actionNecessary = b;
+
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/BlockAction.java b/src/main/java/org/owasp/esapi/waf/actions/BlockAction.java
new file mode 100644
index 0000000..f2ce570
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/BlockAction.java
@@ -0,0 +1,35 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates the request processing should be halted and that a blank response
+ * should be returned.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class BlockAction extends Action {
+
+	public boolean failedRule() {
+		return true;
+	}
+
+
+	public boolean isActionNecessary() {
+		return true;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/DefaultAction.java b/src/main/java/org/owasp/esapi/waf/actions/DefaultAction.java
new file mode 100644
index 0000000..c091564
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/DefaultAction.java
@@ -0,0 +1,34 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates the default action as indicated by the policy file
+ * should be executed.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class DefaultAction extends Action {
+
+	public boolean failedRule() {
+		return true;
+	}
+
+	public boolean isActionNecessary() {
+		return true;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/DoNothingAction.java b/src/main/java/org/owasp/esapi/waf/actions/DoNothingAction.java
new file mode 100644
index 0000000..25dd053
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/DoNothingAction.java
@@ -0,0 +1,34 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates that no further action is necessary.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class DoNothingAction extends Action {
+
+	public boolean failedRule() {
+		return this.failed;
+	}
+
+
+	public boolean isActionNecessary() {
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/RedirectAction.java b/src/main/java/org/owasp/esapi/waf/actions/RedirectAction.java
new file mode 100644
index 0000000..8687510
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/RedirectAction.java
@@ -0,0 +1,39 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.actions;
+
+/**
+ * The class that indicates the user should be redirected to another location.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+public class RedirectAction extends Action {
+
+	private String url = null;
+
+	/*
+	 * Setting this overrides the default value read in the config file.
+	 */
+	public void setRedirectURL(String s) {
+		this.url = s;
+	}
+
+	public String getRedirectURL() {
+		return this.url;
+	}
+
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/actions/package.html b/src/main/java/org/owasp/esapi/waf/actions/package.html
new file mode 100644
index 0000000..94adcc8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/actions/package.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains the Action objects that are executed after a Rule subclass executes. 
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/configuration/.svn/all-wcprops b/src/main/java/org/owasp/esapi/waf/configuration/.svn/all-wcprops
new file mode 100644
index 0000000..ebc74b6
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/configuration
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/configuration/package.html
+END
+AppGuardianConfiguration.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java
+END
+ConfigurationParser.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java
+END
diff --git a/src/main/java/org/owasp/esapi/waf/configuration/.svn/entries b/src/main/java/org/owasp/esapi/waf/configuration/.svn/entries
new file mode 100644
index 0000000..99cdfcc
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/configuration
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.613959Z
+fd8425a660138a851da9c1e6dafc8b4c
+2009-11-09T05:26:38.518570Z
+763
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+277
+

+AppGuardianConfiguration.java
+file
+
+
+
+
+2014-02-18T16:19:52.613959Z
+3d689ecddbe0bf0527ecb5a48773c20e
+2010-11-16T03:55:11.826670Z
+1655
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6247
+

+ConfigurationParser.java
+file
+
+
+
+
+2014-02-18T16:19:52.613959Z
+9a7b47fe5c078401667a3d313aff2c66
+2013-08-31T22:44:12.907706Z
+1894
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22251
+

diff --git a/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/AppGuardianConfiguration.java.svn-base b/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/AppGuardianConfiguration.java.svn-base
new file mode 100644
index 0000000..94ec57b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/AppGuardianConfiguration.java.svn-base
@@ -0,0 +1,227 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.configuration;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.log4j.Level;
+import org.owasp.esapi.waf.rules.Rule;
+
+/**
+ * This class is the object model of the policy file. Also holds a number of constants
+ * used throughout the WAF.
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AppGuardianConfiguration {
+
+	/*
+	 * Fail modes (BLOCK blocks and logs the request, DONT_BLOCK simply logs)
+	 */
+	public static final int LOG = 0;
+	public static final int REDIRECT = 1;
+	public static final int BLOCK = 2;
+
+	/*
+	 * The operators.
+	 */
+	public static final int OPERATOR_EQ = 0;
+	public static final int OPERATOR_CONTAINS = 1;
+	public static final int OPERATOR_IN_LIST = 2;
+	public static final int OPERATOR_EXISTS = 3;
+
+	/*
+	 * We have static copies of the log settings so that the Rule objects
+	 * can access them, because they don't have access to the instance of
+	 * the configuration object.
+	 */
+	public static Level LOG_LEVEL = Level.INFO;	
+	public static String LOG_DIRECTORY = "/WEB-INF/logs";
+
+	/*
+	 * Logging settings.
+	 */
+	private Level logLevel = Level.INFO;
+	private String logDirectory = "/WEB-INF/logs";
+
+	/*
+	 * Default settings.
+	 */
+	public static int DEFAULT_FAIL_ACTION = LOG;
+
+	// TODO: use UTF-8
+	public static String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
+	public static String DEFAULT_CONTENT_TYPE = "text/html; charset=" + DEFAULT_CHARACTER_ENCODING;
+
+	/*
+	 * The JavaScript to redirect users to the default error page. Have
+	 * to use this because response.sendRedirect() can't have an arbitrary
+	 * response code and that is a requirement.
+	 */
+	public static final String JAVASCRIPT_TARGET_TOKEN = "##1##";
+	public static final String JAVASCRIPT_REDIRECT = "<html><body><script>document.location='" + JAVASCRIPT_TARGET_TOKEN + "';</script></body></html>";
+
+	/*
+	 * The aliases declared in the beginning of the config file.
+	 */
+	private HashMap<String,Object> aliases;
+
+	/*
+	 * Fail response settings.
+	 */
+	private String defaultErrorPage;
+	private int defaultResponseCode;
+
+	private boolean forceHttpOnlyFlagToSession = false;
+	private boolean forceSecureFlagToSession = false;
+
+	private String sessionCookieName;
+	
+	public String getSessionCookieName() {
+		return sessionCookieName;
+	}
+
+	public void setSessionCookieName(String sessionCookieName) {
+		this.sessionCookieName = sessionCookieName;
+	}
+
+	/*
+	 * The object-level rules encapsulated by the stage in which they are executed.
+	 */
+	private List<Rule> beforeBodyRules;
+	private List<Rule> afterBodyRules;
+	private List<Rule> beforeResponseRules;
+	private List<Rule> cookieRules;
+
+	public AppGuardianConfiguration() {
+		beforeBodyRules = new ArrayList<Rule>();
+		afterBodyRules = new ArrayList<Rule>();
+		beforeResponseRules = new ArrayList<Rule>();
+		cookieRules = new ArrayList<Rule>();
+
+		aliases = new HashMap<String,Object>();
+	}
+
+	/*
+	 * The following methods are all deprecated because
+	 * we use ESAPI logging structures now.
+	 */
+	@Deprecated
+	public Level getLogLevel() {
+		return logLevel;
+	}
+	
+	@Deprecated
+	public void setLogLevel(Level level) {
+		LOG_LEVEL = level;
+		this.logLevel = level;
+	}
+	
+	@Deprecated
+	public void setLogDirectory(String dir) {
+		LOG_DIRECTORY = dir;
+		this.logDirectory = dir;
+	}
+	
+	@Deprecated
+	public String getLogDirectory() {
+		return logDirectory;
+	}
+	
+	public String getDefaultErrorPage() {
+		return defaultErrorPage;
+	}
+
+	public void setDefaultErrorPage(String defaultErrorPage) {
+		this.defaultErrorPage = defaultErrorPage;
+	}
+
+	public int getDefaultResponseCode() {
+		return defaultResponseCode;
+	}
+
+	public void setDefaultResponseCode(int defaultResponseCode) {
+		this.defaultResponseCode = defaultResponseCode;
+	}
+
+	public void addAlias(String key, Object obj) {
+		aliases.put(key, obj);
+	}
+
+	public List<Rule> getBeforeBodyRules() {
+		return beforeBodyRules;
+	}
+
+	public List<Rule> getAfterBodyRules() {
+		return afterBodyRules;
+	}
+
+	public List<Rule> getBeforeResponseRules() {
+		return beforeResponseRules;
+	}
+
+	public List<Rule> getCookieRules() {
+		return cookieRules;
+	}
+
+	public void addBeforeBodyRule(Rule r) {
+		beforeBodyRules.add(r);
+	}
+
+	public void addAfterBodyRule(Rule r) {
+		afterBodyRules.add(r);
+	}
+
+	public void addBeforeResponseRule(Rule r) {
+		beforeResponseRules.add(r);
+	}
+
+	public void addCookieRule(Rule r) {
+		cookieRules.add(r);
+	}
+
+	public void setApplyHTTPOnlyFlagToSessionCookie(boolean shouldApply) {
+		forceHttpOnlyFlagToSession = shouldApply;
+	}
+
+	public void setApplySecureFlagToSessionCookie(boolean shouldApply) {
+		forceSecureFlagToSession = shouldApply;
+	}
+	
+	public boolean isUsingHttpOnlyFlagOnSessionCookie() {
+		return forceHttpOnlyFlagToSession;
+	}
+
+	public boolean isUsingSecureFlagOnSessionCookie() {
+		return forceSecureFlagToSession;
+	}
+	
+	public String toString() {
+		StringBuilder sb = new StringBuilder( "WAF Configuration\n" );
+		sb.append( "Before body rules:\n" );
+		for ( Rule rule : beforeBodyRules ) sb.append( "  " + rule.toString() + "\n" );
+		sb.append( "After body rules:\n" );
+		for ( Rule rule : afterBodyRules ) sb.append( "  " + rule.toString() + "\n" );
+		sb.append( "Before response rules:\n" );
+		for ( Rule rule : beforeResponseRules ) sb.append( "  " + rule.toString() + "\n" );
+		sb.append( "Cookie rules:\n" );
+		for ( Rule rule : cookieRules ) sb.append( "  " + rule.toString() + "\n" );
+		return sb.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/ConfigurationParser.java.svn-base b/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/ConfigurationParser.java.svn-base
new file mode 100644
index 0000000..2366fcd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/ConfigurationParser.java.svn-base
@@ -0,0 +1,627 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.configuration;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import nu.xom.Builder;
+import nu.xom.Document;
+import nu.xom.Element;
+import nu.xom.Elements;
+import nu.xom.ParsingException;
+import nu.xom.ValidityException;
+
+import org.apache.log4j.Level;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.waf.ConfigurationException;
+import org.owasp.esapi.waf.rules.AddHTTPOnlyFlagRule;
+import org.owasp.esapi.waf.rules.AddHeaderRule;
+import org.owasp.esapi.waf.rules.AddSecureFlagRule;
+import org.owasp.esapi.waf.rules.AuthenticatedRule;
+import org.owasp.esapi.waf.rules.BeanShellRule;
+import org.owasp.esapi.waf.rules.DetectOutboundContentRule;
+import org.owasp.esapi.waf.rules.EnforceHTTPSRule;
+import org.owasp.esapi.waf.rules.HTTPMethodRule;
+import org.owasp.esapi.waf.rules.IPRule;
+import org.owasp.esapi.waf.rules.MustMatchRule;
+import org.owasp.esapi.waf.rules.PathExtensionRule;
+import org.owasp.esapi.waf.rules.ReplaceContentRule;
+import org.owasp.esapi.waf.rules.RestrictContentTypeRule;
+import org.owasp.esapi.waf.rules.RestrictUserAgentRule;
+import org.owasp.esapi.waf.rules.SimpleVirtualPatchRule;
+
+import bsh.EvalError;
+
+/**
+ * 
+ * The class used to turn a policy file's contents into an object model. 
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.configuration.AppGuardianConfiguration
+ */
+public class ConfigurationParser {
+
+	private static final String REGEX = "regex";
+	private static final String DEFAULT_PATH_APPLY_ALL = ".*";
+	private static final int DEFAULT_RESPONSE_CODE = 403;
+	private static final String DEFAULT_SESSION_COOKIE;
+	
+	static {
+		String sessionIdName = null;
+		try {
+			sessionIdName = ESAPI.securityConfiguration().getHttpSessionIdName();
+		} catch (Throwable t) {
+			sessionIdName = "JSESSIONID";	// If all else fails...
+		}
+		DEFAULT_SESSION_COOKIE = sessionIdName;
+	}
+	
+	private static final String[] STAGES = {
+		"before-request-body",
+		"after-request-body",
+		"before-response"
+	};
+	
+	public static AppGuardianConfiguration readConfigurationFile(InputStream stream, String webRootDir) throws ConfigurationException {
+
+		AppGuardianConfiguration config = new AppGuardianConfiguration();
+
+		Builder parser = new Builder();
+		Document doc;
+		Element root;
+
+		try {
+
+			doc = parser.build(stream);
+			root = doc.getRootElement();
+
+			Element aliasesRoot = root.getFirstChildElement("aliases");
+			Element settingsRoot = root.getFirstChildElement("settings");
+			Element authNRoot = root.getFirstChildElement("authentication-rules");
+			Element authZRoot = root.getFirstChildElement("authorization-rules");
+			Element urlRoot = root.getFirstChildElement("url-rules");
+			Element headerRoot = root.getFirstChildElement("header-rules");
+			Element customRulesRoot = root.getFirstChildElement("custom-rules");;
+			Element virtualPatchesRoot = root.getFirstChildElement("virtual-patches");
+			Element outboundRoot = root.getFirstChildElement("outbound-rules");
+			Element beanShellRoot = root.getFirstChildElement("bean-shell-rules");
+			
+			
+			/**
+			 * Parse the 'aliases' section.
+			 */
+			if ( aliasesRoot != null ) {
+				Elements aliases = aliasesRoot.getChildElements("alias");
+	
+				for(int i=0;i<aliases.size();i++) {
+					Element e = aliases.get(i);
+					String name = e.getAttributeValue("name");
+					String type = e.getAttributeValue("type");
+					String value = e.getValue();
+					if ( REGEX.equals(type) ) {
+						config.addAlias(name, Pattern.compile(value));
+					} else {
+						config.addAlias(name, value);
+					}
+				}
+			}
+			
+			/**
+			 * Parse the 'settings' section.
+			 */
+			if ( settingsRoot == null ) {
+				throw new ConfigurationException("", "The <settings> section is required");
+			} else if ( settingsRoot != null ) {
+				
+				
+				try {
+					String sessionCookieName = settingsRoot.getFirstChildElement("session-cookie-name").getValue();
+					if ( ! "".equals(sessionCookieName) ) {
+						config.setSessionCookieName(sessionCookieName);
+					}
+				} catch (NullPointerException npe) {
+					config.setSessionCookieName(DEFAULT_SESSION_COOKIE);
+				}
+
+				String mode = settingsRoot.getFirstChildElement("mode").getValue();
+				
+				if ( "block".equals(mode.toLowerCase() ) ) {
+					AppGuardianConfiguration.DEFAULT_FAIL_ACTION = AppGuardianConfiguration.BLOCK;
+				} else if ( "redirect".equals(mode.toLowerCase() ) ){
+					AppGuardianConfiguration.DEFAULT_FAIL_ACTION = AppGuardianConfiguration.REDIRECT;
+				} else {
+					AppGuardianConfiguration.DEFAULT_FAIL_ACTION = AppGuardianConfiguration.LOG;
+				}
+	
+				Element errorHandlingRoot = settingsRoot.getFirstChildElement("error-handling");
+	
+				config.setDefaultErrorPage( errorHandlingRoot.getFirstChildElement("default-redirect-page").getValue() );
+	
+				try {
+					config.setDefaultResponseCode( Integer.parseInt(errorHandlingRoot.getFirstChildElement("block-status").getValue()) );
+				} catch (Exception e) {
+					config.setDefaultResponseCode( DEFAULT_RESPONSE_CODE );
+				}
+			}
+			
+			/*
+			 * The WAF separate logging is going to be merged in the 2.0
+			 * release, so this is deprecated.
+			 */
+			Element loggingRoot = settingsRoot.getFirstChildElement("logging");
+			if ( loggingRoot != null ) {
+				config.setLogDirectory(loggingRoot.getFirstChildElement("log-directory").getValue());
+				config.setLogLevel(Level.toLevel(loggingRoot.getFirstChildElement("log-level").getValue()));
+			}
+			
+			/**
+			 * Parse the 'authentication-rules' section if they have one.
+			 */
+			if ( authNRoot != null ) {
+				String key = authNRoot.getAttributeValue("key");
+				String path = authNRoot.getAttributeValue("path");
+				String id = authNRoot.getAttributeValue("id");
+
+				if ( path != null && key != null ) {
+					config.addBeforeBodyRule(new AuthenticatedRule(id,key,Pattern.compile(path),getExceptionsFromElement(authNRoot)));
+				} else if ( key != null ) {
+					config.addBeforeBodyRule(new AuthenticatedRule(id,key,null,getExceptionsFromElement(authNRoot)));
+				} else {
+					throw new ConfigurationException("","The <authentication-rules> rule requires a 'key' attribute");
+				}
+			}
+
+			/**
+			 * Parse 'authorization-rules' section if they have one.
+			 */
+
+			if ( authZRoot != null ) {
+
+				Elements restrictNodes = authZRoot.getChildElements("restrict-source-ip");
+
+				for(int i=0;i<restrictNodes.size();i++) {
+
+					Element restrictNodeRoot = restrictNodes.get(i);
+					String id = restrictNodeRoot.getAttributeValue("id");
+					Pattern ips = Pattern.compile(restrictNodeRoot.getAttributeValue("ip-regex"));
+					String ipHeader = restrictNodeRoot.getAttributeValue("ip-header");
+					if ( REGEX.equalsIgnoreCase(restrictNodeRoot.getAttributeValue("type")) ) {
+						config.addBeforeBodyRule( new IPRule(id, ips, Pattern.compile(restrictNodeRoot.getValue()),ipHeader));
+					} else {
+						config.addBeforeBodyRule( new IPRule(id, ips, restrictNodeRoot.getValue()) );
+					}
+
+				}
+
+				Elements mustMatchNodes = authZRoot.getChildElements("must-match");
+
+				for(int i=0;i<mustMatchNodes.size();i++) {
+
+					Element e = mustMatchNodes.get(i);
+					Pattern path = Pattern.compile(e.getAttributeValue("path"));
+					String variable = e.getAttributeValue("variable");
+					String value = e.getAttributeValue("value");
+					String operator = e.getAttributeValue("operator");
+					String id = e.getAttributeValue("id");
+					int op = AppGuardianConfiguration.OPERATOR_EQ;
+
+					if ( "exists".equalsIgnoreCase(operator)) {
+						op = AppGuardianConfiguration.OPERATOR_EXISTS;
+					} else if ( "inList".equalsIgnoreCase(operator)) {
+						op = AppGuardianConfiguration.OPERATOR_IN_LIST;
+					} else if ( "contains".equalsIgnoreCase(operator)) {
+						op = AppGuardianConfiguration.OPERATOR_CONTAINS;
+					}
+
+					config.addAfterBodyRule( new MustMatchRule(id, path,variable,op,value) );
+				}
+
+			}
+
+			/**
+			 * Parse the 'url-rules' section if they have one.
+			 */
+			if ( urlRoot != null ) {
+
+				Elements restrictExtensionNodes = urlRoot.getChildElements("restrict-extension");
+				Elements restrictMethodNodes = urlRoot.getChildElements("restrict-method");
+				Elements enforceHttpsNodes = urlRoot.getChildElements("enforce-https");
+
+				/*
+				 * Read in rules that allow an app to restrict by extension.
+				 * E.g., you may want to explicitly only allow:
+				 *  .jsp, .jpg, .gif, .css, .js, etc.
+				 *
+				 * You may also want to instead explicitly deny:
+				 * .bak, .log, .txt, etc.
+				 */
+
+				for (int i=0;i<restrictExtensionNodes.size();i++) {
+
+					Element e = restrictExtensionNodes.get(i);
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					String id = e.getAttributeValue("id");
+
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-extension rules can't have both 'allow' and 'deny'" );
+					}
+
+					if ( allow != null ) {
+
+						config.addBeforeBodyRule( new PathExtensionRule(id,Pattern.compile( ".*\\" + allow + "$"),null) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new PathExtensionRule(id, null,Pattern.compile( ".*\\" + deny + "$")) );
+
+					} else {
+						throw new ConfigurationException("", "restrict extension rule should have either a 'deny' or 'allow' attribute");
+					}
+				}
+
+				/*
+				 * Read in rules that allow the site to control
+				 * which HTTP methods are allowed to reach the
+				 * app.
+				 *
+				 * 99% of the time, you'll only need POST and
+				 * GET.
+				 */
+				for (int i=0;i<restrictMethodNodes.size();i++) {
+
+					Element e = restrictMethodNodes.get(i);
+
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					String path = e.getAttributeValue("path");
+					String id = e.getAttributeValue("id");
+
+					if ( path == null ) {
+						path = DEFAULT_PATH_APPLY_ALL;
+					}
+
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-method rule should not have both 'allow' and 'deny' values");
+					}
+
+					if ( allow != null ) {
+
+						config.addBeforeBodyRule( new HTTPMethodRule(id, Pattern.compile(allow), null, Pattern.compile(path)) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new HTTPMethodRule(id, null, Pattern.compile(deny), Pattern.compile(path)) );
+
+					} else {
+						throw new ConfigurationException("", "restrict-method rule should have either an 'allow' or 'deny' value");
+					}
+				}
+
+				for (int i=0;i<enforceHttpsNodes.size();i++) {
+
+					Element e = (Element)enforceHttpsNodes.get(i);
+					String path = e.getAttributeValue("path");
+					String action = e.getAttributeValue("action");
+					String id = e.getAttributeValue("id");
+					List<Object> exceptions = getExceptionsFromElement(e);
+
+					config.addBeforeBodyRule( new EnforceHTTPSRule(id, Pattern.compile(path), exceptions, action) );
+				}
+
+			}
+
+			if ( headerRoot != null ) {
+
+				Elements restrictContentTypes = headerRoot.getChildElements("restrict-content-type");
+				Elements restrictUserAgents = headerRoot.getChildElements("restrict-user-agent");
+
+				for(int i=0;i<restrictContentTypes.size();i++) {
+
+					Element e = restrictContentTypes.get(i);
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					String id = e.getAttributeValue("id");
+
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-content-type rule should not have both 'allow' and 'deny' values");
+					}
+
+					if ( allow != null ) {
+
+						config.addBeforeBodyRule( new RestrictContentTypeRule(id, Pattern.compile(allow), null) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new RestrictContentTypeRule(id, null, Pattern.compile(deny)) );
+
+					} else {
+						throw new ConfigurationException("", "restrict-content-type rule should have either an 'allow' or 'deny' value");
+					}
+				}
+
+				for(int i=0;i<restrictUserAgents.size();i++) {
+					Element e = restrictUserAgents.get(i);
+					String id = e.getAttributeValue("id");
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-user-agent rule should not have both 'allow' and 'deny' values");
+					}
+
+					if ( allow != null ) {
+						
+						config.addBeforeBodyRule( new RestrictUserAgentRule(id, Pattern.compile(allow), null) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new RestrictUserAgentRule(id, null, Pattern.compile(deny)) );
+
+					} else {
+						throw new ConfigurationException("", "restrict-user-agent rule should have either an 'allow' or 'deny' value");
+					}
+				}
+
+			}
+
+			if ( virtualPatchesRoot != null ) {
+				Elements virtualPatchNodes = virtualPatchesRoot.getChildElements("virtual-patch");
+				for(int i=0;i<virtualPatchNodes.size();i++) {
+					Element e = virtualPatchNodes.get(i);
+					String id = e.getAttributeValue("id");
+					String path = e.getAttributeValue("path");
+					String variable = e.getAttributeValue("variable");
+					String pattern = e.getAttributeValue("pattern");
+					String message = e.getAttributeValue("message");
+
+					config.addAfterBodyRule( new SimpleVirtualPatchRule(id, Pattern.compile(path), variable, Pattern.compile(pattern), message) );
+				}
+			}
+
+			// Haven't implemented this yet. Not sure what we want those rules to look like.
+			/*
+			if ( customRulesRoot != null ) {
+				Elements rules = customRulesRoot.getChildElements("rule");
+				
+				 // Parse the complex rules.
+				 
+			}
+			*/
+			
+			if ( outboundRoot != null ) {
+
+				/*
+				 * Parse the <add-header> rules. This could be used to add:
+				 * - X-I-DONT-WANT-TO-BE-FRAMED
+				 * - Caching prevention headers
+				 * - Custom application headers
+				 */
+
+				Elements addHeaderNodes = outboundRoot.getChildElements("add-header");
+
+				for(int i=0;i<addHeaderNodes.size();i++) {
+					Element e = addHeaderNodes.get(i);
+					String name = e.getAttributeValue("name");
+					String value = e.getAttributeValue("value");
+					String path = e.getAttributeValue("path");
+					String id = e.getAttributeValue("id");
+
+					if ( path == null ) {
+						path = DEFAULT_PATH_APPLY_ALL;
+					}
+
+					AddHeaderRule ahr = new AddHeaderRule(id, name, value, Pattern.compile(path), getExceptionsFromElement(e));
+					config.addBeforeResponseRule(ahr);
+
+				}
+
+				/*
+				 * Parse the <add-http-only-flag> rules that allow
+				 * us to add the HTTPOnly flag to cookies, both
+				 * custom and app server.
+				 */
+				Elements addHTTPOnlyFlagNodes = outboundRoot.getChildElements("add-http-only-flag");
+
+				for(int i=0;i<addHTTPOnlyFlagNodes.size();i++) {
+					Element e = addHTTPOnlyFlagNodes.get(i);
+
+					Elements cookiePatterns = e.getChildElements("cookie");
+					String id = e.getAttributeValue("id");
+					ArrayList<Pattern> patterns = new ArrayList<Pattern>();
+
+					for(int j=0;j<cookiePatterns.size();j++) {
+						Element cookie = cookiePatterns.get(j);
+						patterns.add(Pattern.compile(cookie.getAttributeValue("name")));
+					}
+
+					AddHTTPOnlyFlagRule ahfr = new AddHTTPOnlyFlagRule(id, patterns);
+					config.addCookieRule(ahfr);
+
+					if ( ahfr.doesCookieMatch(config.getSessionCookieName()) ) {
+						config.setApplyHTTPOnlyFlagToSessionCookie(true);
+					}
+				}
+
+				/*
+				 * Parse the <add-secure-flag> rules that allow
+				 * us to add the secure flag to cookies, both
+				 * custom and app server.
+				 */
+				Elements addSecureFlagNodes = outboundRoot.getChildElements("add-secure-flag");
+
+				for(int i=0;i<addSecureFlagNodes.size();i++) {
+					Element e = addSecureFlagNodes.get(i);
+					String id = e.getAttributeValue("id");
+					Elements cookiePatterns = e.getChildElements("cookie");
+					ArrayList<Pattern> patterns = new ArrayList<Pattern>();
+
+					for(int j=0;j<cookiePatterns.size();j++) {
+						Element cookie = cookiePatterns.get(j);
+						patterns.add(Pattern.compile(cookie.getAttributeValue("name")));
+					}
+
+					AddSecureFlagRule asfr = new AddSecureFlagRule(id, patterns);
+					config.addCookieRule(asfr);
+
+					if ( asfr.doesCookieMatch(config.getSessionCookieName()) ) {
+						config.setApplySecureFlagToSessionCookie(true);
+					}
+
+				}
+
+				/*
+				 * Parse dynamic-insertion nodes that allow us to dynamically
+				 * insert stuff into responses.
+				 */
+				Elements dynamicInsertionNodes = outboundRoot.getChildElements("dynamic-insertion");
+
+				for(int i=0;i<dynamicInsertionNodes.size();i++) {
+
+					Element e = dynamicInsertionNodes.get(i);
+					String pattern = e.getAttributeValue("pattern");
+					String id = e.getAttributeValue("id");
+					String contentType = e.getAttributeValue("content-type");
+					String urlPaths = e.getAttributeValue("path");
+					Element replacement = e.getFirstChildElement("replacement");
+
+					ReplaceContentRule rcr = new ReplaceContentRule(
+							id, 
+							Pattern.compile(pattern,Pattern.DOTALL), 
+							replacement.getValue(),
+							contentType != null ? Pattern.compile(contentType) : null,
+							urlPaths != null ? Pattern.compile(urlPaths) : null);
+					
+					config.addBeforeResponseRule(rcr);
+
+				}
+
+				/*
+				 * Parse detect-content nodes that allow us to simply detect data
+				 * leaving in responses.
+				 */
+				Elements detectContentNodes = outboundRoot.getChildElements("detect-content");
+
+				for(int i=0;i<detectContentNodes.size();i++) {
+
+					Element e = detectContentNodes.get(i);
+					String token = e.getAttributeValue("pattern");
+					String contentType = e.getAttributeValue("content-type");
+					String id = e.getAttributeValue("id");
+					String path = e.getAttributeValue("path");
+
+					if ( token == null ) {
+						throw new ConfigurationException("", "<detect-content> rules must contain a 'pattern' attribute");
+					} else if ( contentType == null ) {
+						throw new ConfigurationException("", "<detect-content> rules must contain a 'content-type' attribute");
+					}
+
+					DetectOutboundContentRule docr = new DetectOutboundContentRule(
+							id, 
+							Pattern.compile(contentType),
+							Pattern.compile(token,Pattern.DOTALL),
+							path != null ? Pattern.compile(path) : null);
+					
+					config.addBeforeResponseRule(docr);
+
+				}
+
+			}
+			
+			/**
+			 * Parse the 'bean-shell-rules' section.
+			 */
+			
+			if ( beanShellRoot != null ) {
+			
+				Elements beanShellRules = beanShellRoot.getChildElements("bean-shell-script");
+				
+				for (int i=0;i<beanShellRules.size(); i++) {
+
+					Element e = beanShellRules.get(i);
+					
+					String id = e.getAttributeValue("id");
+					String fileName = e.getAttributeValue("file");
+					String stage = e.getAttributeValue("stage"); //
+					String path = e.getAttributeValue("path");
+					
+					if ( id == null ) {
+						throw new ConfigurationException("", "bean shell rules all require a unique 'id' attribute");
+					}
+					
+					if ( fileName == null ) {
+						throw new ConfigurationException("", "bean shell rules all require a unique 'file' attribute that has the location of the .bsh script" );
+					}
+					
+					try {
+						
+						BeanShellRule bsr = new BeanShellRule(
+								webRootDir + fileName, 
+								id,
+								path != null ? Pattern.compile(path) : null);
+						
+						if ( STAGES[0].equals(stage) ) {
+							config.addBeforeBodyRule(bsr);
+						} else if ( STAGES[1].equals(stage)) {
+							config.addAfterBodyRule(bsr);
+						} else if ( STAGES[2].equals(stage)) {
+							config.addBeforeResponseRule(bsr);
+						} else {
+							throw new ConfigurationException("", "bean shell rules all require a 'stage' attribute when the rule should be fired (valid values are " + STAGES[0] + ", " + STAGES[1] + ", or " + STAGES[2] + ")" );
+						}
+												
+					} catch (FileNotFoundException fnfe) {
+						throw new ConfigurationException ("", "bean shell rule '" + id + "' had a source file that could not be found (" + fileName + "), web directory = " + webRootDir );
+					} catch (EvalError ee) {
+						throw new ConfigurationException ("", "bean shell rule '" + id + "' contained an error (" + ee.getErrorText() + "): " + ee.getScriptStackTrace());
+					}
+					
+				}
+			}
+
+		} catch (ValidityException e) {
+			throw new ConfigurationException("", "Problem validating WAF XML file", e);
+		} catch (ParsingException e) {
+			throw new ConfigurationException("", "Problem parsing WAF XML file", e);
+		} catch (IOException e) {
+			throw new ConfigurationException("", "I/O problem reading WAF XML file", e);
+		}
+
+		return config;
+
+	}
+
+	private static List<Object> getExceptionsFromElement(Element root) {
+		Elements exceptions = root.getChildElements("path-exception");
+		ArrayList<Object> exceptionList = new ArrayList<Object>();
+
+		for(int i=0;i<exceptions.size();i++) {
+			Element e = exceptions.get(i);
+			if ( REGEX.equalsIgnoreCase(e.getAttributeValue("type"))) {
+				exceptionList.add( Pattern.compile(e.getValue()) );
+			} else {
+				exceptionList.add( e.getValue() );
+			}
+		}
+		return exceptionList;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..ea68242
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/.svn/text-base/package.html.svn-base
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains the both the configuration object model and the 
+utility class to create that object model from an existing policy file. 
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java b/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java
new file mode 100644
index 0000000..94ec57b
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java
@@ -0,0 +1,227 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.configuration;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.log4j.Level;
+import org.owasp.esapi.waf.rules.Rule;
+
+/**
+ * This class is the object model of the policy file. Also holds a number of constants
+ * used throughout the WAF.
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AppGuardianConfiguration {
+
+	/*
+	 * Fail modes (BLOCK blocks and logs the request, DONT_BLOCK simply logs)
+	 */
+	public static final int LOG = 0;
+	public static final int REDIRECT = 1;
+	public static final int BLOCK = 2;
+
+	/*
+	 * The operators.
+	 */
+	public static final int OPERATOR_EQ = 0;
+	public static final int OPERATOR_CONTAINS = 1;
+	public static final int OPERATOR_IN_LIST = 2;
+	public static final int OPERATOR_EXISTS = 3;
+
+	/*
+	 * We have static copies of the log settings so that the Rule objects
+	 * can access them, because they don't have access to the instance of
+	 * the configuration object.
+	 */
+	public static Level LOG_LEVEL = Level.INFO;	
+	public static String LOG_DIRECTORY = "/WEB-INF/logs";
+
+	/*
+	 * Logging settings.
+	 */
+	private Level logLevel = Level.INFO;
+	private String logDirectory = "/WEB-INF/logs";
+
+	/*
+	 * Default settings.
+	 */
+	public static int DEFAULT_FAIL_ACTION = LOG;
+
+	// TODO: use UTF-8
+	public static String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
+	public static String DEFAULT_CONTENT_TYPE = "text/html; charset=" + DEFAULT_CHARACTER_ENCODING;
+
+	/*
+	 * The JavaScript to redirect users to the default error page. Have
+	 * to use this because response.sendRedirect() can't have an arbitrary
+	 * response code and that is a requirement.
+	 */
+	public static final String JAVASCRIPT_TARGET_TOKEN = "##1##";
+	public static final String JAVASCRIPT_REDIRECT = "<html><body><script>document.location='" + JAVASCRIPT_TARGET_TOKEN + "';</script></body></html>";
+
+	/*
+	 * The aliases declared in the beginning of the config file.
+	 */
+	private HashMap<String,Object> aliases;
+
+	/*
+	 * Fail response settings.
+	 */
+	private String defaultErrorPage;
+	private int defaultResponseCode;
+
+	private boolean forceHttpOnlyFlagToSession = false;
+	private boolean forceSecureFlagToSession = false;
+
+	private String sessionCookieName;
+	
+	public String getSessionCookieName() {
+		return sessionCookieName;
+	}
+
+	public void setSessionCookieName(String sessionCookieName) {
+		this.sessionCookieName = sessionCookieName;
+	}
+
+	/*
+	 * The object-level rules encapsulated by the stage in which they are executed.
+	 */
+	private List<Rule> beforeBodyRules;
+	private List<Rule> afterBodyRules;
+	private List<Rule> beforeResponseRules;
+	private List<Rule> cookieRules;
+
+	public AppGuardianConfiguration() {
+		beforeBodyRules = new ArrayList<Rule>();
+		afterBodyRules = new ArrayList<Rule>();
+		beforeResponseRules = new ArrayList<Rule>();
+		cookieRules = new ArrayList<Rule>();
+
+		aliases = new HashMap<String,Object>();
+	}
+
+	/*
+	 * The following methods are all deprecated because
+	 * we use ESAPI logging structures now.
+	 */
+	@Deprecated
+	public Level getLogLevel() {
+		return logLevel;
+	}
+	
+	@Deprecated
+	public void setLogLevel(Level level) {
+		LOG_LEVEL = level;
+		this.logLevel = level;
+	}
+	
+	@Deprecated
+	public void setLogDirectory(String dir) {
+		LOG_DIRECTORY = dir;
+		this.logDirectory = dir;
+	}
+	
+	@Deprecated
+	public String getLogDirectory() {
+		return logDirectory;
+	}
+	
+	public String getDefaultErrorPage() {
+		return defaultErrorPage;
+	}
+
+	public void setDefaultErrorPage(String defaultErrorPage) {
+		this.defaultErrorPage = defaultErrorPage;
+	}
+
+	public int getDefaultResponseCode() {
+		return defaultResponseCode;
+	}
+
+	public void setDefaultResponseCode(int defaultResponseCode) {
+		this.defaultResponseCode = defaultResponseCode;
+	}
+
+	public void addAlias(String key, Object obj) {
+		aliases.put(key, obj);
+	}
+
+	public List<Rule> getBeforeBodyRules() {
+		return beforeBodyRules;
+	}
+
+	public List<Rule> getAfterBodyRules() {
+		return afterBodyRules;
+	}
+
+	public List<Rule> getBeforeResponseRules() {
+		return beforeResponseRules;
+	}
+
+	public List<Rule> getCookieRules() {
+		return cookieRules;
+	}
+
+	public void addBeforeBodyRule(Rule r) {
+		beforeBodyRules.add(r);
+	}
+
+	public void addAfterBodyRule(Rule r) {
+		afterBodyRules.add(r);
+	}
+
+	public void addBeforeResponseRule(Rule r) {
+		beforeResponseRules.add(r);
+	}
+
+	public void addCookieRule(Rule r) {
+		cookieRules.add(r);
+	}
+
+	public void setApplyHTTPOnlyFlagToSessionCookie(boolean shouldApply) {
+		forceHttpOnlyFlagToSession = shouldApply;
+	}
+
+	public void setApplySecureFlagToSessionCookie(boolean shouldApply) {
+		forceSecureFlagToSession = shouldApply;
+	}
+	
+	public boolean isUsingHttpOnlyFlagOnSessionCookie() {
+		return forceHttpOnlyFlagToSession;
+	}
+
+	public boolean isUsingSecureFlagOnSessionCookie() {
+		return forceSecureFlagToSession;
+	}
+	
+	public String toString() {
+		StringBuilder sb = new StringBuilder( "WAF Configuration\n" );
+		sb.append( "Before body rules:\n" );
+		for ( Rule rule : beforeBodyRules ) sb.append( "  " + rule.toString() + "\n" );
+		sb.append( "After body rules:\n" );
+		for ( Rule rule : afterBodyRules ) sb.append( "  " + rule.toString() + "\n" );
+		sb.append( "Before response rules:\n" );
+		for ( Rule rule : beforeResponseRules ) sb.append( "  " + rule.toString() + "\n" );
+		sb.append( "Cookie rules:\n" );
+		for ( Rule rule : cookieRules ) sb.append( "  " + rule.toString() + "\n" );
+		return sb.toString();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java b/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java
new file mode 100644
index 0000000..2366fcd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java
@@ -0,0 +1,627 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.configuration;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import nu.xom.Builder;
+import nu.xom.Document;
+import nu.xom.Element;
+import nu.xom.Elements;
+import nu.xom.ParsingException;
+import nu.xom.ValidityException;
+
+import org.apache.log4j.Level;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.waf.ConfigurationException;
+import org.owasp.esapi.waf.rules.AddHTTPOnlyFlagRule;
+import org.owasp.esapi.waf.rules.AddHeaderRule;
+import org.owasp.esapi.waf.rules.AddSecureFlagRule;
+import org.owasp.esapi.waf.rules.AuthenticatedRule;
+import org.owasp.esapi.waf.rules.BeanShellRule;
+import org.owasp.esapi.waf.rules.DetectOutboundContentRule;
+import org.owasp.esapi.waf.rules.EnforceHTTPSRule;
+import org.owasp.esapi.waf.rules.HTTPMethodRule;
+import org.owasp.esapi.waf.rules.IPRule;
+import org.owasp.esapi.waf.rules.MustMatchRule;
+import org.owasp.esapi.waf.rules.PathExtensionRule;
+import org.owasp.esapi.waf.rules.ReplaceContentRule;
+import org.owasp.esapi.waf.rules.RestrictContentTypeRule;
+import org.owasp.esapi.waf.rules.RestrictUserAgentRule;
+import org.owasp.esapi.waf.rules.SimpleVirtualPatchRule;
+
+import bsh.EvalError;
+
+/**
+ * 
+ * The class used to turn a policy file's contents into an object model. 
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.configuration.AppGuardianConfiguration
+ */
+public class ConfigurationParser {
+
+	private static final String REGEX = "regex";
+	private static final String DEFAULT_PATH_APPLY_ALL = ".*";
+	private static final int DEFAULT_RESPONSE_CODE = 403;
+	private static final String DEFAULT_SESSION_COOKIE;
+	
+	static {
+		String sessionIdName = null;
+		try {
+			sessionIdName = ESAPI.securityConfiguration().getHttpSessionIdName();
+		} catch (Throwable t) {
+			sessionIdName = "JSESSIONID";	// If all else fails...
+		}
+		DEFAULT_SESSION_COOKIE = sessionIdName;
+	}
+	
+	private static final String[] STAGES = {
+		"before-request-body",
+		"after-request-body",
+		"before-response"
+	};
+	
+	public static AppGuardianConfiguration readConfigurationFile(InputStream stream, String webRootDir) throws ConfigurationException {
+
+		AppGuardianConfiguration config = new AppGuardianConfiguration();
+
+		Builder parser = new Builder();
+		Document doc;
+		Element root;
+
+		try {
+
+			doc = parser.build(stream);
+			root = doc.getRootElement();
+
+			Element aliasesRoot = root.getFirstChildElement("aliases");
+			Element settingsRoot = root.getFirstChildElement("settings");
+			Element authNRoot = root.getFirstChildElement("authentication-rules");
+			Element authZRoot = root.getFirstChildElement("authorization-rules");
+			Element urlRoot = root.getFirstChildElement("url-rules");
+			Element headerRoot = root.getFirstChildElement("header-rules");
+			Element customRulesRoot = root.getFirstChildElement("custom-rules");;
+			Element virtualPatchesRoot = root.getFirstChildElement("virtual-patches");
+			Element outboundRoot = root.getFirstChildElement("outbound-rules");
+			Element beanShellRoot = root.getFirstChildElement("bean-shell-rules");
+			
+			
+			/**
+			 * Parse the 'aliases' section.
+			 */
+			if ( aliasesRoot != null ) {
+				Elements aliases = aliasesRoot.getChildElements("alias");
+	
+				for(int i=0;i<aliases.size();i++) {
+					Element e = aliases.get(i);
+					String name = e.getAttributeValue("name");
+					String type = e.getAttributeValue("type");
+					String value = e.getValue();
+					if ( REGEX.equals(type) ) {
+						config.addAlias(name, Pattern.compile(value));
+					} else {
+						config.addAlias(name, value);
+					}
+				}
+			}
+			
+			/**
+			 * Parse the 'settings' section.
+			 */
+			if ( settingsRoot == null ) {
+				throw new ConfigurationException("", "The <settings> section is required");
+			} else if ( settingsRoot != null ) {
+				
+				
+				try {
+					String sessionCookieName = settingsRoot.getFirstChildElement("session-cookie-name").getValue();
+					if ( ! "".equals(sessionCookieName) ) {
+						config.setSessionCookieName(sessionCookieName);
+					}
+				} catch (NullPointerException npe) {
+					config.setSessionCookieName(DEFAULT_SESSION_COOKIE);
+				}
+
+				String mode = settingsRoot.getFirstChildElement("mode").getValue();
+				
+				if ( "block".equals(mode.toLowerCase() ) ) {
+					AppGuardianConfiguration.DEFAULT_FAIL_ACTION = AppGuardianConfiguration.BLOCK;
+				} else if ( "redirect".equals(mode.toLowerCase() ) ){
+					AppGuardianConfiguration.DEFAULT_FAIL_ACTION = AppGuardianConfiguration.REDIRECT;
+				} else {
+					AppGuardianConfiguration.DEFAULT_FAIL_ACTION = AppGuardianConfiguration.LOG;
+				}
+	
+				Element errorHandlingRoot = settingsRoot.getFirstChildElement("error-handling");
+	
+				config.setDefaultErrorPage( errorHandlingRoot.getFirstChildElement("default-redirect-page").getValue() );
+	
+				try {
+					config.setDefaultResponseCode( Integer.parseInt(errorHandlingRoot.getFirstChildElement("block-status").getValue()) );
+				} catch (Exception e) {
+					config.setDefaultResponseCode( DEFAULT_RESPONSE_CODE );
+				}
+			}
+			
+			/*
+			 * The WAF separate logging is going to be merged in the 2.0
+			 * release, so this is deprecated.
+			 */
+			Element loggingRoot = settingsRoot.getFirstChildElement("logging");
+			if ( loggingRoot != null ) {
+				config.setLogDirectory(loggingRoot.getFirstChildElement("log-directory").getValue());
+				config.setLogLevel(Level.toLevel(loggingRoot.getFirstChildElement("log-level").getValue()));
+			}
+			
+			/**
+			 * Parse the 'authentication-rules' section if they have one.
+			 */
+			if ( authNRoot != null ) {
+				String key = authNRoot.getAttributeValue("key");
+				String path = authNRoot.getAttributeValue("path");
+				String id = authNRoot.getAttributeValue("id");
+
+				if ( path != null && key != null ) {
+					config.addBeforeBodyRule(new AuthenticatedRule(id,key,Pattern.compile(path),getExceptionsFromElement(authNRoot)));
+				} else if ( key != null ) {
+					config.addBeforeBodyRule(new AuthenticatedRule(id,key,null,getExceptionsFromElement(authNRoot)));
+				} else {
+					throw new ConfigurationException("","The <authentication-rules> rule requires a 'key' attribute");
+				}
+			}
+
+			/**
+			 * Parse 'authorization-rules' section if they have one.
+			 */
+
+			if ( authZRoot != null ) {
+
+				Elements restrictNodes = authZRoot.getChildElements("restrict-source-ip");
+
+				for(int i=0;i<restrictNodes.size();i++) {
+
+					Element restrictNodeRoot = restrictNodes.get(i);
+					String id = restrictNodeRoot.getAttributeValue("id");
+					Pattern ips = Pattern.compile(restrictNodeRoot.getAttributeValue("ip-regex"));
+					String ipHeader = restrictNodeRoot.getAttributeValue("ip-header");
+					if ( REGEX.equalsIgnoreCase(restrictNodeRoot.getAttributeValue("type")) ) {
+						config.addBeforeBodyRule( new IPRule(id, ips, Pattern.compile(restrictNodeRoot.getValue()),ipHeader));
+					} else {
+						config.addBeforeBodyRule( new IPRule(id, ips, restrictNodeRoot.getValue()) );
+					}
+
+				}
+
+				Elements mustMatchNodes = authZRoot.getChildElements("must-match");
+
+				for(int i=0;i<mustMatchNodes.size();i++) {
+
+					Element e = mustMatchNodes.get(i);
+					Pattern path = Pattern.compile(e.getAttributeValue("path"));
+					String variable = e.getAttributeValue("variable");
+					String value = e.getAttributeValue("value");
+					String operator = e.getAttributeValue("operator");
+					String id = e.getAttributeValue("id");
+					int op = AppGuardianConfiguration.OPERATOR_EQ;
+
+					if ( "exists".equalsIgnoreCase(operator)) {
+						op = AppGuardianConfiguration.OPERATOR_EXISTS;
+					} else if ( "inList".equalsIgnoreCase(operator)) {
+						op = AppGuardianConfiguration.OPERATOR_IN_LIST;
+					} else if ( "contains".equalsIgnoreCase(operator)) {
+						op = AppGuardianConfiguration.OPERATOR_CONTAINS;
+					}
+
+					config.addAfterBodyRule( new MustMatchRule(id, path,variable,op,value) );
+				}
+
+			}
+
+			/**
+			 * Parse the 'url-rules' section if they have one.
+			 */
+			if ( urlRoot != null ) {
+
+				Elements restrictExtensionNodes = urlRoot.getChildElements("restrict-extension");
+				Elements restrictMethodNodes = urlRoot.getChildElements("restrict-method");
+				Elements enforceHttpsNodes = urlRoot.getChildElements("enforce-https");
+
+				/*
+				 * Read in rules that allow an app to restrict by extension.
+				 * E.g., you may want to explicitly only allow:
+				 *  .jsp, .jpg, .gif, .css, .js, etc.
+				 *
+				 * You may also want to instead explicitly deny:
+				 * .bak, .log, .txt, etc.
+				 */
+
+				for (int i=0;i<restrictExtensionNodes.size();i++) {
+
+					Element e = restrictExtensionNodes.get(i);
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					String id = e.getAttributeValue("id");
+
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-extension rules can't have both 'allow' and 'deny'" );
+					}
+
+					if ( allow != null ) {
+
+						config.addBeforeBodyRule( new PathExtensionRule(id,Pattern.compile( ".*\\" + allow + "$"),null) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new PathExtensionRule(id, null,Pattern.compile( ".*\\" + deny + "$")) );
+
+					} else {
+						throw new ConfigurationException("", "restrict extension rule should have either a 'deny' or 'allow' attribute");
+					}
+				}
+
+				/*
+				 * Read in rules that allow the site to control
+				 * which HTTP methods are allowed to reach the
+				 * app.
+				 *
+				 * 99% of the time, you'll only need POST and
+				 * GET.
+				 */
+				for (int i=0;i<restrictMethodNodes.size();i++) {
+
+					Element e = restrictMethodNodes.get(i);
+
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					String path = e.getAttributeValue("path");
+					String id = e.getAttributeValue("id");
+
+					if ( path == null ) {
+						path = DEFAULT_PATH_APPLY_ALL;
+					}
+
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-method rule should not have both 'allow' and 'deny' values");
+					}
+
+					if ( allow != null ) {
+
+						config.addBeforeBodyRule( new HTTPMethodRule(id, Pattern.compile(allow), null, Pattern.compile(path)) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new HTTPMethodRule(id, null, Pattern.compile(deny), Pattern.compile(path)) );
+
+					} else {
+						throw new ConfigurationException("", "restrict-method rule should have either an 'allow' or 'deny' value");
+					}
+				}
+
+				for (int i=0;i<enforceHttpsNodes.size();i++) {
+
+					Element e = (Element)enforceHttpsNodes.get(i);
+					String path = e.getAttributeValue("path");
+					String action = e.getAttributeValue("action");
+					String id = e.getAttributeValue("id");
+					List<Object> exceptions = getExceptionsFromElement(e);
+
+					config.addBeforeBodyRule( new EnforceHTTPSRule(id, Pattern.compile(path), exceptions, action) );
+				}
+
+			}
+
+			if ( headerRoot != null ) {
+
+				Elements restrictContentTypes = headerRoot.getChildElements("restrict-content-type");
+				Elements restrictUserAgents = headerRoot.getChildElements("restrict-user-agent");
+
+				for(int i=0;i<restrictContentTypes.size();i++) {
+
+					Element e = restrictContentTypes.get(i);
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					String id = e.getAttributeValue("id");
+
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-content-type rule should not have both 'allow' and 'deny' values");
+					}
+
+					if ( allow != null ) {
+
+						config.addBeforeBodyRule( new RestrictContentTypeRule(id, Pattern.compile(allow), null) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new RestrictContentTypeRule(id, null, Pattern.compile(deny)) );
+
+					} else {
+						throw new ConfigurationException("", "restrict-content-type rule should have either an 'allow' or 'deny' value");
+					}
+				}
+
+				for(int i=0;i<restrictUserAgents.size();i++) {
+					Element e = restrictUserAgents.get(i);
+					String id = e.getAttributeValue("id");
+					String allow = e.getAttributeValue("allow");
+					String deny = e.getAttributeValue("deny");
+					if ( allow != null && deny != null ) {
+						throw new ConfigurationException("", "restrict-user-agent rule should not have both 'allow' and 'deny' values");
+					}
+
+					if ( allow != null ) {
+						
+						config.addBeforeBodyRule( new RestrictUserAgentRule(id, Pattern.compile(allow), null) );
+
+					} else if ( deny != null ) {
+
+						config.addBeforeBodyRule( new RestrictUserAgentRule(id, null, Pattern.compile(deny)) );
+
+					} else {
+						throw new ConfigurationException("", "restrict-user-agent rule should have either an 'allow' or 'deny' value");
+					}
+				}
+
+			}
+
+			if ( virtualPatchesRoot != null ) {
+				Elements virtualPatchNodes = virtualPatchesRoot.getChildElements("virtual-patch");
+				for(int i=0;i<virtualPatchNodes.size();i++) {
+					Element e = virtualPatchNodes.get(i);
+					String id = e.getAttributeValue("id");
+					String path = e.getAttributeValue("path");
+					String variable = e.getAttributeValue("variable");
+					String pattern = e.getAttributeValue("pattern");
+					String message = e.getAttributeValue("message");
+
+					config.addAfterBodyRule( new SimpleVirtualPatchRule(id, Pattern.compile(path), variable, Pattern.compile(pattern), message) );
+				}
+			}
+
+			// Haven't implemented this yet. Not sure what we want those rules to look like.
+			/*
+			if ( customRulesRoot != null ) {
+				Elements rules = customRulesRoot.getChildElements("rule");
+				
+				 // Parse the complex rules.
+				 
+			}
+			*/
+			
+			if ( outboundRoot != null ) {
+
+				/*
+				 * Parse the <add-header> rules. This could be used to add:
+				 * - X-I-DONT-WANT-TO-BE-FRAMED
+				 * - Caching prevention headers
+				 * - Custom application headers
+				 */
+
+				Elements addHeaderNodes = outboundRoot.getChildElements("add-header");
+
+				for(int i=0;i<addHeaderNodes.size();i++) {
+					Element e = addHeaderNodes.get(i);
+					String name = e.getAttributeValue("name");
+					String value = e.getAttributeValue("value");
+					String path = e.getAttributeValue("path");
+					String id = e.getAttributeValue("id");
+
+					if ( path == null ) {
+						path = DEFAULT_PATH_APPLY_ALL;
+					}
+
+					AddHeaderRule ahr = new AddHeaderRule(id, name, value, Pattern.compile(path), getExceptionsFromElement(e));
+					config.addBeforeResponseRule(ahr);
+
+				}
+
+				/*
+				 * Parse the <add-http-only-flag> rules that allow
+				 * us to add the HTTPOnly flag to cookies, both
+				 * custom and app server.
+				 */
+				Elements addHTTPOnlyFlagNodes = outboundRoot.getChildElements("add-http-only-flag");
+
+				for(int i=0;i<addHTTPOnlyFlagNodes.size();i++) {
+					Element e = addHTTPOnlyFlagNodes.get(i);
+
+					Elements cookiePatterns = e.getChildElements("cookie");
+					String id = e.getAttributeValue("id");
+					ArrayList<Pattern> patterns = new ArrayList<Pattern>();
+
+					for(int j=0;j<cookiePatterns.size();j++) {
+						Element cookie = cookiePatterns.get(j);
+						patterns.add(Pattern.compile(cookie.getAttributeValue("name")));
+					}
+
+					AddHTTPOnlyFlagRule ahfr = new AddHTTPOnlyFlagRule(id, patterns);
+					config.addCookieRule(ahfr);
+
+					if ( ahfr.doesCookieMatch(config.getSessionCookieName()) ) {
+						config.setApplyHTTPOnlyFlagToSessionCookie(true);
+					}
+				}
+
+				/*
+				 * Parse the <add-secure-flag> rules that allow
+				 * us to add the secure flag to cookies, both
+				 * custom and app server.
+				 */
+				Elements addSecureFlagNodes = outboundRoot.getChildElements("add-secure-flag");
+
+				for(int i=0;i<addSecureFlagNodes.size();i++) {
+					Element e = addSecureFlagNodes.get(i);
+					String id = e.getAttributeValue("id");
+					Elements cookiePatterns = e.getChildElements("cookie");
+					ArrayList<Pattern> patterns = new ArrayList<Pattern>();
+
+					for(int j=0;j<cookiePatterns.size();j++) {
+						Element cookie = cookiePatterns.get(j);
+						patterns.add(Pattern.compile(cookie.getAttributeValue("name")));
+					}
+
+					AddSecureFlagRule asfr = new AddSecureFlagRule(id, patterns);
+					config.addCookieRule(asfr);
+
+					if ( asfr.doesCookieMatch(config.getSessionCookieName()) ) {
+						config.setApplySecureFlagToSessionCookie(true);
+					}
+
+				}
+
+				/*
+				 * Parse dynamic-insertion nodes that allow us to dynamically
+				 * insert stuff into responses.
+				 */
+				Elements dynamicInsertionNodes = outboundRoot.getChildElements("dynamic-insertion");
+
+				for(int i=0;i<dynamicInsertionNodes.size();i++) {
+
+					Element e = dynamicInsertionNodes.get(i);
+					String pattern = e.getAttributeValue("pattern");
+					String id = e.getAttributeValue("id");
+					String contentType = e.getAttributeValue("content-type");
+					String urlPaths = e.getAttributeValue("path");
+					Element replacement = e.getFirstChildElement("replacement");
+
+					ReplaceContentRule rcr = new ReplaceContentRule(
+							id, 
+							Pattern.compile(pattern,Pattern.DOTALL), 
+							replacement.getValue(),
+							contentType != null ? Pattern.compile(contentType) : null,
+							urlPaths != null ? Pattern.compile(urlPaths) : null);
+					
+					config.addBeforeResponseRule(rcr);
+
+				}
+
+				/*
+				 * Parse detect-content nodes that allow us to simply detect data
+				 * leaving in responses.
+				 */
+				Elements detectContentNodes = outboundRoot.getChildElements("detect-content");
+
+				for(int i=0;i<detectContentNodes.size();i++) {
+
+					Element e = detectContentNodes.get(i);
+					String token = e.getAttributeValue("pattern");
+					String contentType = e.getAttributeValue("content-type");
+					String id = e.getAttributeValue("id");
+					String path = e.getAttributeValue("path");
+
+					if ( token == null ) {
+						throw new ConfigurationException("", "<detect-content> rules must contain a 'pattern' attribute");
+					} else if ( contentType == null ) {
+						throw new ConfigurationException("", "<detect-content> rules must contain a 'content-type' attribute");
+					}
+
+					DetectOutboundContentRule docr = new DetectOutboundContentRule(
+							id, 
+							Pattern.compile(contentType),
+							Pattern.compile(token,Pattern.DOTALL),
+							path != null ? Pattern.compile(path) : null);
+					
+					config.addBeforeResponseRule(docr);
+
+				}
+
+			}
+			
+			/**
+			 * Parse the 'bean-shell-rules' section.
+			 */
+			
+			if ( beanShellRoot != null ) {
+			
+				Elements beanShellRules = beanShellRoot.getChildElements("bean-shell-script");
+				
+				for (int i=0;i<beanShellRules.size(); i++) {
+
+					Element e = beanShellRules.get(i);
+					
+					String id = e.getAttributeValue("id");
+					String fileName = e.getAttributeValue("file");
+					String stage = e.getAttributeValue("stage"); //
+					String path = e.getAttributeValue("path");
+					
+					if ( id == null ) {
+						throw new ConfigurationException("", "bean shell rules all require a unique 'id' attribute");
+					}
+					
+					if ( fileName == null ) {
+						throw new ConfigurationException("", "bean shell rules all require a unique 'file' attribute that has the location of the .bsh script" );
+					}
+					
+					try {
+						
+						BeanShellRule bsr = new BeanShellRule(
+								webRootDir + fileName, 
+								id,
+								path != null ? Pattern.compile(path) : null);
+						
+						if ( STAGES[0].equals(stage) ) {
+							config.addBeforeBodyRule(bsr);
+						} else if ( STAGES[1].equals(stage)) {
+							config.addAfterBodyRule(bsr);
+						} else if ( STAGES[2].equals(stage)) {
+							config.addBeforeResponseRule(bsr);
+						} else {
+							throw new ConfigurationException("", "bean shell rules all require a 'stage' attribute when the rule should be fired (valid values are " + STAGES[0] + ", " + STAGES[1] + ", or " + STAGES[2] + ")" );
+						}
+												
+					} catch (FileNotFoundException fnfe) {
+						throw new ConfigurationException ("", "bean shell rule '" + id + "' had a source file that could not be found (" + fileName + "), web directory = " + webRootDir );
+					} catch (EvalError ee) {
+						throw new ConfigurationException ("", "bean shell rule '" + id + "' contained an error (" + ee.getErrorText() + "): " + ee.getScriptStackTrace());
+					}
+					
+				}
+			}
+
+		} catch (ValidityException e) {
+			throw new ConfigurationException("", "Problem validating WAF XML file", e);
+		} catch (ParsingException e) {
+			throw new ConfigurationException("", "Problem parsing WAF XML file", e);
+		} catch (IOException e) {
+			throw new ConfigurationException("", "I/O problem reading WAF XML file", e);
+		}
+
+		return config;
+
+	}
+
+	private static List<Object> getExceptionsFromElement(Element root) {
+		Elements exceptions = root.getChildElements("path-exception");
+		ArrayList<Object> exceptionList = new ArrayList<Object>();
+
+		for(int i=0;i<exceptions.size();i++) {
+			Element e = exceptions.get(i);
+			if ( REGEX.equalsIgnoreCase(e.getAttributeValue("type"))) {
+				exceptionList.add( Pattern.compile(e.getValue()) );
+			} else {
+				exceptionList.add( e.getValue() );
+			}
+		}
+		return exceptionList;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/configuration/package.html b/src/main/java/org/owasp/esapi/waf/configuration/package.html
new file mode 100644
index 0000000..ea68242
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/configuration/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains the both the configuration object model and the 
+utility class to create that object model from an existing policy file. 
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/all-wcprops b/src/main/java/org/owasp/esapi/waf/internal/.svn/all-wcprops
new file mode 100644
index 0000000..5e0211a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal
+END
+Parameter.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal/Parameter.java
+END
+InterceptingPrintWriter.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal/InterceptingPrintWriter.java
+END
+InterceptingHTTPServletRequest.java
+K 25
+svn:wc:ra_dav:version-url
+V 114
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java
+END
+InterceptingServletOutputStream.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal/package.html
+END
+InterceptingHTTPServletResponse.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletResponse.java
+END
diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/entries b/src/main/java/org/owasp/esapi/waf/internal/.svn/entries
new file mode 100644
index 0000000..ddd6a39
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/entries
@@ -0,0 +1,232 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/internal
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+InterceptingServletOutputStream.java
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+f469bde9a1f6f7a1c2353c712740f9e0
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4232
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+0fa7914ff76dc282cf723411e839238a
+2009-11-09T05:26:38.518570Z
+763
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+245
+

+InterceptingHTTPServletResponse.java
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+4c4560595c3b43fbee8d93b09d2ffa34
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5605
+

+Parameter.java
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+b818e17c555979bcd575d9d4d9391ebe
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1278
+

+InterceptingPrintWriter.java
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+c3e91ebb6e3f5d5d850543922c806ae2
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3726
+

+InterceptingHTTPServletRequest.java
+file
+
+
+
+
+2014-02-18T16:19:52.657960Z
+8e4bb6e9aa411666f707165c94f0c474
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5078
+

diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHTTPServletRequest.java.svn-base b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHTTPServletRequest.java.svn-base
new file mode 100644
index 0000000..6766f74
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHTTPServletRequest.java.svn-base
@@ -0,0 +1,189 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.RandomAccessFile;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.util.Streams;
+
+/**
+ * The wrapper for the HttpServletRequest object which will be passed to the application
+ * being protected by the WAF. It contains logic for parsing multipart parameters out of
+ * the request and provided downstream application logic a way of accessing it like it 
+ * hasn't been touched.
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class InterceptingHTTPServletRequest extends HttpServletRequestWrapper {
+
+	private Vector<Parameter> allParameters;
+	private Vector<String> allParameterNames;
+	private static int CHUNKED_BUFFER_SIZE = 1024;
+	
+	private boolean isMultipart = false;
+	private RandomAccessFile requestBody;
+	private RAFInputStream is;
+	
+	public ServletInputStream getInputStream() throws IOException {
+		
+		if ( isMultipart ) {
+			return is;	
+		} else {
+			return super.getInputStream();
+		}
+        
+    }
+	
+	public BufferedReader getReader() throws IOException {
+        String enc = getCharacterEncoding();
+        if(enc == null) enc = "UTF-8";
+        return new BufferedReader(new InputStreamReader(getInputStream(), enc));
+    }
+	
+	public InterceptingHTTPServletRequest(HttpServletRequest request) throws FileUploadException, IOException {
+
+		super(request);
+
+		allParameters = new Vector<Parameter>();
+		allParameterNames = new Vector<String>();
+
+
+		/*
+		 * Get all the regular parameters.
+		 */
+
+		Enumeration e = request.getParameterNames();
+
+		while(e.hasMoreElements()) {
+			String param = (String)e.nextElement();
+			allParameters.add(new Parameter(param,request.getParameter(param),false));
+			allParameterNames.add(param);
+		}
+
+
+		/*
+		 * Get all the multipart fields.
+		 */
+
+		isMultipart = ServletFileUpload.isMultipartContent(request);
+
+		if ( isMultipart ) {
+
+			requestBody = new RandomAccessFile( File.createTempFile("oew","mpc"), "rw");
+	    	
+	    	byte buffer[] = new byte[CHUNKED_BUFFER_SIZE];
+
+	    	long size = 0;
+	    	int len = 0;
+
+	    	while ( len != -1 && size <= Integer.MAX_VALUE) {
+	    		len = request.getInputStream().read(buffer, 0, CHUNKED_BUFFER_SIZE);
+	    		if ( len != -1 ) {
+	    			size += len;
+	    			requestBody.write(buffer,0,len);	
+	    		}
+	    	}
+			
+	    	is = new RAFInputStream(requestBody);
+	    	
+			ServletFileUpload sfu = new ServletFileUpload();
+			FileItemIterator iter = sfu.getItemIterator(this);
+
+			while(iter.hasNext()) {
+				FileItemStream item = iter.next();
+				String name = item.getFieldName();
+				InputStream stream = item.openStream();
+
+				/*
+				 * If this is a regular form field, add it to our
+				 * parameter collection.
+				 */
+
+				if (item.isFormField()) {
+
+					String value = Streams.asString(stream);
+
+					allParameters.add(new Parameter(name,value,true));
+			    	allParameterNames.add(name);
+
+			    } else {
+			    	/*
+			    	 * This is a multipart content that is not a
+			    	 * regular form field. Nothing to do here.
+			    	 */
+			    	
+			    }
+
+			}
+			
+			requestBody.seek(0);
+			
+		}
+
+	}
+
+	public String getDictionaryParameter(String s) {
+
+		for(int i=0;i<allParameters.size();i++) {
+			Parameter p = allParameters.get(i);
+			if ( p.getName().equals(s) ) {
+				return p.getValue();
+			}
+		}
+		
+		return null;
+	}
+
+	public Enumeration getDictionaryParameterNames() {
+		return allParameterNames.elements();
+	}
+	
+	
+	private class RAFInputStream extends ServletInputStream {
+		
+		RandomAccessFile raf;
+		
+		public RAFInputStream(RandomAccessFile raf) throws IOException {
+			this.raf = raf;
+			this.raf.seek(0);
+		}
+
+		public int read() throws IOException {
+			return raf.read();
+		}
+		
+		public synchronized void reset() throws IOException {
+			raf.seek(0);
+		}
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHTTPServletResponse.java.svn-base b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHTTPServletResponse.java.svn-base
new file mode 100644
index 0000000..2cd41a3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHTTPServletResponse.java.svn-base
@@ -0,0 +1,186 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.owasp.esapi.waf.rules.AddHTTPOnlyFlagRule;
+import org.owasp.esapi.waf.rules.AddSecureFlagRule;
+import org.owasp.esapi.waf.rules.Rule;
+
+/**
+ * The wrapper for the HttpServletResponse object which will be passed to the application
+ * being protected by the WAF. It contains logic for the response building API in order
+ * to allow the WAF rules regarding responses to work. Much of the work is delegated to
+ * other classes, especially InterceptingServletOutputStream
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class InterceptingHTTPServletResponse extends HttpServletResponseWrapper {
+
+	private InterceptingPrintWriter ipw;
+	private InterceptingServletOutputStream isos;
+	private String contentType;
+
+	private List<AddSecureFlagRule> addSecureFlagRules = null;
+	private List<AddHTTPOnlyFlagRule> addHTTPOnlyFlagRules = null;
+	private boolean alreadyCalledWriter = false;
+	private boolean alreadyCalledOutputStream = false;
+
+	public InterceptingHTTPServletResponse(HttpServletResponse response, boolean buffering, List<Rule> cookieRules) throws IOException {
+
+		super(response);
+		
+		this.contentType = response.getContentType();
+		
+		this.isos = new InterceptingServletOutputStream(response.getOutputStream(), buffering);
+		this.ipw = new InterceptingPrintWriter(new PrintWriter(isos));
+
+		addSecureFlagRules = new ArrayList<AddSecureFlagRule>();
+		addHTTPOnlyFlagRules = new ArrayList<AddHTTPOnlyFlagRule>();
+
+		for(int i=0;i<cookieRules.size();i++) {
+			Rule r = cookieRules.get(i);
+			if ( r instanceof AddSecureFlagRule ) {
+				addSecureFlagRules.add((AddSecureFlagRule)r);
+			} else if ( r instanceof AddHTTPOnlyFlagRule ) {
+				addHTTPOnlyFlagRules.add((AddHTTPOnlyFlagRule)r);
+			}
+		}
+	}
+
+	public boolean isUsingWriter() {
+		return alreadyCalledWriter;
+	}
+
+	public InterceptingServletOutputStream getInterceptingServletOutputStream() {
+		return isos;
+	}
+
+	public ServletOutputStream getOutputStream() throws IllegalStateException, IOException {
+		if ( alreadyCalledWriter == true ) {
+			throw new IllegalStateException();
+		}
+
+		alreadyCalledOutputStream = true;
+
+		return isos;
+    }
+
+	public PrintWriter getWriter() throws IOException {
+		if ( alreadyCalledOutputStream == true ) {
+			throw new IllegalStateException();
+		}
+		alreadyCalledWriter = true;
+
+		return ipw;
+	}
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String s) {
+    	contentType = s;
+    }
+
+    public void flush() {
+    	ipw.flush();
+    }
+
+    public void commit() throws IOException {
+
+    	if ( alreadyCalledWriter ) {
+    		ipw.flush();
+    	}
+
+    	isos.commit();
+    }
+
+    public void addCookie(Cookie cookie) {
+    	addCookie(cookie, cookie.getMaxAge()<=0);
+    }
+    
+	public void addCookie(Cookie cookie, boolean isSession) {
+
+		boolean addSecureFlag = cookie.getSecure();
+		boolean addHTTPOnlyFlag = false;
+
+		if ( ! cookie.getSecure() && addSecureFlagRules != null ) {
+			for(int i=0;i<addSecureFlagRules.size();i++) {
+				AddSecureFlagRule asfr = addSecureFlagRules.get(i);
+				if ( asfr.doesCookieMatch(cookie.getName())) {
+					addSecureFlag = true;
+				}
+			}
+		}
+
+		if ( addHTTPOnlyFlagRules != null ) {
+			for(int i=0;i<addHTTPOnlyFlagRules.size();i++) {
+				AddHTTPOnlyFlagRule ashr = addHTTPOnlyFlagRules.get(i);
+				if ( ashr.doesCookieMatch(cookie.getName())) {
+					addHTTPOnlyFlag = true;
+				}
+			}
+		}
+
+		String cookieValue = createCookieHeader(cookie.getName(),cookie.getValue(),
+												cookie.getMaxAge(),cookie.getDomain(),
+												cookie.getPath(), addSecureFlag,
+												addHTTPOnlyFlag, isSession);
+		addHeader("Set-Cookie", cookieValue);
+
+
+	}
+
+	private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure, boolean httpOnly, boolean isTemporary) {
+        // create the special cookie header instead of creating a Java cookie
+        // Set-Cookie:<name>=<value>[; <name>=<value>][; expires=<date>][;
+        // domain=<domain_name>][; path=<some_path>][; secure][;HttpOnly
+        String header = name + "=" + value;
+
+        if ( ! isTemporary ) {
+        	header += "; Max-Age=" + maxAge;
+        }
+
+        if (domain != null) {
+            header += "; Domain=" + domain;
+        }
+        if (path != null) {
+            header += "; Path=" + path;
+        }
+
+        if ( secure ) {
+        	header += "; Secure";
+        }
+
+        if (httpOnly) {
+        	header += "; HttpOnly";
+        }
+
+        return header;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingPrintWriter.java.svn-base b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingPrintWriter.java.svn-base
new file mode 100644
index 0000000..d73b8bf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingPrintWriter.java.svn-base
@@ -0,0 +1,182 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+/**
+ * The PrintWriter needed to buffer outbound data generated by the application
+ * being protected by the WAF. Currently no logic is needed here right now due
+ * to the WAF things have been architected in the main file, 
+ * InterceptingHTTPServletResponse.
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse
+ */
+public class InterceptingPrintWriter extends PrintWriter {
+
+	public InterceptingPrintWriter(Writer out) {
+		super(out);
+	}
+
+	public PrintWriter append(char c) {
+		return super.append(c);
+	}
+
+	public PrintWriter append(CharSequence csq, int start, int end) {
+		return super.append(csq, start, end);
+	}
+
+	public PrintWriter append(CharSequence csq) {
+		return super.append(csq);
+	}
+
+	public boolean checkError() {
+		return super.checkError();
+	}
+
+// Java 1.6 only
+//	protected void clearError() {
+//		super.clearError();
+//	}
+
+	public void close() {
+		super.close();
+	}
+
+	public void flush() {
+		super.flush();
+	}
+
+	public PrintWriter format(Locale l, String format, Object... args) {
+		return super.format(l, format, args);
+	}
+
+	public PrintWriter format(String format, Object... args) {
+		return super.format(format, args);
+	}
+
+	public void print(boolean b) {
+		super.print(b);
+	}
+
+	public void print(char c) {
+		super.print(c);
+	}
+
+	public void print(char[] s) {
+		super.print(s);
+	}
+
+	public void print(double d) {
+		super.print(d);
+	}
+
+	public void print(float f) {
+		super.print(f);
+	}
+
+	public void print(int i) {
+		super.print(i);
+	}
+
+	public void print(long l) {
+		super.print(l);
+	}
+
+	public void print(Object obj) {
+		super.print(obj);
+	}
+
+	public void print(String s) {
+		super.print(s);
+	}
+
+	public PrintWriter printf(Locale l, String format, Object... args) {
+		return super.printf(l, format, args);
+	}
+
+	public PrintWriter printf(String format, Object... args) {
+		return super.printf(format, args);
+	}
+
+	public void println() {
+		super.println();
+	}
+
+	public void println(boolean x) {
+		super.println(x);
+	}
+
+	public void println(char x) {
+		super.println(x);
+	}
+
+	public void println(char[] x) {
+		super.println(x);
+	}
+
+	public void println(double x) {
+		super.println(x);
+	}
+
+	public void println(float x) {
+		super.println(x);
+	}
+
+	public void println(int x) {
+		super.println(x);
+	}
+
+	public void println(long x) {
+		super.println(x);
+	}
+
+	public void println(Object x) {
+		super.println(x);
+	}
+
+	public void println(String x) {
+		super.println(x);
+	}
+
+	protected void setError() {
+		super.setError();
+	}
+
+	public void write(char[] buf, int off, int len) {
+		super.write(buf, off, len);
+	}
+
+	public void write(char[] buf) {
+		super.write(buf);
+	}
+
+	public void write(int c) {
+		super.write(c);
+	}
+
+	public void write(String s, int off, int len) {
+		super.write(s, off, len);
+	}
+
+	public void write(String s) {
+		super.write(s);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingServletOutputStream.java.svn-base b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingServletOutputStream.java.svn-base
new file mode 100644
index 0000000..9c42a4f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingServletOutputStream.java.svn-base
@@ -0,0 +1,161 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * This class was inspired by ModSecurity for Java by Ivan Ristic. We hook
+ * the response stream and queue up all outbound data so that we can apply
+ * egress rules. For efficiency, we decide off the bat if we need to buffer
+ * responses to accomplish any of the rules in the policy file.
+ *
+ * If not, we just forward everything through, otherwise we write data to our
+ * byte stream that we will eventually forward en totale to the user agent.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+
+public class InterceptingServletOutputStream extends ServletOutputStream {
+
+	private static final int FLUSH_BLOCK_SIZE = 1024;
+	private ServletOutputStream os;
+	private boolean buffering;
+	private boolean committed;
+	private boolean closed;
+	
+	private RandomAccessFile out;
+	
+	public InterceptingServletOutputStream(ServletOutputStream os, boolean buffered) throws FileNotFoundException, IOException {
+		super();
+		this.os = os;
+		this.buffering = buffered;
+		this.committed = false;
+		this.closed = false;
+		
+		/*
+		 * Creating a RandomAccessFile to keep track of output generated. I made
+		 * the prefix and suffix small for less processing. The "oew" is intended
+		 * to stand for "OWASP ESAPI WAF" and the "hop" for HTTP output.
+		 */
+		this.out = new RandomAccessFile ( File.createTempFile("oew", ".hop"), "rw" ); 
+	}
+
+	public void reset() throws IOException {
+		out.setLength(0L);
+	}
+
+	public byte[] getResponseBytes() throws IOException {
+		
+		byte[] buffer = new byte[(int) out.length()];
+		out.seek(0);
+		out.read(buffer, 0, (int)out.length());
+		out.seek(out.length());
+		return buffer;
+		
+	}
+
+	public void setResponseBytes(byte[] responseBytes) throws IOException {
+		
+		if ( ! buffering && out.length() > 0 ) {
+			throw new IOException("Already committed response because not currently buffering");
+		}
+
+		out.setLength(0L);
+		out.write(responseBytes);
+	}
+
+	public void write(int i) throws IOException {
+		if (!buffering) {
+			os.write(i);
+		}
+		out.write(i);
+	}
+
+	public void write(byte[] b) throws IOException {
+        if (!buffering) {
+        	os.write(b, 0, b.length);
+        }
+        out.write(b, 0, b.length);
+    }
+
+	public void write(byte[] b, int off, int len) throws IOException {
+        if (!buffering) {
+        	os.write(b, off, len);
+        }
+        out.write(b, off, len);
+    }
+
+	public void flush() throws IOException {
+		
+		if (buffering) {
+		
+			synchronized(out) {
+
+				out.seek(0);
+				
+				byte[] buff = new byte[FLUSH_BLOCK_SIZE];
+				
+				for(int i=0;i<out.length();) {
+					
+					long currentPos = out.getFilePointer();
+					long totalSize = out.length();
+					int amountToWrite = FLUSH_BLOCK_SIZE;
+					
+					if ( (totalSize - currentPos) < FLUSH_BLOCK_SIZE ) {
+						amountToWrite = (int) (totalSize - currentPos);
+					}
+			
+					out.read(buff, 0, (int)amountToWrite);
+					
+					os.write(buff,0,amountToWrite);
+					
+					i+=amountToWrite;
+					
+				}
+				
+				out.setLength(0);
+				
+			}
+		}
+
+	}
+
+	public void commit() throws IOException {
+		
+		if (!buffering) { // || committed || closed
+        	return;
+        } else {
+        	flush();
+        }
+		committed = true;
+    }
+
+    public void close() throws IOException {
+    	
+        if (!buffering)  {
+        	os.close();
+        }
+        closed = true;
+
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/Parameter.java.svn-base b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/Parameter.java.svn-base
new file mode 100644
index 0000000..81d16a1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/Parameter.java.svn-base
@@ -0,0 +1,49 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+/**
+ * A simple object to represent a name=value HTTP parameter.
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class Parameter {
+
+	private String name;
+	private String value;
+	private boolean fromMultipart;
+
+	public Parameter(String name, String value, boolean fromMultipart) {
+		this.name = name;
+		this.value = value;
+		this.fromMultipart = fromMultipart;
+	}
+
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	public String getValue() {
+		return value;
+	}
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..3b53551
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/.svn/text-base/package.html.svn-base
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains all HTTP-related classes used internally by the WAF for the implementation
+of its rules. 
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java b/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java
new file mode 100644
index 0000000..6766f74
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java
@@ -0,0 +1,189 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.RandomAccessFile;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.apache.commons.fileupload.FileItemIterator;
+import org.apache.commons.fileupload.FileItemStream;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.util.Streams;
+
+/**
+ * The wrapper for the HttpServletRequest object which will be passed to the application
+ * being protected by the WAF. It contains logic for parsing multipart parameters out of
+ * the request and provided downstream application logic a way of accessing it like it 
+ * hasn't been touched.
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class InterceptingHTTPServletRequest extends HttpServletRequestWrapper {
+
+	private Vector<Parameter> allParameters;
+	private Vector<String> allParameterNames;
+	private static int CHUNKED_BUFFER_SIZE = 1024;
+	
+	private boolean isMultipart = false;
+	private RandomAccessFile requestBody;
+	private RAFInputStream is;
+	
+	public ServletInputStream getInputStream() throws IOException {
+		
+		if ( isMultipart ) {
+			return is;	
+		} else {
+			return super.getInputStream();
+		}
+        
+    }
+	
+	public BufferedReader getReader() throws IOException {
+        String enc = getCharacterEncoding();
+        if(enc == null) enc = "UTF-8";
+        return new BufferedReader(new InputStreamReader(getInputStream(), enc));
+    }
+	
+	public InterceptingHTTPServletRequest(HttpServletRequest request) throws FileUploadException, IOException {
+
+		super(request);
+
+		allParameters = new Vector<Parameter>();
+		allParameterNames = new Vector<String>();
+
+
+		/*
+		 * Get all the regular parameters.
+		 */
+
+		Enumeration e = request.getParameterNames();
+
+		while(e.hasMoreElements()) {
+			String param = (String)e.nextElement();
+			allParameters.add(new Parameter(param,request.getParameter(param),false));
+			allParameterNames.add(param);
+		}
+
+
+		/*
+		 * Get all the multipart fields.
+		 */
+
+		isMultipart = ServletFileUpload.isMultipartContent(request);
+
+		if ( isMultipart ) {
+
+			requestBody = new RandomAccessFile( File.createTempFile("oew","mpc"), "rw");
+	    	
+	    	byte buffer[] = new byte[CHUNKED_BUFFER_SIZE];
+
+	    	long size = 0;
+	    	int len = 0;
+
+	    	while ( len != -1 && size <= Integer.MAX_VALUE) {
+	    		len = request.getInputStream().read(buffer, 0, CHUNKED_BUFFER_SIZE);
+	    		if ( len != -1 ) {
+	    			size += len;
+	    			requestBody.write(buffer,0,len);	
+	    		}
+	    	}
+			
+	    	is = new RAFInputStream(requestBody);
+	    	
+			ServletFileUpload sfu = new ServletFileUpload();
+			FileItemIterator iter = sfu.getItemIterator(this);
+
+			while(iter.hasNext()) {
+				FileItemStream item = iter.next();
+				String name = item.getFieldName();
+				InputStream stream = item.openStream();
+
+				/*
+				 * If this is a regular form field, add it to our
+				 * parameter collection.
+				 */
+
+				if (item.isFormField()) {
+
+					String value = Streams.asString(stream);
+
+					allParameters.add(new Parameter(name,value,true));
+			    	allParameterNames.add(name);
+
+			    } else {
+			    	/*
+			    	 * This is a multipart content that is not a
+			    	 * regular form field. Nothing to do here.
+			    	 */
+			    	
+			    }
+
+			}
+			
+			requestBody.seek(0);
+			
+		}
+
+	}
+
+	public String getDictionaryParameter(String s) {
+
+		for(int i=0;i<allParameters.size();i++) {
+			Parameter p = allParameters.get(i);
+			if ( p.getName().equals(s) ) {
+				return p.getValue();
+			}
+		}
+		
+		return null;
+	}
+
+	public Enumeration getDictionaryParameterNames() {
+		return allParameterNames.elements();
+	}
+	
+	
+	private class RAFInputStream extends ServletInputStream {
+		
+		RandomAccessFile raf;
+		
+		public RAFInputStream(RandomAccessFile raf) throws IOException {
+			this.raf = raf;
+			this.raf.seek(0);
+		}
+
+		public int read() throws IOException {
+			return raf.read();
+		}
+		
+		public synchronized void reset() throws IOException {
+			raf.seek(0);
+		}
+	}
+	
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletResponse.java b/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletResponse.java
new file mode 100644
index 0000000..2cd41a3
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletResponse.java
@@ -0,0 +1,186 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.owasp.esapi.waf.rules.AddHTTPOnlyFlagRule;
+import org.owasp.esapi.waf.rules.AddSecureFlagRule;
+import org.owasp.esapi.waf.rules.Rule;
+
+/**
+ * The wrapper for the HttpServletResponse object which will be passed to the application
+ * being protected by the WAF. It contains logic for the response building API in order
+ * to allow the WAF rules regarding responses to work. Much of the work is delegated to
+ * other classes, especially InterceptingServletOutputStream
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class InterceptingHTTPServletResponse extends HttpServletResponseWrapper {
+
+	private InterceptingPrintWriter ipw;
+	private InterceptingServletOutputStream isos;
+	private String contentType;
+
+	private List<AddSecureFlagRule> addSecureFlagRules = null;
+	private List<AddHTTPOnlyFlagRule> addHTTPOnlyFlagRules = null;
+	private boolean alreadyCalledWriter = false;
+	private boolean alreadyCalledOutputStream = false;
+
+	public InterceptingHTTPServletResponse(HttpServletResponse response, boolean buffering, List<Rule> cookieRules) throws IOException {
+
+		super(response);
+		
+		this.contentType = response.getContentType();
+		
+		this.isos = new InterceptingServletOutputStream(response.getOutputStream(), buffering);
+		this.ipw = new InterceptingPrintWriter(new PrintWriter(isos));
+
+		addSecureFlagRules = new ArrayList<AddSecureFlagRule>();
+		addHTTPOnlyFlagRules = new ArrayList<AddHTTPOnlyFlagRule>();
+
+		for(int i=0;i<cookieRules.size();i++) {
+			Rule r = cookieRules.get(i);
+			if ( r instanceof AddSecureFlagRule ) {
+				addSecureFlagRules.add((AddSecureFlagRule)r);
+			} else if ( r instanceof AddHTTPOnlyFlagRule ) {
+				addHTTPOnlyFlagRules.add((AddHTTPOnlyFlagRule)r);
+			}
+		}
+	}
+
+	public boolean isUsingWriter() {
+		return alreadyCalledWriter;
+	}
+
+	public InterceptingServletOutputStream getInterceptingServletOutputStream() {
+		return isos;
+	}
+
+	public ServletOutputStream getOutputStream() throws IllegalStateException, IOException {
+		if ( alreadyCalledWriter == true ) {
+			throw new IllegalStateException();
+		}
+
+		alreadyCalledOutputStream = true;
+
+		return isos;
+    }
+
+	public PrintWriter getWriter() throws IOException {
+		if ( alreadyCalledOutputStream == true ) {
+			throw new IllegalStateException();
+		}
+		alreadyCalledWriter = true;
+
+		return ipw;
+	}
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String s) {
+    	contentType = s;
+    }
+
+    public void flush() {
+    	ipw.flush();
+    }
+
+    public void commit() throws IOException {
+
+    	if ( alreadyCalledWriter ) {
+    		ipw.flush();
+    	}
+
+    	isos.commit();
+    }
+
+    public void addCookie(Cookie cookie) {
+    	addCookie(cookie, cookie.getMaxAge()<=0);
+    }
+    
+	public void addCookie(Cookie cookie, boolean isSession) {
+
+		boolean addSecureFlag = cookie.getSecure();
+		boolean addHTTPOnlyFlag = false;
+
+		if ( ! cookie.getSecure() && addSecureFlagRules != null ) {
+			for(int i=0;i<addSecureFlagRules.size();i++) {
+				AddSecureFlagRule asfr = addSecureFlagRules.get(i);
+				if ( asfr.doesCookieMatch(cookie.getName())) {
+					addSecureFlag = true;
+				}
+			}
+		}
+
+		if ( addHTTPOnlyFlagRules != null ) {
+			for(int i=0;i<addHTTPOnlyFlagRules.size();i++) {
+				AddHTTPOnlyFlagRule ashr = addHTTPOnlyFlagRules.get(i);
+				if ( ashr.doesCookieMatch(cookie.getName())) {
+					addHTTPOnlyFlag = true;
+				}
+			}
+		}
+
+		String cookieValue = createCookieHeader(cookie.getName(),cookie.getValue(),
+												cookie.getMaxAge(),cookie.getDomain(),
+												cookie.getPath(), addSecureFlag,
+												addHTTPOnlyFlag, isSession);
+		addHeader("Set-Cookie", cookieValue);
+
+
+	}
+
+	private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure, boolean httpOnly, boolean isTemporary) {
+        // create the special cookie header instead of creating a Java cookie
+        // Set-Cookie:<name>=<value>[; <name>=<value>][; expires=<date>][;
+        // domain=<domain_name>][; path=<some_path>][; secure][;HttpOnly
+        String header = name + "=" + value;
+
+        if ( ! isTemporary ) {
+        	header += "; Max-Age=" + maxAge;
+        }
+
+        if (domain != null) {
+            header += "; Domain=" + domain;
+        }
+        if (path != null) {
+            header += "; Path=" + path;
+        }
+
+        if ( secure ) {
+        	header += "; Secure";
+        }
+
+        if (httpOnly) {
+        	header += "; HttpOnly";
+        }
+
+        return header;
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/InterceptingPrintWriter.java b/src/main/java/org/owasp/esapi/waf/internal/InterceptingPrintWriter.java
new file mode 100644
index 0000000..d73b8bf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/InterceptingPrintWriter.java
@@ -0,0 +1,182 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+/**
+ * The PrintWriter needed to buffer outbound data generated by the application
+ * being protected by the WAF. Currently no logic is needed here right now due
+ * to the WAF things have been architected in the main file, 
+ * InterceptingHTTPServletResponse.
+ * 
+ * @author Arshan Dabirsiaghi
+ * @see org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse
+ */
+public class InterceptingPrintWriter extends PrintWriter {
+
+	public InterceptingPrintWriter(Writer out) {
+		super(out);
+	}
+
+	public PrintWriter append(char c) {
+		return super.append(c);
+	}
+
+	public PrintWriter append(CharSequence csq, int start, int end) {
+		return super.append(csq, start, end);
+	}
+
+	public PrintWriter append(CharSequence csq) {
+		return super.append(csq);
+	}
+
+	public boolean checkError() {
+		return super.checkError();
+	}
+
+// Java 1.6 only
+//	protected void clearError() {
+//		super.clearError();
+//	}
+
+	public void close() {
+		super.close();
+	}
+
+	public void flush() {
+		super.flush();
+	}
+
+	public PrintWriter format(Locale l, String format, Object... args) {
+		return super.format(l, format, args);
+	}
+
+	public PrintWriter format(String format, Object... args) {
+		return super.format(format, args);
+	}
+
+	public void print(boolean b) {
+		super.print(b);
+	}
+
+	public void print(char c) {
+		super.print(c);
+	}
+
+	public void print(char[] s) {
+		super.print(s);
+	}
+
+	public void print(double d) {
+		super.print(d);
+	}
+
+	public void print(float f) {
+		super.print(f);
+	}
+
+	public void print(int i) {
+		super.print(i);
+	}
+
+	public void print(long l) {
+		super.print(l);
+	}
+
+	public void print(Object obj) {
+		super.print(obj);
+	}
+
+	public void print(String s) {
+		super.print(s);
+	}
+
+	public PrintWriter printf(Locale l, String format, Object... args) {
+		return super.printf(l, format, args);
+	}
+
+	public PrintWriter printf(String format, Object... args) {
+		return super.printf(format, args);
+	}
+
+	public void println() {
+		super.println();
+	}
+
+	public void println(boolean x) {
+		super.println(x);
+	}
+
+	public void println(char x) {
+		super.println(x);
+	}
+
+	public void println(char[] x) {
+		super.println(x);
+	}
+
+	public void println(double x) {
+		super.println(x);
+	}
+
+	public void println(float x) {
+		super.println(x);
+	}
+
+	public void println(int x) {
+		super.println(x);
+	}
+
+	public void println(long x) {
+		super.println(x);
+	}
+
+	public void println(Object x) {
+		super.println(x);
+	}
+
+	public void println(String x) {
+		super.println(x);
+	}
+
+	protected void setError() {
+		super.setError();
+	}
+
+	public void write(char[] buf, int off, int len) {
+		super.write(buf, off, len);
+	}
+
+	public void write(char[] buf) {
+		super.write(buf);
+	}
+
+	public void write(int c) {
+		super.write(c);
+	}
+
+	public void write(String s, int off, int len) {
+		super.write(s, off, len);
+	}
+
+	public void write(String s) {
+		super.write(s);
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java b/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java
new file mode 100644
index 0000000..9c42a4f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java
@@ -0,0 +1,161 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * This class was inspired by ModSecurity for Java by Ivan Ristic. We hook
+ * the response stream and queue up all outbound data so that we can apply
+ * egress rules. For efficiency, we decide off the bat if we need to buffer
+ * responses to accomplish any of the rules in the policy file.
+ *
+ * If not, we just forward everything through, otherwise we write data to our
+ * byte stream that we will eventually forward en totale to the user agent.
+ * 
+ * @author Arshan Dabirsiaghi
+ */
+
+public class InterceptingServletOutputStream extends ServletOutputStream {
+
+	private static final int FLUSH_BLOCK_SIZE = 1024;
+	private ServletOutputStream os;
+	private boolean buffering;
+	private boolean committed;
+	private boolean closed;
+	
+	private RandomAccessFile out;
+	
+	public InterceptingServletOutputStream(ServletOutputStream os, boolean buffered) throws FileNotFoundException, IOException {
+		super();
+		this.os = os;
+		this.buffering = buffered;
+		this.committed = false;
+		this.closed = false;
+		
+		/*
+		 * Creating a RandomAccessFile to keep track of output generated. I made
+		 * the prefix and suffix small for less processing. The "oew" is intended
+		 * to stand for "OWASP ESAPI WAF" and the "hop" for HTTP output.
+		 */
+		this.out = new RandomAccessFile ( File.createTempFile("oew", ".hop"), "rw" ); 
+	}
+
+	public void reset() throws IOException {
+		out.setLength(0L);
+	}
+
+	public byte[] getResponseBytes() throws IOException {
+		
+		byte[] buffer = new byte[(int) out.length()];
+		out.seek(0);
+		out.read(buffer, 0, (int)out.length());
+		out.seek(out.length());
+		return buffer;
+		
+	}
+
+	public void setResponseBytes(byte[] responseBytes) throws IOException {
+		
+		if ( ! buffering && out.length() > 0 ) {
+			throw new IOException("Already committed response because not currently buffering");
+		}
+
+		out.setLength(0L);
+		out.write(responseBytes);
+	}
+
+	public void write(int i) throws IOException {
+		if (!buffering) {
+			os.write(i);
+		}
+		out.write(i);
+	}
+
+	public void write(byte[] b) throws IOException {
+        if (!buffering) {
+        	os.write(b, 0, b.length);
+        }
+        out.write(b, 0, b.length);
+    }
+
+	public void write(byte[] b, int off, int len) throws IOException {
+        if (!buffering) {
+        	os.write(b, off, len);
+        }
+        out.write(b, off, len);
+    }
+
+	public void flush() throws IOException {
+		
+		if (buffering) {
+		
+			synchronized(out) {
+
+				out.seek(0);
+				
+				byte[] buff = new byte[FLUSH_BLOCK_SIZE];
+				
+				for(int i=0;i<out.length();) {
+					
+					long currentPos = out.getFilePointer();
+					long totalSize = out.length();
+					int amountToWrite = FLUSH_BLOCK_SIZE;
+					
+					if ( (totalSize - currentPos) < FLUSH_BLOCK_SIZE ) {
+						amountToWrite = (int) (totalSize - currentPos);
+					}
+			
+					out.read(buff, 0, (int)amountToWrite);
+					
+					os.write(buff,0,amountToWrite);
+					
+					i+=amountToWrite;
+					
+				}
+				
+				out.setLength(0);
+				
+			}
+		}
+
+	}
+
+	public void commit() throws IOException {
+		
+		if (!buffering) { // || committed || closed
+        	return;
+        } else {
+        	flush();
+        }
+		committed = true;
+    }
+
+    public void close() throws IOException {
+    	
+        if (!buffering)  {
+        	os.close();
+        }
+        closed = true;
+
+    }
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/Parameter.java b/src/main/java/org/owasp/esapi/waf/internal/Parameter.java
new file mode 100644
index 0000000..81d16a1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/Parameter.java
@@ -0,0 +1,49 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.internal;
+
+/**
+ * A simple object to represent a name=value HTTP parameter.
+ * 
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class Parameter {
+
+	private String name;
+	private String value;
+	private boolean fromMultipart;
+
+	public Parameter(String name, String value, boolean fromMultipart) {
+		this.name = name;
+		this.value = value;
+		this.fromMultipart = fromMultipart;
+	}
+
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	public String getValue() {
+		return value;
+	}
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/internal/package.html b/src/main/java/org/owasp/esapi/waf/internal/package.html
new file mode 100644
index 0000000..3b53551
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/internal/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains all HTTP-related classes used internally by the WAF for the implementation
+of its rules. 
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/package.html b/src/main/java/org/owasp/esapi/waf/package.html
new file mode 100644
index 0000000..bf23005
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/package.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains the ESAPI Web Application Firewall (WAF). It is an optional feature of ESAPI
+that can be used with or without ESAPI's other security controls in place. It's purpose is to provide
+fast virtual patching capabilities against known vulnerabilities or the enforcement of existing
+security policies where possible.
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/all-wcprops b/src/main/java/org/owasp/esapi/waf/rules/.svn/all-wcprops
new file mode 100644
index 0000000..8a9aa2e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/all-wcprops
@@ -0,0 +1,119 @@
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules
+END
+RestrictUserAgentRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/RestrictUserAgentRule.java
+END
+RuleUtil.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java
+END
+Rule.java
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/Rule.java
+END
+SimpleVirtualPatchRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/SimpleVirtualPatchRule.java
+END
+HTTPMethodRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/HTTPMethodRule.java
+END
+AuthenticatedRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/AuthenticatedRule.java
+END
+DetectOutboundContentRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/DetectOutboundContentRule.java
+END
+AddHTTPOnlyFlagRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/AddHTTPOnlyFlagRule.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/package.html
+END
+GeneralAttackSignatureRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/GeneralAttackSignatureRule.java
+END
+AddSecureFlagRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/AddSecureFlagRule.java
+END
+PathExtensionRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/PathExtensionRule.java
+END
+RestrictContentTypeRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/RestrictContentTypeRule.java
+END
+BeanShellRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java
+END
+AddHeaderRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/AddHeaderRule.java
+END
+EnforceHTTPSRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/EnforceHTTPSRule.java
+END
+MustMatchRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/MustMatchRule.java
+END
+ReplaceContentRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/ReplaceContentRule.java
+END
+IPRule.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules/IPRule.java
+END
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/entries b/src/main/java/org/owasp/esapi/waf/rules/.svn/entries
new file mode 100644
index 0000000..7fedec9
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/entries
@@ -0,0 +1,674 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/main/java/org/owasp/esapi/waf/rules
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-03-23T14:39:22.287535Z
+1741
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+AddHeaderRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+0c50c8c686b48279a1457037b13a6d70
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2494
+

+EnforceHTTPSRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+37c83579fccebe2598281e0c00f2b596
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2736
+

+MustMatchRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+28320857d6ea0cc051db0228e4654cd6
+2010-02-05T23:55:46.897956Z
+1120
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11155
+

+ReplaceContentRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+a8e8ade6b5adb0a080596cacfe904d2e
+2010-11-16T03:55:11.826670Z
+1655
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3908
+

+IPRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+f586d22c5004eba52243e6a763adc949
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2341
+

+RestrictUserAgentRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+8ebfffb590c2faff61008b973b3d578d
+2009-11-20T00:33:25.026048Z
+832
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2601
+

+RuleUtil.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+aabd11c29badf3860e1ac2a7489c5390
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3139
+

+Rule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+0281fef9d6b9a0c51e1ef0cf1e42178e
+2010-11-16T03:55:11.826670Z
+1655
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1797
+

+SimpleVirtualPatchRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+552f64e9edf33c0595fbcc8a1f750130
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4498
+

+HTTPMethodRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+d583ff01fa8da76bb6ef65485ff2f729
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2476
+

+AuthenticatedRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+9e3bfd8f0eec8d48dace7c8a67307bda
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2686
+

+DetectOutboundContentRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+9ce0508b50d68880dd584f49d5598105
+2009-11-10T17:28:58.101254Z
+778
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3709
+

+AddHTTPOnlyFlagRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+b79ac64d493092d88928e8610d7cd113
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1734
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+beae08ef15d97e3802087949fc228757
+2009-11-09T05:26:38.518570Z
+763
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+278
+

+GeneralAttackSignatureRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+099235eb91d3df072d72c89450e3416b
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2140
+

+AddSecureFlagRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+49697afe96397fa7bee30b5473be628f
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1729
+

+PathExtensionRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+de8768389e75225e6a5a6584edaf3400
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1911
+

+RestrictContentTypeRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+6608e2f0593085cada11af0c1dd3685c
+2009-11-09T06:02:47.274867Z
+764
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2352
+

+BeanShellRule.java
+file
+
+
+
+
+2014-02-18T16:19:52.645960Z
+69506e241a0c0855b00e0055c2840d87
+2011-03-23T14:39:22.287535Z
+1741
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3144
+

diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddHTTPOnlyFlagRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddHTTPOnlyFlagRule.java.svn-base
new file mode 100644
index 0000000..6a382fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddHTTPOnlyFlagRule.java.svn-base
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <add-http-only-flag> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AddHTTPOnlyFlagRule extends Rule {
+
+	private List<Pattern> name;
+
+	public AddHTTPOnlyFlagRule(String id, List<Pattern> name) {
+		setId(id);
+		this.name = name;
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		DoNothingAction action = new DoNothingAction();
+
+		return action;
+	}
+
+	public boolean doesCookieMatch(String cookieName) {
+
+		for(int i=0;i<name.size();i++) {
+			Pattern p = name.get(i);
+			if ( p.matcher(cookieName).matches() ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddHeaderRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddHeaderRule.java.svn-base
new file mode 100644
index 0000000..d235b59
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddHeaderRule.java.svn-base
@@ -0,0 +1,92 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <add-header> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AddHeaderRule extends Rule {
+
+	private String header;
+	private String value;
+	private Pattern path;
+	private List<Object> exceptions;
+
+	public AddHeaderRule(String id, String header, String value, Pattern path, List<Object> exceptions) {
+		setId(id);
+		this.header = header;
+		this.value = value;
+		this.path = path;
+		this.exceptions = exceptions;
+	}
+
+	public Action check(
+			HttpServletRequest request, 
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		DoNothingAction action = new DoNothingAction();
+
+		if ( path.matcher(request.getRequestURI()).matches() ) {
+
+			for(int i=0;i<exceptions.size();i++) {
+
+				Object o = exceptions.get(i);
+
+				if ( o instanceof String ) {
+					if ( request.getRequestURI().equals((String)o)) {
+						action.setFailed(false);
+						action.setActionNecessary(false);
+						return action;
+					}
+				} else if ( o instanceof Pattern ) {
+					if ( ((Pattern)o).matcher(request.getRequestURI()).matches() ) {
+						action.setFailed(false);
+						action.setActionNecessary(false);
+						return action;					
+					}
+				}
+
+			}
+
+
+			action.setFailed(true);
+			action.setActionNecessary(false);
+
+			if ( response != null ) {
+				response.setHeader(header, value);
+			} else {
+				httpResponse.setHeader(header, value);
+			}
+
+		}
+
+		return action;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddSecureFlagRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddSecureFlagRule.java.svn-base
new file mode 100644
index 0000000..6dabe53
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AddSecureFlagRule.java.svn-base
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <add-secure-flag> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AddSecureFlagRule extends Rule {
+
+	private List<Pattern> name;
+
+	public AddSecureFlagRule(String id, List<Pattern> name) {
+		this.name = name;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+		
+		DoNothingAction action = new DoNothingAction();
+
+		return action;
+	}
+
+	public boolean doesCookieMatch(String cookieName) {
+
+		for(int i=0;i<name.size();i++) {
+			Pattern p = name.get(i);
+			if ( p.matcher(cookieName).matches() ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AuthenticatedRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AuthenticatedRule.java.svn-base
new file mode 100644
index 0000000..747a0f6
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/AuthenticatedRule.java.svn-base
@@ -0,0 +1,92 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <authentication-rules> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AuthenticatedRule extends Rule {
+
+	private String sessionAttribute;
+	private Pattern path;
+	private List<Object> exceptions;
+
+	public AuthenticatedRule(String id, String sessionAttribute, Pattern path, List<Object> exceptions) {
+		this.sessionAttribute = sessionAttribute;
+		this.path = path;
+		this.exceptions = exceptions;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		HttpSession session = request.getSession();
+		String uri = request.getRequestURI();
+
+		if ( path != null && ! path.matcher(uri).matches() ) {
+			return new DoNothingAction();
+		}
+
+		if ( session != null && session.getAttribute(sessionAttribute) != null ) {
+
+			return new DoNothingAction();
+
+		} else { /* check if it's one of the exceptions */
+
+			Iterator<Object> it = exceptions.iterator();
+
+			while(it.hasNext()) {
+				Object o = it.next();
+				if ( o instanceof Pattern ) {
+
+					Pattern p = (Pattern)o;
+					if ( p.matcher(uri).matches() ) {
+						return new DoNothingAction();
+					}
+
+				} else if ( o instanceof String ) {
+
+					if ( uri.equals((String)o)) {
+						return new DoNothingAction();
+					}
+
+				}
+			}
+		}
+
+		log(request, "User requested unauthenticated access to URI '" + request.getRequestURI() + "' [querystring="+request.getQueryString()+"]");
+
+		return new DefaultAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/BeanShellRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/BeanShellRule.java.svn-base
new file mode 100644
index 0000000..f54589f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/BeanShellRule.java.svn-base
@@ -0,0 +1,116 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+import bsh.EvalError;
+import bsh.Interpreter;
+
+/**
+ * This is the Rule subclass executed for <bean-shell-script> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class BeanShellRule extends Rule {
+
+	private Interpreter i;
+	private String script;
+	private Pattern path;
+	
+	public BeanShellRule(String fileLocation, String id, Pattern path) throws IOException, EvalError { 
+		i = new Interpreter();
+		i.set("logger", logger);
+		this.script = getFileContents( ESAPI.securityConfiguration().getResourceFile(fileLocation));
+		this.id = id;
+		this.path = path;
+	}
+	
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * Early fail: if the URL doesn't match one we're interested in.
+		 */
+		
+		if ( path != null && ! path.matcher(request.getRequestURI()).matches() ) {
+			return new DoNothingAction();
+		}
+		
+		/*
+		 * Run the beanshell that we've already parsed
+		 * and pre-compiled. Populate the "request"
+		 * and "response" objects so the script has
+		 * access to the same variables we do here.
+		 */
+		
+		try {
+		
+			Action a = null;
+			
+			i.set("action", a);
+			i.set("request", request);
+			
+			if ( response != null ) {
+				i.set("response", response);	
+			} else {
+				i.set("response", httpResponse);
+			}
+
+			i.set("session", request.getSession());
+			i.eval(script);
+
+			a = (Action)i.get("action");
+	
+			if ( a != null ) {
+				return a;
+			}
+			
+		} catch (EvalError e) {
+			log(request,"Error running custom beanshell rule (" + id + ") - " + e.getMessage());
+		}
+	
+		return new DoNothingAction();
+	}
+	
+	private String getFileContents(File f) throws IOException {
+		
+		FileReader fr = new FileReader(f);
+		StringBuffer sb = new StringBuffer();
+		String line;
+		BufferedReader br = new BufferedReader(fr);
+		
+		while( (line=br.readLine()) != null ) {
+			sb.append(line + System.getProperty("line.separator"));
+		}
+		
+		return sb.toString();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/DetectOutboundContentRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/DetectOutboundContentRule.java.svn-base
new file mode 100644
index 0000000..96fe3c1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/DetectOutboundContentRule.java.svn-base
@@ -0,0 +1,116 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <detect-content> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class DetectOutboundContentRule extends Rule {
+
+	private Pattern contentType;
+	private Pattern pattern;
+	private Pattern uri;
+	
+	public DetectOutboundContentRule(String id, Pattern contentType, Pattern pattern, Pattern uri) {
+		this.contentType = contentType;
+		this.pattern = pattern;
+		this.uri = uri;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * Early fail: if URI doesn't match.
+		 */
+		if ( uri != null && ! uri.matcher(request.getRequestURI()).matches() ) {
+			return new DoNothingAction(); 
+		}
+
+		/*
+		 * Early fail: if the content type is one we'd like to search for output patterns.
+		 */
+
+		String inboundContentType;
+		String charEnc;
+		
+		if ( response != null ) {
+			if ( response.getContentType() == null ) {
+				response.setContentType(AppGuardianConfiguration.DEFAULT_CONTENT_TYPE);
+			}
+			inboundContentType = response.getContentType();
+			charEnc = response.getCharacterEncoding();
+			
+		} else {
+			if ( httpResponse.getContentType() == null ) {
+				httpResponse.setContentType(AppGuardianConfiguration.DEFAULT_CONTENT_TYPE);
+			}
+			inboundContentType = httpResponse.getContentType();
+			charEnc = httpResponse.getCharacterEncoding();
+		}
+	
+		if ( contentType.matcher(inboundContentType).matches() ) {
+			/*
+			 * Depending on the encoding, search through the bytes
+			 * for the pattern.
+			 */
+			try {
+
+				byte[] bytes = null;
+				
+				try {
+					bytes = response.getInterceptingServletOutputStream().getResponseBytes();
+				} catch (IOException ioe) {
+					log(request,"Error matching pattern '" + pattern.pattern() + "', IOException encountered (possibly too large?): " + ioe.getMessage() + " (in response to URL: '" + request.getRequestURL() + "')");
+					return new DoNothingAction(); // yes this is a fail open!
+				}
+
+				String s = new String(bytes,charEnc);
+
+				if ( pattern.matcher(s).matches() ) {
+
+					log(request,"Content pattern '" + pattern.pattern() + "' was found in response to URL: '" + request.getRequestURL() + "'");
+					return new DefaultAction();
+
+				}
+
+			} catch (UnsupportedEncodingException uee) {
+				log(request,"Content pattern '" + pattern.pattern() + "' could not be found due to encoding error: " + uee.getMessage());
+			}
+		}
+
+		return new DoNothingAction();
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/EnforceHTTPSRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/EnforceHTTPSRule.java.svn-base
new file mode 100644
index 0000000..9da25bd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/EnforceHTTPSRule.java.svn-base
@@ -0,0 +1,95 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.actions.RedirectAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <enforce-https> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class EnforceHTTPSRule extends Rule {
+
+	private Pattern path;
+	private List<Object> exceptions;
+	private String action;
+
+	/*
+	 * action = [ redirect | block ] [=default (redirect will redirect to error page]
+	 */
+
+	public EnforceHTTPSRule(String id, Pattern path, List<Object> exceptions, String action) {
+		this.path = path;
+		this.exceptions = exceptions;
+		this.action = action;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		if ( ! request.isSecure() ) {
+
+			if ( path.matcher(request.getRequestURI()).matches() ) {
+
+				Iterator<Object> it = exceptions.iterator();
+
+				while(it.hasNext()){
+
+					Object o = it.next();
+
+					if ( o instanceof String ) {
+						if ( ((String)o).equalsIgnoreCase(request.getRequestURI()) ) {
+							return new DoNothingAction();
+						}
+					} else if ( o instanceof Pattern ) {
+						if ( ((Pattern)o).matcher(request.getRequestURI()).matches() ) {
+							return new DoNothingAction();
+						}
+					}
+
+				}
+
+				log(request,"Insecure request to resource detected in URL: '" + request.getRequestURL() + "'");
+
+				if ( "redirect".equals(action) ) {
+					RedirectAction ra = new RedirectAction();
+					ra.setRedirectURL(request.getRequestURL().toString().replaceFirst("http", "https"));
+					return ra;
+				}
+
+				return new DefaultAction();
+
+			}
+		}
+
+		return new DoNothingAction();
+
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/GeneralAttackSignatureRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/GeneralAttackSignatureRule.java.svn-base
new file mode 100644
index 0000000..1c53550
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/GeneralAttackSignatureRule.java.svn-base
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <general-attack-signature> rules, which 
+ * are not currently implemented.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class GeneralAttackSignatureRule extends Rule {
+
+	private Pattern signature;
+
+	public GeneralAttackSignatureRule(String id, Pattern signature) {
+		this.signature = signature;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest req,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		InterceptingHTTPServletRequest request = (InterceptingHTTPServletRequest)req;
+		Enumeration e = request.getParameterNames();
+
+		while(e.hasMoreElements()) {
+			String param = (String)e.nextElement();
+			if ( signature.matcher(request.getDictionaryParameter(param)).matches() ) {
+				log(request,"General attack signature detected in parameter '" + param + "' value '" + request.getDictionaryParameter(param) + "'");
+				return new DefaultAction();
+			}
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/HTTPMethodRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/HTTPMethodRule.java.svn-base
new file mode 100644
index 0000000..5aa132f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/HTTPMethodRule.java.svn-base
@@ -0,0 +1,78 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <restrict-method> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class HTTPMethodRule extends Rule {
+
+	private Pattern allowedMethods;
+	private Pattern deniedMethods;
+	private Pattern path;
+
+	public HTTPMethodRule(String id, Pattern allowedMethods, Pattern deniedMethods, Pattern path) {
+		this.allowedMethods = allowedMethods;
+		this.deniedMethods = deniedMethods;
+		this.path = path;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * If no path is specified, apply rule globally.
+		 */
+		String uri = request.getRequestURI();
+		String method = request.getMethod();
+
+		if ( path == null || path.matcher(uri).matches() ) {
+			/*
+			 *	Order allow, deny.
+			 */
+
+			if ( allowedMethods != null && allowedMethods.matcher(method).matches() ) {
+				return new DoNothingAction();
+			} else if ( allowedMethods != null ) {
+				log(request,"Disallowed HTTP method '" + request.getMethod() + "' found for URL: " + request.getRequestURL());
+				return new DefaultAction();
+			}
+
+			if ( deniedMethods != null && deniedMethods.matcher(method).matches() ) {
+				log(request,"Disallowed HTTP method '" + request.getMethod() + "' found for URL: " + request.getRequestURL());
+				return new DefaultAction();
+			}
+
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/IPRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/IPRule.java.svn-base
new file mode 100644
index 0000000..e6a15c8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/IPRule.java.svn-base
@@ -0,0 +1,79 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <detect-source-ip> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class IPRule extends Rule {
+
+	private Pattern allowedIP;
+	private String exactPath;
+	private Pattern path;
+	private boolean useExactPath = false;
+	private String ipHeader;
+
+	public IPRule(String id, Pattern allowedIP, Pattern path, String ipHeader) {
+		this.allowedIP = allowedIP;
+		this.path = path;
+		this.useExactPath = false;
+		this.ipHeader = ipHeader;
+		setId(id);
+	}
+
+	public IPRule(String id, Pattern allowedIP, String exactPath) {
+		this.path = null;
+		this.exactPath = exactPath;
+		this.useExactPath = true;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		String uri = request.getRequestURI();
+
+		if ( (!useExactPath && path.matcher(uri).matches()) ||
+			 ( useExactPath && exactPath.equals(uri)) ) {
+			
+			String sourceIP = request.getRemoteAddr() + "";
+			
+			if ( ipHeader != null ) {
+				sourceIP = request.getHeader(ipHeader);
+			}
+			
+			if ( ! allowedIP.matcher(sourceIP).matches() ) {
+				log(request, "IP not allowed to access URI '" + uri + "'");
+				return new DefaultAction();
+			}
+		}
+
+		return new DoNothingAction();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/MustMatchRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/MustMatchRule.java.svn-base
new file mode 100644
index 0000000..44e09cd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/MustMatchRule.java.svn-base
@@ -0,0 +1,336 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <must-match> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class MustMatchRule extends Rule {
+
+	private static final String REQUEST_PARAMETERS = "request.parameters.";
+	private static final String REQUEST_HEADERS = "request.headers.";
+	private static final String REQUEST_URI = "request.uri";
+	private static final String REQUEST_URL = "request.url";
+	private static final String SESSION_ATTRIBUTES = "session.";
+
+	private Pattern path;
+	private String variable;
+	private int operator;
+	private String value;
+
+	public MustMatchRule(String id, Pattern path, String variable, int operator, String value) {
+		this.path = path;
+		this.variable = variable;
+		this.operator = operator;
+		this.value = value;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest req,
+			InterceptingHTTPServletResponse response,
+			HttpServletResponse httpResponse) {
+
+		InterceptingHTTPServletRequest request = (InterceptingHTTPServletRequest)req;
+
+		String uri = request.getRequestURI();
+		if ( ! path.matcher(uri).matches() ) {
+
+			return new DoNothingAction();
+
+		} else {
+
+			String target = null;
+
+			/*
+			 * First check if we're going to be dealing with request parameters
+			 */
+			if ( variable.startsWith( REQUEST_PARAMETERS ) ) {
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EXISTS ) {
+
+					target = variable.substring(REQUEST_PARAMETERS.length());
+
+					if ( request.getParameter(target) != null ) {
+						return new DoNothingAction();
+					}
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_IN_LIST ) {
+
+					/*
+					 * This doesn't make sense. The variable to test is a request parameter
+					 * but the rule is looking for a List. Let the control fall through
+					 * to the bottom where we'll return false.
+					 */
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+
+					/**
+					 * Working with request parameters. If we detect
+					 * simple regex characters, we treat it as a regex.
+					 * Otherwise we treat it as a single parameter.
+					 */
+					target = variable.substring(REQUEST_PARAMETERS.length());
+
+					if ( target.contains("*") || target.contains("?") ) {
+
+						target = target.replaceAll("*", ".*");
+						Pattern p = Pattern.compile(target);
+
+						Enumeration e = request.getParameterNames();
+
+						while(e.hasMoreElements()) {
+							String param = (String)e.nextElement();
+
+							if ( p.matcher(param).matches() ) {
+								String s = request.getParameter(param);
+								if ( ! RuleUtil.testValue(s, value, operator) ) {
+									log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "' parameter='"+param+"'");
+									return new DefaultAction();
+								}
+							}
+						}
+
+					} else {
+
+						String s = request.getParameter(target);
+
+						if ( ! RuleUtil.testValue(s, value, operator) ) {
+							log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "', parameter='"+target+"'");
+							return new DefaultAction();
+						}
+
+					}
+				}
+
+			} else if ( variable.startsWith( REQUEST_HEADERS ) ) {
+
+				/**
+				 * Do the same for request headers.
+				 */
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EXISTS ) {
+
+					target = variable.substring(REQUEST_HEADERS.length());
+
+					if ( request.getHeader(target) != null ) {
+						return new DoNothingAction();
+					}
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_IN_LIST ) {
+
+					/*
+					 * This doesn't make sense. The variable to test is a request header
+					 * but the rule is looking for a List. Let the control fall through
+					 * to the bottom where we'll return false.
+					 */
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+
+					target = variable.substring(REQUEST_HEADERS.length());
+
+					if ( target.contains("*") || target.contains("?") ) {
+
+						target = target.replaceAll("*", ".*");
+						Pattern p = Pattern.compile(target);
+
+						Enumeration e = request.getHeaderNames();
+
+						while(e.hasMoreElements()) {
+							String header = (String)e.nextElement();
+							if ( p.matcher(header).matches() ) {
+								String s = request.getHeader(header);
+								if ( ! RuleUtil.testValue(s, value, operator) ) {
+									log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "', header='"+header+"'");
+									return new DefaultAction();
+								}
+							}
+						}
+
+						return new DoNothingAction();
+
+					} else {
+
+						String s = request.getHeader(target);
+
+						if ( s == null || ! RuleUtil.testValue(s, value, operator) ) {
+							log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "', header='"+target+"'");
+							return new DefaultAction();
+						}
+
+						return new DoNothingAction();
+
+					}
+
+				}
+
+			} else if ( variable.startsWith(SESSION_ATTRIBUTES) ) {
+
+				/**
+				 * Do the same for session attributes. Can't possibly match
+				 * ANY rule if there is no session object.
+				 */
+				if ( request.getSession(false) == null ) {
+					return new DefaultAction();
+				}
+
+				target = variable.substring(SESSION_ATTRIBUTES.length()+1);
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_IN_LIST ) {
+
+					/*
+					 * Want to check if the List/Enumeration/whatever stored
+					 * in "target" contains the value in "value".
+					 */
+
+					Object o = request.getSession(false).getAttribute(target);
+
+					if ( o instanceof Collection ) {
+						if ( RuleUtil.isInList((Collection)o, value) ) {
+							return new DoNothingAction();
+						} else {
+							log(request, "MustMatch rule failed - looking for value='" + value + "', in session Collection attribute '" + target + "']");
+							return new DefaultAction();
+						}
+					} else if ( o instanceof Map ) {
+						if ( RuleUtil.isInList((Map)o, value) ) {
+							return new DoNothingAction();
+						} else {
+							log(request, "MustMatch rule failed - looking for value='" + value + "', in session Map attribute '" + target + "']");
+							return new DefaultAction();
+						}
+					} else if ( o instanceof Enumeration ) {
+						if ( RuleUtil.isInList((Enumeration)o, value) ) {
+							return new DoNothingAction();
+						} else {
+							log(request, "MustMatch rule failed - looking for value='" + value + "', in session Enumeration attribute '" + target + "']");
+							return new DefaultAction();
+						}
+					}
+
+					/*
+					 * The attribute was not a common list-type of Java object s
+					 * let the control fall through to the bottom where it will
+					 * fail.
+					 */
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EXISTS) {
+
+					Object o = request.getSession(false).getAttribute(target);
+
+					if ( o != null ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "MustMatch rule failed - couldn't find required session attribute='" + target + "'");
+						return new DefaultAction();
+					}
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+
+					if ( target.contains("*") || target.contains("?") ) {
+
+						target = target.replaceAll("\\*", ".*");
+						Pattern p = Pattern.compile(target);
+
+						Enumeration e = request.getSession(false).getAttributeNames();
+
+						while(e.hasMoreElements()) {
+
+							String attr = (String)e.nextElement();
+
+							if (p.matcher(attr).matches() ) {
+
+								Object o = request.getSession(false).getAttribute(attr);
+
+								if ( ! RuleUtil.testValue((String)o, value, operator) ) {
+									log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', session attribute='" + attr + "', attribute value='"+(String)o+"'");
+									return new DefaultAction();
+								} else {
+									return new DoNothingAction();
+								}
+							}
+						}
+
+					} else {
+
+						Object o = request.getSession(false).getAttribute(target);
+
+						if ( ! RuleUtil.testValue((String)o, value, operator) ) {
+							log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', session attribute='" + target + "', attribute value='"+(String)o+"'");
+							return new DefaultAction();
+						} else {
+							return new DoNothingAction();
+						}
+
+					}
+
+				}
+
+			} else if ( variable.equals( REQUEST_URI ) ) {
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+					if ( RuleUtil.testValue(request.getRequestURI(), value, operator) ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "MustMatch rule on request URI failed (operator="+operator+"), requestURI='" + request.getRequestURI() + "', value='" + value+ "'");
+						return new DefaultAction();
+					}
+				}
+
+				/*
+				 * Any other operator doesn't make sense.
+				 */
+
+			} else if ( variable.equals( REQUEST_URL ) ) {
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+					if ( RuleUtil.testValue(request.getRequestURL().toString(), value, operator) ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "MustMatch rule on request URL failed (operator="+operator+"), requestURL='" + request.getRequestURL() + "', value='" + value+ "'");
+						return new DefaultAction();
+					}
+				}
+
+				/*
+				 * Any other operator doesn't make sense.
+				 */
+			}
+
+		}
+
+		log(request, "MustMatch rule failed close on URL '" + request.getRequestURL() + "'");
+		return new DefaultAction();
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/PathExtensionRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/PathExtensionRule.java.svn-base
new file mode 100644
index 0000000..f27fc4f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/PathExtensionRule.java.svn-base
@@ -0,0 +1,60 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <restrict-extension> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class PathExtensionRule extends Rule {
+
+	private Pattern allow;
+	private Pattern deny;
+
+	public PathExtensionRule (String id, Pattern allow, Pattern deny) {
+		this.allow = allow;
+		this.deny = deny;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		if ( allow != null && allow.matcher(request.getRequestURI()).matches() ) {
+			return new DoNothingAction();
+		} else if ( deny != null && deny.matcher(request.getRequestURI()).matches() ) {
+
+			log(request, "Disallowed extension pattern '" + deny.pattern() + "' found on URI '" + request.getRequestURI() + "'");
+
+			return new DefaultAction();
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/ReplaceContentRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/ReplaceContentRule.java.svn-base
new file mode 100644
index 0000000..f8c996a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/ReplaceContentRule.java.svn-base
@@ -0,0 +1,113 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <dynamic-insertion> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class ReplaceContentRule extends Rule {
+
+	private Pattern pattern;
+	private String replacement;
+	private Pattern contentType;
+	private Pattern path;
+	
+	public ReplaceContentRule(String id, Pattern pattern, String replacement, Pattern contentType, Pattern path) {
+		this.pattern = pattern;
+		this.replacement = replacement;
+		this.path = path;
+		this.contentType = contentType;
+		setId(id);
+	}
+
+	/*
+	 * Use regular expressions with capturing parentheses to perform replacement.
+	 */
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * First early fail: if the URI doesn't match the paths we're interested in.
+		 */
+		String uri = request.getRequestURI();
+		if ( path != null && ! path.matcher(uri).matches() ) {
+			return new DoNothingAction();
+		}
+		
+		/*
+		 * Second early fail: if the content type is one we'd like to search for output patterns.
+		 */
+
+		if ( contentType != null ) {
+			if ( response.getContentType() != null && ! contentType.matcher(response.getContentType()).matches() ) {
+				return new DoNothingAction();
+			}
+		}
+
+		byte[] bytes = null;
+
+		try {
+			bytes = response.getInterceptingServletOutputStream().getResponseBytes();
+		} catch (IOException ioe) {
+			log(request,"Error matching pattern '" + pattern.pattern() + "', IOException encountered (possibly too large?): " + ioe.getMessage() + " (in response to URL: '" + request.getRequestURL() + "')");
+			return new DoNothingAction(); // yes this is a fail open!
+		}
+
+		
+		try {
+
+			String s = new String(bytes,response.getCharacterEncoding());
+
+			Matcher m = pattern.matcher(s);
+			String canary = m.replaceAll(replacement);
+			
+			try {
+				
+				if ( ! s.equals(canary) ) {
+					response.getInterceptingServletOutputStream().setResponseBytes(canary.getBytes(response.getCharacterEncoding()));
+					logger.debug(Logger.SECURITY_SUCCESS, "Successfully replaced pattern '" + pattern.pattern() + "' on response to URL '" + request.getRequestURL() + "'");
+				}
+				
+			} catch (IOException ioe) {
+				logger.error(Logger.SECURITY_FAILURE, "Failed to replace pattern '" + pattern.pattern() + "' on response to URL '" + request.getRequestURL() + "' due to [" + ioe.getMessage() + "]");
+			}
+
+		} catch(UnsupportedEncodingException uee) {
+			logger.error(Logger.SECURITY_FAILURE, "Failed to replace pattern '" + pattern.pattern() + "' on response to URL '" + request.getRequestURL() + "' due to [" + uee.getMessage() + "]");
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RestrictContentTypeRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RestrictContentTypeRule.java.svn-base
new file mode 100644
index 0000000..d454469
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RestrictContentTypeRule.java.svn-base
@@ -0,0 +1,70 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <dynamic-insertion> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class RestrictContentTypeRule extends Rule {
+
+	private Pattern allow;
+	private Pattern deny;
+
+	public RestrictContentTypeRule(String id, Pattern allow, Pattern deny) {
+		this.allow = allow;
+		this.deny = deny;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/* can't check content type if it's not available */
+		if ( request.getContentType() == null ) {
+			return new DoNothingAction();
+		}
+
+		if ( allow != null ) {
+			if ( allow.matcher(request.getContentType()).matches() ) {
+				return new DoNothingAction();
+			}
+			log(request, "Disallowed content type based on allow pattern '" + allow.pattern() + "' found on URI '" + request.getRequestURI() + "' (value was '" + request.getContentType() +"')");
+		} else if ( deny != null ) {
+			if ( ! deny.matcher(request.getContentType()).matches() ) {
+				return new DoNothingAction();
+			}
+			log(request, "Disallowed content type based on deny pattern '" + deny.pattern() + "' found on URI '" + request.getRequestURI() + "' (value was '" + request.getContentType() + ")'");
+		}
+
+
+		return new DefaultAction();
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RestrictUserAgentRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RestrictUserAgentRule.java.svn-base
new file mode 100644
index 0000000..33dfc17
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RestrictUserAgentRule.java.svn-base
@@ -0,0 +1,80 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.BlockAction;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <restrict-user-agent> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class RestrictUserAgentRule extends Rule {
+
+	private static final String USER_AGENT_HEADER = "User-Agent";
+
+	private Pattern allow;
+	private Pattern deny;
+
+	public RestrictUserAgentRule(String id, Pattern allow, Pattern deny) {
+		this.allow = allow;
+		this.deny = deny;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request, InterceptingHTTPServletResponse response, HttpServletResponse httpResponse) {
+		
+		String userAgent = request.getHeader( USER_AGENT_HEADER );
+		
+		if ( userAgent == null ) userAgent="";
+		
+		if ( allow != null ) {
+			if ( allow.matcher(userAgent).matches() ) {
+				return new DoNothingAction();
+			}
+		} else if ( deny != null ) {
+			if ( ! deny.matcher(userAgent).matches() ) {
+				return new DoNothingAction();
+			}
+		}
+
+		log(request, "Disallowed user agent pattern '" + deny.pattern() + "' found in user agent '" + request.getHeader(USER_AGENT_HEADER) + "'");
+	
+		/*
+		 * If we don't force this to "block", the user will be in an infinite loop, possibly
+		 * eating our bandwidth, and in the case of a dread false positive, really piss them
+		 * off.
+		 * 
+		 * Better to just reject.
+		 */
+		if ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION == AppGuardianConfiguration.REDIRECT ) {
+			return new BlockAction();
+		}
+
+		return new DefaultAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/Rule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/Rule.java.svn-base
new file mode 100644
index 0000000..fe57c49
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/Rule.java.svn-base
@@ -0,0 +1,55 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the base class for the WAF rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public abstract class Rule {
+
+	protected String id = "(no rule ID)";
+	protected static Logger logger = ESAPI.getLogger(Rule.class);
+
+	public abstract Action check( HttpServletRequest request, InterceptingHTTPServletResponse response, HttpServletResponse httpResponse );
+
+	public void log( HttpServletRequest request, String message ) {
+		logger.warning(Logger.SECURITY_FAILURE,"[IP=" + request.getRemoteAddr() +
+				",Rule=" + this.getClass().getSimpleName() + ",ID="+id+"] " + message);
+	}
+
+	protected void setId(String id) {
+		if ( id == null || "".equals(id) )
+			return;
+
+		this.id = id;
+	}
+
+	public String toString() {
+		return "Rule:" + this.getClass().getName();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RuleUtil.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RuleUtil.java.svn-base
new file mode 100644
index 0000000..4520101
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/RuleUtil.java.svn-base
@@ -0,0 +1,151 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+
+/**
+ * This is a small utility class for use by Rule subclasses.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class RuleUtil {
+
+	public static boolean isInList(Map m, String s) {
+
+		Iterator it = m.keySet().iterator();
+
+		while( it.hasNext() ) {
+			String key = (String)it.next();
+			if ( key.equals(s) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public static boolean isInList(Collection c, String s) {
+
+		Iterator it = c.iterator();
+
+		while(it.hasNext()) {
+
+			Object o = it.next();
+
+			if ( o instanceof String ) {
+
+				if ( s.equals((String)o)) {
+					return true;
+				}
+
+			} else if ( o instanceof Integer ) {
+
+				try {
+					if ( Integer.parseInt(s) == ((Integer)o).intValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Long ) {
+
+				try {
+					if ( Long.parseLong(s) == ((Long)o).longValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Double ) {
+
+				try {
+					if ( Double.parseDouble(s) == ((Double)o).doubleValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+			}
+
+		}
+
+		return false;
+	}
+
+	/*
+	 * Enumeration
+	 */
+	public static boolean isInList(Enumeration en, String s) {
+
+		for(; en.hasMoreElements();) {
+
+			Object o = en.nextElement();
+
+			if ( o instanceof String ) {
+
+				if ( s.equals((String)o)) {
+					return true;
+				}
+
+			} else if ( o instanceof Integer ) {
+
+				try {
+					if ( Integer.parseInt(s) == ((Integer)o).intValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Long ) {
+
+				try {
+					if ( Long.parseLong(s) == ((Long)o).longValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Double ) {
+
+				try {
+					if ( Double.parseDouble(s) == ((Double)o).doubleValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+			}
+
+		}
+
+		return false;
+	}
+
+	public static boolean testValue(String s, String test, int operator) {
+
+		switch(operator) {
+			case AppGuardianConfiguration.OPERATOR_EQ:
+
+				return test.equals(s);
+
+			case AppGuardianConfiguration.OPERATOR_CONTAINS:
+
+				return test.contains(s);
+
+		}
+
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/SimpleVirtualPatchRule.java.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/SimpleVirtualPatchRule.java.svn-base
new file mode 100644
index 0000000..f7886cf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/SimpleVirtualPatchRule.java.svn-base
@@ -0,0 +1,139 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <virtual-patch> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class SimpleVirtualPatchRule extends Rule {
+
+	private static final String REQUEST_PARAMETERS = "request.parameters.";
+	private static final String REQUEST_HEADERS = "request.headers.";
+
+	private Pattern path;
+	private String variable;
+	private Pattern valid;
+	private String message;
+
+	public SimpleVirtualPatchRule(String id, Pattern path, String variable, Pattern valid, String message) {
+		setId(id);
+		this.path = path;
+		this.variable = variable;
+		this.valid = valid;
+		this.message = message;
+	}
+
+	public Action check(HttpServletRequest req,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		InterceptingHTTPServletRequest request = (InterceptingHTTPServletRequest)req;
+
+		String uri = request.getRequestURI();
+		if ( ! path.matcher(uri).matches() ) {
+
+			return new DoNothingAction();
+
+		} else {
+
+			/*
+			 * Decide which parameters/headers to act on.
+			 */
+			String target = null;
+			Enumeration en = null;
+			boolean parameter = true;
+
+			if ( variable.startsWith(REQUEST_PARAMETERS)) {
+
+				target = variable.substring(REQUEST_PARAMETERS.length());
+				en = request.getParameterNames();
+
+			} else if ( variable.startsWith(REQUEST_HEADERS) ) {
+
+				parameter = false;
+				target = variable.substring(REQUEST_HEADERS.length());
+				en = request.getHeaderNames();
+
+			} else {
+				log(request, "Patch failed (improperly configured variable '" + variable + "')");
+				return new DefaultAction();
+			}
+
+			/*
+			 * If it contains a regex character, it's a regex. Loop through elements and grab any matches.
+			 */
+			if ( target.contains("*") || target.contains("?") ) {
+
+				target = target.replaceAll("\\*", ".*");
+				Pattern p = Pattern.compile(target);
+				while (en.hasMoreElements() ) {
+					String s = (String)en.nextElement();
+					String value = null;
+					if ( p.matcher(s).matches() ) {
+						if ( parameter ) {
+							value = request.getDictionaryParameter(s);
+						} else {
+							value = request.getHeader(s);
+						}
+						if ( value != null && ! valid.matcher(value).matches() ) {
+							log(request, "Virtual patch tripped on variable '" + variable + "' (specifically '" + s + "'). User input was '" + value + "' and legal pattern was '" + valid.pattern() + "': " + message);
+							return new DefaultAction();
+						}
+					}
+				}
+				
+				return new DoNothingAction();
+
+			} else {
+
+				if ( parameter ) {
+					String value = request.getDictionaryParameter(target);
+					if ( value == null || valid.matcher(value).matches() ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "Virtual patch tripped on parameter '" + target + "'. User input was '" + value + "' and legal pattern was '" + valid.pattern() + "': " + message);
+						return new DefaultAction();
+					}
+				} else {
+					String value = request.getHeader(target);
+					if ( value == null || valid.matcher(value).matches() ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "Virtual patch tripped on header '" + target + "'. User input was '" + value + "' and legal pattern was '" + valid.pattern() + "': " + message);
+						return new DefaultAction();
+					}
+				}
+			}
+
+		}
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/package.html.svn-base b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..133148e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/.svn/text-base/package.html.svn-base
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains all of the Rule subclasses that correspond to policy file entries. Each 
+class contains the logic for enforcing its rule. 
+ 
+</body>
+</html>
diff --git a/src/main/java/org/owasp/esapi/waf/rules/AddHTTPOnlyFlagRule.java b/src/main/java/org/owasp/esapi/waf/rules/AddHTTPOnlyFlagRule.java
new file mode 100644
index 0000000..6a382fa
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/AddHTTPOnlyFlagRule.java
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <add-http-only-flag> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AddHTTPOnlyFlagRule extends Rule {
+
+	private List<Pattern> name;
+
+	public AddHTTPOnlyFlagRule(String id, List<Pattern> name) {
+		setId(id);
+		this.name = name;
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		DoNothingAction action = new DoNothingAction();
+
+		return action;
+	}
+
+	public boolean doesCookieMatch(String cookieName) {
+
+		for(int i=0;i<name.size();i++) {
+			Pattern p = name.get(i);
+			if ( p.matcher(cookieName).matches() ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/AddHeaderRule.java b/src/main/java/org/owasp/esapi/waf/rules/AddHeaderRule.java
new file mode 100644
index 0000000..d235b59
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/AddHeaderRule.java
@@ -0,0 +1,92 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <add-header> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AddHeaderRule extends Rule {
+
+	private String header;
+	private String value;
+	private Pattern path;
+	private List<Object> exceptions;
+
+	public AddHeaderRule(String id, String header, String value, Pattern path, List<Object> exceptions) {
+		setId(id);
+		this.header = header;
+		this.value = value;
+		this.path = path;
+		this.exceptions = exceptions;
+	}
+
+	public Action check(
+			HttpServletRequest request, 
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		DoNothingAction action = new DoNothingAction();
+
+		if ( path.matcher(request.getRequestURI()).matches() ) {
+
+			for(int i=0;i<exceptions.size();i++) {
+
+				Object o = exceptions.get(i);
+
+				if ( o instanceof String ) {
+					if ( request.getRequestURI().equals((String)o)) {
+						action.setFailed(false);
+						action.setActionNecessary(false);
+						return action;
+					}
+				} else if ( o instanceof Pattern ) {
+					if ( ((Pattern)o).matcher(request.getRequestURI()).matches() ) {
+						action.setFailed(false);
+						action.setActionNecessary(false);
+						return action;					
+					}
+				}
+
+			}
+
+
+			action.setFailed(true);
+			action.setActionNecessary(false);
+
+			if ( response != null ) {
+				response.setHeader(header, value);
+			} else {
+				httpResponse.setHeader(header, value);
+			}
+
+		}
+
+		return action;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/AddSecureFlagRule.java b/src/main/java/org/owasp/esapi/waf/rules/AddSecureFlagRule.java
new file mode 100644
index 0000000..6dabe53
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/AddSecureFlagRule.java
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <add-secure-flag> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AddSecureFlagRule extends Rule {
+
+	private List<Pattern> name;
+
+	public AddSecureFlagRule(String id, List<Pattern> name) {
+		this.name = name;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+		
+		DoNothingAction action = new DoNothingAction();
+
+		return action;
+	}
+
+	public boolean doesCookieMatch(String cookieName) {
+
+		for(int i=0;i<name.size();i++) {
+			Pattern p = name.get(i);
+			if ( p.matcher(cookieName).matches() ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/AuthenticatedRule.java b/src/main/java/org/owasp/esapi/waf/rules/AuthenticatedRule.java
new file mode 100644
index 0000000..747a0f6
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/AuthenticatedRule.java
@@ -0,0 +1,92 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <authentication-rules> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class AuthenticatedRule extends Rule {
+
+	private String sessionAttribute;
+	private Pattern path;
+	private List<Object> exceptions;
+
+	public AuthenticatedRule(String id, String sessionAttribute, Pattern path, List<Object> exceptions) {
+		this.sessionAttribute = sessionAttribute;
+		this.path = path;
+		this.exceptions = exceptions;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		HttpSession session = request.getSession();
+		String uri = request.getRequestURI();
+
+		if ( path != null && ! path.matcher(uri).matches() ) {
+			return new DoNothingAction();
+		}
+
+		if ( session != null && session.getAttribute(sessionAttribute) != null ) {
+
+			return new DoNothingAction();
+
+		} else { /* check if it's one of the exceptions */
+
+			Iterator<Object> it = exceptions.iterator();
+
+			while(it.hasNext()) {
+				Object o = it.next();
+				if ( o instanceof Pattern ) {
+
+					Pattern p = (Pattern)o;
+					if ( p.matcher(uri).matches() ) {
+						return new DoNothingAction();
+					}
+
+				} else if ( o instanceof String ) {
+
+					if ( uri.equals((String)o)) {
+						return new DoNothingAction();
+					}
+
+				}
+			}
+		}
+
+		log(request, "User requested unauthenticated access to URI '" + request.getRequestURI() + "' [querystring="+request.getQueryString()+"]");
+
+		return new DefaultAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java b/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java
new file mode 100644
index 0000000..f54589f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java
@@ -0,0 +1,116 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+import bsh.EvalError;
+import bsh.Interpreter;
+
+/**
+ * This is the Rule subclass executed for <bean-shell-script> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class BeanShellRule extends Rule {
+
+	private Interpreter i;
+	private String script;
+	private Pattern path;
+	
+	public BeanShellRule(String fileLocation, String id, Pattern path) throws IOException, EvalError { 
+		i = new Interpreter();
+		i.set("logger", logger);
+		this.script = getFileContents( ESAPI.securityConfiguration().getResourceFile(fileLocation));
+		this.id = id;
+		this.path = path;
+	}
+	
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * Early fail: if the URL doesn't match one we're interested in.
+		 */
+		
+		if ( path != null && ! path.matcher(request.getRequestURI()).matches() ) {
+			return new DoNothingAction();
+		}
+		
+		/*
+		 * Run the beanshell that we've already parsed
+		 * and pre-compiled. Populate the "request"
+		 * and "response" objects so the script has
+		 * access to the same variables we do here.
+		 */
+		
+		try {
+		
+			Action a = null;
+			
+			i.set("action", a);
+			i.set("request", request);
+			
+			if ( response != null ) {
+				i.set("response", response);	
+			} else {
+				i.set("response", httpResponse);
+			}
+
+			i.set("session", request.getSession());
+			i.eval(script);
+
+			a = (Action)i.get("action");
+	
+			if ( a != null ) {
+				return a;
+			}
+			
+		} catch (EvalError e) {
+			log(request,"Error running custom beanshell rule (" + id + ") - " + e.getMessage());
+		}
+	
+		return new DoNothingAction();
+	}
+	
+	private String getFileContents(File f) throws IOException {
+		
+		FileReader fr = new FileReader(f);
+		StringBuffer sb = new StringBuffer();
+		String line;
+		BufferedReader br = new BufferedReader(fr);
+		
+		while( (line=br.readLine()) != null ) {
+			sb.append(line + System.getProperty("line.separator"));
+		}
+		
+		return sb.toString();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/DetectOutboundContentRule.java b/src/main/java/org/owasp/esapi/waf/rules/DetectOutboundContentRule.java
new file mode 100644
index 0000000..96fe3c1
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/DetectOutboundContentRule.java
@@ -0,0 +1,116 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <detect-content> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class DetectOutboundContentRule extends Rule {
+
+	private Pattern contentType;
+	private Pattern pattern;
+	private Pattern uri;
+	
+	public DetectOutboundContentRule(String id, Pattern contentType, Pattern pattern, Pattern uri) {
+		this.contentType = contentType;
+		this.pattern = pattern;
+		this.uri = uri;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * Early fail: if URI doesn't match.
+		 */
+		if ( uri != null && ! uri.matcher(request.getRequestURI()).matches() ) {
+			return new DoNothingAction(); 
+		}
+
+		/*
+		 * Early fail: if the content type is one we'd like to search for output patterns.
+		 */
+
+		String inboundContentType;
+		String charEnc;
+		
+		if ( response != null ) {
+			if ( response.getContentType() == null ) {
+				response.setContentType(AppGuardianConfiguration.DEFAULT_CONTENT_TYPE);
+			}
+			inboundContentType = response.getContentType();
+			charEnc = response.getCharacterEncoding();
+			
+		} else {
+			if ( httpResponse.getContentType() == null ) {
+				httpResponse.setContentType(AppGuardianConfiguration.DEFAULT_CONTENT_TYPE);
+			}
+			inboundContentType = httpResponse.getContentType();
+			charEnc = httpResponse.getCharacterEncoding();
+		}
+	
+		if ( contentType.matcher(inboundContentType).matches() ) {
+			/*
+			 * Depending on the encoding, search through the bytes
+			 * for the pattern.
+			 */
+			try {
+
+				byte[] bytes = null;
+				
+				try {
+					bytes = response.getInterceptingServletOutputStream().getResponseBytes();
+				} catch (IOException ioe) {
+					log(request,"Error matching pattern '" + pattern.pattern() + "', IOException encountered (possibly too large?): " + ioe.getMessage() + " (in response to URL: '" + request.getRequestURL() + "')");
+					return new DoNothingAction(); // yes this is a fail open!
+				}
+
+				String s = new String(bytes,charEnc);
+
+				if ( pattern.matcher(s).matches() ) {
+
+					log(request,"Content pattern '" + pattern.pattern() + "' was found in response to URL: '" + request.getRequestURL() + "'");
+					return new DefaultAction();
+
+				}
+
+			} catch (UnsupportedEncodingException uee) {
+				log(request,"Content pattern '" + pattern.pattern() + "' could not be found due to encoding error: " + uee.getMessage());
+			}
+		}
+
+		return new DoNothingAction();
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/EnforceHTTPSRule.java b/src/main/java/org/owasp/esapi/waf/rules/EnforceHTTPSRule.java
new file mode 100644
index 0000000..9da25bd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/EnforceHTTPSRule.java
@@ -0,0 +1,95 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.actions.RedirectAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <enforce-https> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class EnforceHTTPSRule extends Rule {
+
+	private Pattern path;
+	private List<Object> exceptions;
+	private String action;
+
+	/*
+	 * action = [ redirect | block ] [=default (redirect will redirect to error page]
+	 */
+
+	public EnforceHTTPSRule(String id, Pattern path, List<Object> exceptions, String action) {
+		this.path = path;
+		this.exceptions = exceptions;
+		this.action = action;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		if ( ! request.isSecure() ) {
+
+			if ( path.matcher(request.getRequestURI()).matches() ) {
+
+				Iterator<Object> it = exceptions.iterator();
+
+				while(it.hasNext()){
+
+					Object o = it.next();
+
+					if ( o instanceof String ) {
+						if ( ((String)o).equalsIgnoreCase(request.getRequestURI()) ) {
+							return new DoNothingAction();
+						}
+					} else if ( o instanceof Pattern ) {
+						if ( ((Pattern)o).matcher(request.getRequestURI()).matches() ) {
+							return new DoNothingAction();
+						}
+					}
+
+				}
+
+				log(request,"Insecure request to resource detected in URL: '" + request.getRequestURL() + "'");
+
+				if ( "redirect".equals(action) ) {
+					RedirectAction ra = new RedirectAction();
+					ra.setRedirectURL(request.getRequestURL().toString().replaceFirst("http", "https"));
+					return ra;
+				}
+
+				return new DefaultAction();
+
+			}
+		}
+
+		return new DoNothingAction();
+
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/GeneralAttackSignatureRule.java b/src/main/java/org/owasp/esapi/waf/rules/GeneralAttackSignatureRule.java
new file mode 100644
index 0000000..1c53550
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/GeneralAttackSignatureRule.java
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <general-attack-signature> rules, which 
+ * are not currently implemented.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class GeneralAttackSignatureRule extends Rule {
+
+	private Pattern signature;
+
+	public GeneralAttackSignatureRule(String id, Pattern signature) {
+		this.signature = signature;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest req,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		InterceptingHTTPServletRequest request = (InterceptingHTTPServletRequest)req;
+		Enumeration e = request.getParameterNames();
+
+		while(e.hasMoreElements()) {
+			String param = (String)e.nextElement();
+			if ( signature.matcher(request.getDictionaryParameter(param)).matches() ) {
+				log(request,"General attack signature detected in parameter '" + param + "' value '" + request.getDictionaryParameter(param) + "'");
+				return new DefaultAction();
+			}
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/HTTPMethodRule.java b/src/main/java/org/owasp/esapi/waf/rules/HTTPMethodRule.java
new file mode 100644
index 0000000..5aa132f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/HTTPMethodRule.java
@@ -0,0 +1,78 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <restrict-method> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class HTTPMethodRule extends Rule {
+
+	private Pattern allowedMethods;
+	private Pattern deniedMethods;
+	private Pattern path;
+
+	public HTTPMethodRule(String id, Pattern allowedMethods, Pattern deniedMethods, Pattern path) {
+		this.allowedMethods = allowedMethods;
+		this.deniedMethods = deniedMethods;
+		this.path = path;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * If no path is specified, apply rule globally.
+		 */
+		String uri = request.getRequestURI();
+		String method = request.getMethod();
+
+		if ( path == null || path.matcher(uri).matches() ) {
+			/*
+			 *	Order allow, deny.
+			 */
+
+			if ( allowedMethods != null && allowedMethods.matcher(method).matches() ) {
+				return new DoNothingAction();
+			} else if ( allowedMethods != null ) {
+				log(request,"Disallowed HTTP method '" + request.getMethod() + "' found for URL: " + request.getRequestURL());
+				return new DefaultAction();
+			}
+
+			if ( deniedMethods != null && deniedMethods.matcher(method).matches() ) {
+				log(request,"Disallowed HTTP method '" + request.getMethod() + "' found for URL: " + request.getRequestURL());
+				return new DefaultAction();
+			}
+
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/IPRule.java b/src/main/java/org/owasp/esapi/waf/rules/IPRule.java
new file mode 100644
index 0000000..e6a15c8
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/IPRule.java
@@ -0,0 +1,79 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <detect-source-ip> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class IPRule extends Rule {
+
+	private Pattern allowedIP;
+	private String exactPath;
+	private Pattern path;
+	private boolean useExactPath = false;
+	private String ipHeader;
+
+	public IPRule(String id, Pattern allowedIP, Pattern path, String ipHeader) {
+		this.allowedIP = allowedIP;
+		this.path = path;
+		this.useExactPath = false;
+		this.ipHeader = ipHeader;
+		setId(id);
+	}
+
+	public IPRule(String id, Pattern allowedIP, String exactPath) {
+		this.path = null;
+		this.exactPath = exactPath;
+		this.useExactPath = true;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		String uri = request.getRequestURI();
+
+		if ( (!useExactPath && path.matcher(uri).matches()) ||
+			 ( useExactPath && exactPath.equals(uri)) ) {
+			
+			String sourceIP = request.getRemoteAddr() + "";
+			
+			if ( ipHeader != null ) {
+				sourceIP = request.getHeader(ipHeader);
+			}
+			
+			if ( ! allowedIP.matcher(sourceIP).matches() ) {
+				log(request, "IP not allowed to access URI '" + uri + "'");
+				return new DefaultAction();
+			}
+		}
+
+		return new DoNothingAction();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/MustMatchRule.java b/src/main/java/org/owasp/esapi/waf/rules/MustMatchRule.java
new file mode 100644
index 0000000..44e09cd
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/MustMatchRule.java
@@ -0,0 +1,336 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <must-match> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class MustMatchRule extends Rule {
+
+	private static final String REQUEST_PARAMETERS = "request.parameters.";
+	private static final String REQUEST_HEADERS = "request.headers.";
+	private static final String REQUEST_URI = "request.uri";
+	private static final String REQUEST_URL = "request.url";
+	private static final String SESSION_ATTRIBUTES = "session.";
+
+	private Pattern path;
+	private String variable;
+	private int operator;
+	private String value;
+
+	public MustMatchRule(String id, Pattern path, String variable, int operator, String value) {
+		this.path = path;
+		this.variable = variable;
+		this.operator = operator;
+		this.value = value;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest req,
+			InterceptingHTTPServletResponse response,
+			HttpServletResponse httpResponse) {
+
+		InterceptingHTTPServletRequest request = (InterceptingHTTPServletRequest)req;
+
+		String uri = request.getRequestURI();
+		if ( ! path.matcher(uri).matches() ) {
+
+			return new DoNothingAction();
+
+		} else {
+
+			String target = null;
+
+			/*
+			 * First check if we're going to be dealing with request parameters
+			 */
+			if ( variable.startsWith( REQUEST_PARAMETERS ) ) {
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EXISTS ) {
+
+					target = variable.substring(REQUEST_PARAMETERS.length());
+
+					if ( request.getParameter(target) != null ) {
+						return new DoNothingAction();
+					}
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_IN_LIST ) {
+
+					/*
+					 * This doesn't make sense. The variable to test is a request parameter
+					 * but the rule is looking for a List. Let the control fall through
+					 * to the bottom where we'll return false.
+					 */
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+
+					/**
+					 * Working with request parameters. If we detect
+					 * simple regex characters, we treat it as a regex.
+					 * Otherwise we treat it as a single parameter.
+					 */
+					target = variable.substring(REQUEST_PARAMETERS.length());
+
+					if ( target.contains("*") || target.contains("?") ) {
+
+						target = target.replaceAll("*", ".*");
+						Pattern p = Pattern.compile(target);
+
+						Enumeration e = request.getParameterNames();
+
+						while(e.hasMoreElements()) {
+							String param = (String)e.nextElement();
+
+							if ( p.matcher(param).matches() ) {
+								String s = request.getParameter(param);
+								if ( ! RuleUtil.testValue(s, value, operator) ) {
+									log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "' parameter='"+param+"'");
+									return new DefaultAction();
+								}
+							}
+						}
+
+					} else {
+
+						String s = request.getParameter(target);
+
+						if ( ! RuleUtil.testValue(s, value, operator) ) {
+							log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "', parameter='"+target+"'");
+							return new DefaultAction();
+						}
+
+					}
+				}
+
+			} else if ( variable.startsWith( REQUEST_HEADERS ) ) {
+
+				/**
+				 * Do the same for request headers.
+				 */
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EXISTS ) {
+
+					target = variable.substring(REQUEST_HEADERS.length());
+
+					if ( request.getHeader(target) != null ) {
+						return new DoNothingAction();
+					}
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_IN_LIST ) {
+
+					/*
+					 * This doesn't make sense. The variable to test is a request header
+					 * but the rule is looking for a List. Let the control fall through
+					 * to the bottom where we'll return false.
+					 */
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+
+					target = variable.substring(REQUEST_HEADERS.length());
+
+					if ( target.contains("*") || target.contains("?") ) {
+
+						target = target.replaceAll("*", ".*");
+						Pattern p = Pattern.compile(target);
+
+						Enumeration e = request.getHeaderNames();
+
+						while(e.hasMoreElements()) {
+							String header = (String)e.nextElement();
+							if ( p.matcher(header).matches() ) {
+								String s = request.getHeader(header);
+								if ( ! RuleUtil.testValue(s, value, operator) ) {
+									log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "', header='"+header+"'");
+									return new DefaultAction();
+								}
+							}
+						}
+
+						return new DoNothingAction();
+
+					} else {
+
+						String s = request.getHeader(target);
+
+						if ( s == null || ! RuleUtil.testValue(s, value, operator) ) {
+							log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', input='" + s + "', header='"+target+"'");
+							return new DefaultAction();
+						}
+
+						return new DoNothingAction();
+
+					}
+
+				}
+
+			} else if ( variable.startsWith(SESSION_ATTRIBUTES) ) {
+
+				/**
+				 * Do the same for session attributes. Can't possibly match
+				 * ANY rule if there is no session object.
+				 */
+				if ( request.getSession(false) == null ) {
+					return new DefaultAction();
+				}
+
+				target = variable.substring(SESSION_ATTRIBUTES.length()+1);
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_IN_LIST ) {
+
+					/*
+					 * Want to check if the List/Enumeration/whatever stored
+					 * in "target" contains the value in "value".
+					 */
+
+					Object o = request.getSession(false).getAttribute(target);
+
+					if ( o instanceof Collection ) {
+						if ( RuleUtil.isInList((Collection)o, value) ) {
+							return new DoNothingAction();
+						} else {
+							log(request, "MustMatch rule failed - looking for value='" + value + "', in session Collection attribute '" + target + "']");
+							return new DefaultAction();
+						}
+					} else if ( o instanceof Map ) {
+						if ( RuleUtil.isInList((Map)o, value) ) {
+							return new DoNothingAction();
+						} else {
+							log(request, "MustMatch rule failed - looking for value='" + value + "', in session Map attribute '" + target + "']");
+							return new DefaultAction();
+						}
+					} else if ( o instanceof Enumeration ) {
+						if ( RuleUtil.isInList((Enumeration)o, value) ) {
+							return new DoNothingAction();
+						} else {
+							log(request, "MustMatch rule failed - looking for value='" + value + "', in session Enumeration attribute '" + target + "']");
+							return new DefaultAction();
+						}
+					}
+
+					/*
+					 * The attribute was not a common list-type of Java object s
+					 * let the control fall through to the bottom where it will
+					 * fail.
+					 */
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EXISTS) {
+
+					Object o = request.getSession(false).getAttribute(target);
+
+					if ( o != null ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "MustMatch rule failed - couldn't find required session attribute='" + target + "'");
+						return new DefaultAction();
+					}
+
+				} else if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+
+					if ( target.contains("*") || target.contains("?") ) {
+
+						target = target.replaceAll("\\*", ".*");
+						Pattern p = Pattern.compile(target);
+
+						Enumeration e = request.getSession(false).getAttributeNames();
+
+						while(e.hasMoreElements()) {
+
+							String attr = (String)e.nextElement();
+
+							if (p.matcher(attr).matches() ) {
+
+								Object o = request.getSession(false).getAttribute(attr);
+
+								if ( ! RuleUtil.testValue((String)o, value, operator) ) {
+									log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', session attribute='" + attr + "', attribute value='"+(String)o+"'");
+									return new DefaultAction();
+								} else {
+									return new DoNothingAction();
+								}
+							}
+						}
+
+					} else {
+
+						Object o = request.getSession(false).getAttribute(target);
+
+						if ( ! RuleUtil.testValue((String)o, value, operator) ) {
+							log(request, "MustMatch rule failed (operator="+operator+"), value='" + value + "', session attribute='" + target + "', attribute value='"+(String)o+"'");
+							return new DefaultAction();
+						} else {
+							return new DoNothingAction();
+						}
+
+					}
+
+				}
+
+			} else if ( variable.equals( REQUEST_URI ) ) {
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+					if ( RuleUtil.testValue(request.getRequestURI(), value, operator) ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "MustMatch rule on request URI failed (operator="+operator+"), requestURI='" + request.getRequestURI() + "', value='" + value+ "'");
+						return new DefaultAction();
+					}
+				}
+
+				/*
+				 * Any other operator doesn't make sense.
+				 */
+
+			} else if ( variable.equals( REQUEST_URL ) ) {
+
+				if ( operator == AppGuardianConfiguration.OPERATOR_EQ || operator == AppGuardianConfiguration.OPERATOR_CONTAINS ) {
+					if ( RuleUtil.testValue(request.getRequestURL().toString(), value, operator) ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "MustMatch rule on request URL failed (operator="+operator+"), requestURL='" + request.getRequestURL() + "', value='" + value+ "'");
+						return new DefaultAction();
+					}
+				}
+
+				/*
+				 * Any other operator doesn't make sense.
+				 */
+			}
+
+		}
+
+		log(request, "MustMatch rule failed close on URL '" + request.getRequestURL() + "'");
+		return new DefaultAction();
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/PathExtensionRule.java b/src/main/java/org/owasp/esapi/waf/rules/PathExtensionRule.java
new file mode 100644
index 0000000..f27fc4f
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/PathExtensionRule.java
@@ -0,0 +1,60 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <restrict-extension> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class PathExtensionRule extends Rule {
+
+	private Pattern allow;
+	private Pattern deny;
+
+	public PathExtensionRule (String id, Pattern allow, Pattern deny) {
+		this.allow = allow;
+		this.deny = deny;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		if ( allow != null && allow.matcher(request.getRequestURI()).matches() ) {
+			return new DoNothingAction();
+		} else if ( deny != null && deny.matcher(request.getRequestURI()).matches() ) {
+
+			log(request, "Disallowed extension pattern '" + deny.pattern() + "' found on URI '" + request.getRequestURI() + "'");
+
+			return new DefaultAction();
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/ReplaceContentRule.java b/src/main/java/org/owasp/esapi/waf/rules/ReplaceContentRule.java
new file mode 100644
index 0000000..f8c996a
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/ReplaceContentRule.java
@@ -0,0 +1,113 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <dynamic-insertion> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class ReplaceContentRule extends Rule {
+
+	private Pattern pattern;
+	private String replacement;
+	private Pattern contentType;
+	private Pattern path;
+	
+	public ReplaceContentRule(String id, Pattern pattern, String replacement, Pattern contentType, Pattern path) {
+		this.pattern = pattern;
+		this.replacement = replacement;
+		this.path = path;
+		this.contentType = contentType;
+		setId(id);
+	}
+
+	/*
+	 * Use regular expressions with capturing parentheses to perform replacement.
+	 */
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/*
+		 * First early fail: if the URI doesn't match the paths we're interested in.
+		 */
+		String uri = request.getRequestURI();
+		if ( path != null && ! path.matcher(uri).matches() ) {
+			return new DoNothingAction();
+		}
+		
+		/*
+		 * Second early fail: if the content type is one we'd like to search for output patterns.
+		 */
+
+		if ( contentType != null ) {
+			if ( response.getContentType() != null && ! contentType.matcher(response.getContentType()).matches() ) {
+				return new DoNothingAction();
+			}
+		}
+
+		byte[] bytes = null;
+
+		try {
+			bytes = response.getInterceptingServletOutputStream().getResponseBytes();
+		} catch (IOException ioe) {
+			log(request,"Error matching pattern '" + pattern.pattern() + "', IOException encountered (possibly too large?): " + ioe.getMessage() + " (in response to URL: '" + request.getRequestURL() + "')");
+			return new DoNothingAction(); // yes this is a fail open!
+		}
+
+		
+		try {
+
+			String s = new String(bytes,response.getCharacterEncoding());
+
+			Matcher m = pattern.matcher(s);
+			String canary = m.replaceAll(replacement);
+			
+			try {
+				
+				if ( ! s.equals(canary) ) {
+					response.getInterceptingServletOutputStream().setResponseBytes(canary.getBytes(response.getCharacterEncoding()));
+					logger.debug(Logger.SECURITY_SUCCESS, "Successfully replaced pattern '" + pattern.pattern() + "' on response to URL '" + request.getRequestURL() + "'");
+				}
+				
+			} catch (IOException ioe) {
+				logger.error(Logger.SECURITY_FAILURE, "Failed to replace pattern '" + pattern.pattern() + "' on response to URL '" + request.getRequestURL() + "' due to [" + ioe.getMessage() + "]");
+			}
+
+		} catch(UnsupportedEncodingException uee) {
+			logger.error(Logger.SECURITY_FAILURE, "Failed to replace pattern '" + pattern.pattern() + "' on response to URL '" + request.getRequestURL() + "' due to [" + uee.getMessage() + "]");
+		}
+
+		return new DoNothingAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/RestrictContentTypeRule.java b/src/main/java/org/owasp/esapi/waf/rules/RestrictContentTypeRule.java
new file mode 100644
index 0000000..d454469
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/RestrictContentTypeRule.java
@@ -0,0 +1,70 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <dynamic-insertion> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class RestrictContentTypeRule extends Rule {
+
+	private Pattern allow;
+	private Pattern deny;
+
+	public RestrictContentTypeRule(String id, Pattern allow, Pattern deny) {
+		this.allow = allow;
+		this.deny = deny;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		/* can't check content type if it's not available */
+		if ( request.getContentType() == null ) {
+			return new DoNothingAction();
+		}
+
+		if ( allow != null ) {
+			if ( allow.matcher(request.getContentType()).matches() ) {
+				return new DoNothingAction();
+			}
+			log(request, "Disallowed content type based on allow pattern '" + allow.pattern() + "' found on URI '" + request.getRequestURI() + "' (value was '" + request.getContentType() +"')");
+		} else if ( deny != null ) {
+			if ( ! deny.matcher(request.getContentType()).matches() ) {
+				return new DoNothingAction();
+			}
+			log(request, "Disallowed content type based on deny pattern '" + deny.pattern() + "' found on URI '" + request.getRequestURI() + "' (value was '" + request.getContentType() + ")'");
+		}
+
+
+		return new DefaultAction();
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/RestrictUserAgentRule.java b/src/main/java/org/owasp/esapi/waf/rules/RestrictUserAgentRule.java
new file mode 100644
index 0000000..33dfc17
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/RestrictUserAgentRule.java
@@ -0,0 +1,80 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.BlockAction;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <restrict-user-agent> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class RestrictUserAgentRule extends Rule {
+
+	private static final String USER_AGENT_HEADER = "User-Agent";
+
+	private Pattern allow;
+	private Pattern deny;
+
+	public RestrictUserAgentRule(String id, Pattern allow, Pattern deny) {
+		this.allow = allow;
+		this.deny = deny;
+		setId(id);
+	}
+
+	public Action check(HttpServletRequest request, InterceptingHTTPServletResponse response, HttpServletResponse httpResponse) {
+		
+		String userAgent = request.getHeader( USER_AGENT_HEADER );
+		
+		if ( userAgent == null ) userAgent="";
+		
+		if ( allow != null ) {
+			if ( allow.matcher(userAgent).matches() ) {
+				return new DoNothingAction();
+			}
+		} else if ( deny != null ) {
+			if ( ! deny.matcher(userAgent).matches() ) {
+				return new DoNothingAction();
+			}
+		}
+
+		log(request, "Disallowed user agent pattern '" + deny.pattern() + "' found in user agent '" + request.getHeader(USER_AGENT_HEADER) + "'");
+	
+		/*
+		 * If we don't force this to "block", the user will be in an infinite loop, possibly
+		 * eating our bandwidth, and in the case of a dread false positive, really piss them
+		 * off.
+		 * 
+		 * Better to just reject.
+		 */
+		if ( AppGuardianConfiguration.DEFAULT_FAIL_ACTION == AppGuardianConfiguration.REDIRECT ) {
+			return new BlockAction();
+		}
+
+		return new DefaultAction();
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/Rule.java b/src/main/java/org/owasp/esapi/waf/rules/Rule.java
new file mode 100644
index 0000000..fe57c49
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/Rule.java
@@ -0,0 +1,55 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import javax.servlet.http.HttpServletRequest;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the base class for the WAF rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public abstract class Rule {
+
+	protected String id = "(no rule ID)";
+	protected static Logger logger = ESAPI.getLogger(Rule.class);
+
+	public abstract Action check( HttpServletRequest request, InterceptingHTTPServletResponse response, HttpServletResponse httpResponse );
+
+	public void log( HttpServletRequest request, String message ) {
+		logger.warning(Logger.SECURITY_FAILURE,"[IP=" + request.getRemoteAddr() +
+				",Rule=" + this.getClass().getSimpleName() + ",ID="+id+"] " + message);
+	}
+
+	protected void setId(String id) {
+		if ( id == null || "".equals(id) )
+			return;
+
+		this.id = id;
+	}
+
+	public String toString() {
+		return "Rule:" + this.getClass().getName();
+	}
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java b/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java
new file mode 100644
index 0000000..4520101
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java
@@ -0,0 +1,151 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.owasp.esapi.waf.configuration.AppGuardianConfiguration;
+
+/**
+ * This is a small utility class for use by Rule subclasses.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class RuleUtil {
+
+	public static boolean isInList(Map m, String s) {
+
+		Iterator it = m.keySet().iterator();
+
+		while( it.hasNext() ) {
+			String key = (String)it.next();
+			if ( key.equals(s) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public static boolean isInList(Collection c, String s) {
+
+		Iterator it = c.iterator();
+
+		while(it.hasNext()) {
+
+			Object o = it.next();
+
+			if ( o instanceof String ) {
+
+				if ( s.equals((String)o)) {
+					return true;
+				}
+
+			} else if ( o instanceof Integer ) {
+
+				try {
+					if ( Integer.parseInt(s) == ((Integer)o).intValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Long ) {
+
+				try {
+					if ( Long.parseLong(s) == ((Long)o).longValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Double ) {
+
+				try {
+					if ( Double.parseDouble(s) == ((Double)o).doubleValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+			}
+
+		}
+
+		return false;
+	}
+
+	/*
+	 * Enumeration
+	 */
+	public static boolean isInList(Enumeration en, String s) {
+
+		for(; en.hasMoreElements();) {
+
+			Object o = en.nextElement();
+
+			if ( o instanceof String ) {
+
+				if ( s.equals((String)o)) {
+					return true;
+				}
+
+			} else if ( o instanceof Integer ) {
+
+				try {
+					if ( Integer.parseInt(s) == ((Integer)o).intValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Long ) {
+
+				try {
+					if ( Long.parseLong(s) == ((Long)o).longValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+
+			} else if ( o instanceof Double ) {
+
+				try {
+					if ( Double.parseDouble(s) == ((Double)o).doubleValue() ) {
+						return true;
+					}
+				} catch (Exception e) {}
+			}
+
+		}
+
+		return false;
+	}
+
+	public static boolean testValue(String s, String test, int operator) {
+
+		switch(operator) {
+			case AppGuardianConfiguration.OPERATOR_EQ:
+
+				return test.equals(s);
+
+			case AppGuardianConfiguration.OPERATOR_CONTAINS:
+
+				return test.contains(s);
+
+		}
+
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/SimpleVirtualPatchRule.java b/src/main/java/org/owasp/esapi/waf/rules/SimpleVirtualPatchRule.java
new file mode 100644
index 0000000..f7886cf
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/SimpleVirtualPatchRule.java
@@ -0,0 +1,139 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf.rules;
+
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.waf.actions.Action;
+import org.owasp.esapi.waf.actions.DefaultAction;
+import org.owasp.esapi.waf.actions.DoNothingAction;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest;
+import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse;
+
+/**
+ * This is the Rule subclass executed for <virtual-patch> rules.
+ * @author Arshan Dabirsiaghi
+ *
+ */
+public class SimpleVirtualPatchRule extends Rule {
+
+	private static final String REQUEST_PARAMETERS = "request.parameters.";
+	private static final String REQUEST_HEADERS = "request.headers.";
+
+	private Pattern path;
+	private String variable;
+	private Pattern valid;
+	private String message;
+
+	public SimpleVirtualPatchRule(String id, Pattern path, String variable, Pattern valid, String message) {
+		setId(id);
+		this.path = path;
+		this.variable = variable;
+		this.valid = valid;
+		this.message = message;
+	}
+
+	public Action check(HttpServletRequest req,
+			InterceptingHTTPServletResponse response, 
+			HttpServletResponse httpResponse) {
+
+		InterceptingHTTPServletRequest request = (InterceptingHTTPServletRequest)req;
+
+		String uri = request.getRequestURI();
+		if ( ! path.matcher(uri).matches() ) {
+
+			return new DoNothingAction();
+
+		} else {
+
+			/*
+			 * Decide which parameters/headers to act on.
+			 */
+			String target = null;
+			Enumeration en = null;
+			boolean parameter = true;
+
+			if ( variable.startsWith(REQUEST_PARAMETERS)) {
+
+				target = variable.substring(REQUEST_PARAMETERS.length());
+				en = request.getParameterNames();
+
+			} else if ( variable.startsWith(REQUEST_HEADERS) ) {
+
+				parameter = false;
+				target = variable.substring(REQUEST_HEADERS.length());
+				en = request.getHeaderNames();
+
+			} else {
+				log(request, "Patch failed (improperly configured variable '" + variable + "')");
+				return new DefaultAction();
+			}
+
+			/*
+			 * If it contains a regex character, it's a regex. Loop through elements and grab any matches.
+			 */
+			if ( target.contains("*") || target.contains("?") ) {
+
+				target = target.replaceAll("\\*", ".*");
+				Pattern p = Pattern.compile(target);
+				while (en.hasMoreElements() ) {
+					String s = (String)en.nextElement();
+					String value = null;
+					if ( p.matcher(s).matches() ) {
+						if ( parameter ) {
+							value = request.getDictionaryParameter(s);
+						} else {
+							value = request.getHeader(s);
+						}
+						if ( value != null && ! valid.matcher(value).matches() ) {
+							log(request, "Virtual patch tripped on variable '" + variable + "' (specifically '" + s + "'). User input was '" + value + "' and legal pattern was '" + valid.pattern() + "': " + message);
+							return new DefaultAction();
+						}
+					}
+				}
+				
+				return new DoNothingAction();
+
+			} else {
+
+				if ( parameter ) {
+					String value = request.getDictionaryParameter(target);
+					if ( value == null || valid.matcher(value).matches() ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "Virtual patch tripped on parameter '" + target + "'. User input was '" + value + "' and legal pattern was '" + valid.pattern() + "': " + message);
+						return new DefaultAction();
+					}
+				} else {
+					String value = request.getHeader(target);
+					if ( value == null || valid.matcher(value).matches() ) {
+						return new DoNothingAction();
+					} else {
+						log(request, "Virtual patch tripped on header '" + target + "'. User input was '" + value + "' and legal pattern was '" + valid.pattern() + "': " + message);
+						return new DefaultAction();
+					}
+				}
+			}
+
+		}
+
+	}
+
+}
diff --git a/src/main/java/org/owasp/esapi/waf/rules/package.html b/src/main/java/org/owasp/esapi/waf/rules/package.html
new file mode 100644
index 0000000..133148e
--- /dev/null
+++ b/src/main/java/org/owasp/esapi/waf/rules/package.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+This package contains all of the Rule subclasses that correspond to policy file entries. Each 
+class contains the logic for enforcing its rule. 
+ 
+</body>
+</html>
diff --git a/src/test/.svn/all-wcprops b/src/test/.svn/all-wcprops
new file mode 100644
index 0000000..f2fdc88
--- /dev/null
+++ b/src/test/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test
+END
diff --git a/src/test/.svn/entries b/src/test/.svn/entries
new file mode 100644
index 0000000..844911d
--- /dev/null
+++ b/src/test/.svn/entries
@@ -0,0 +1,34 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+resources
+dir
+

+java
+dir
+

diff --git a/src/test/java/.svn/all-wcprops b/src/test/java/.svn/all-wcprops
new file mode 100644
index 0000000..577d07c
--- /dev/null
+++ b/src/test/java/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java
+END
diff --git a/src/test/java/.svn/entries b/src/test/java/.svn/entries
new file mode 100644
index 0000000..2dc175a
--- /dev/null
+++ b/src/test/java/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+org
+dir
+

diff --git a/src/test/java/org/.svn/all-wcprops b/src/test/java/org/.svn/all-wcprops
new file mode 100644
index 0000000..27bc532
--- /dev/null
+++ b/src/test/java/org/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org
+END
diff --git a/src/test/java/org/.svn/entries b/src/test/java/org/.svn/entries
new file mode 100644
index 0000000..ed13ce6
--- /dev/null
+++ b/src/test/java/org/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+owasp
+dir
+

diff --git a/src/test/java/org/owasp/.svn/all-wcprops b/src/test/java/org/owasp/.svn/all-wcprops
new file mode 100644
index 0000000..50ef771
--- /dev/null
+++ b/src/test/java/org/owasp/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp
+END
diff --git a/src/test/java/org/owasp/.svn/entries b/src/test/java/org/owasp/.svn/entries
new file mode 100644
index 0000000..c0f08e5
--- /dev/null
+++ b/src/test/java/org/owasp/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+esapi
+dir
+

diff --git a/src/test/java/org/owasp/esapi/.svn/all-wcprops b/src/test/java/org/owasp/esapi/.svn/all-wcprops
new file mode 100644
index 0000000..097ffbf
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi
+END
+SecurityConfigurationWrapper.java
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java
+END
+ValidationErrorListTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/ValidationErrorListTest.java
+END
+StringUtilitiesTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/StringUtilitiesTest.java
+END
+PreparedStringTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/PreparedStringTest.java
+END
+UserTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/UserTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/.svn/entries b/src/test/java/org/owasp/esapi/.svn/entries
new file mode 100644
index 0000000..d9e74dd
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/entries
@@ -0,0 +1,222 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+reference
+dir
+

+UserTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.485957Z
+25b9a8cd417ded0e25e7fabb76dd65a1
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5318
+

+http
+dir
+

+codecs
+dir
+

+crypto
+dir
+

+SecurityConfigurationWrapper.java
+file
+
+
+
+
+2014-02-18T16:19:52.485957Z
+af5a0d33d57bdc66315b2a840e61a3ae
+2011-03-22T02:17:26.844603Z
+1732
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10066
+

+ValidationErrorListTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.485957Z
+8bcbd49211a0fd1aac5fae463a147f3e
+2009-06-06T15:14:24.195945Z
+531
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3702
+

+StringUtilitiesTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.485957Z
+4aa34bec1841bb2a9ed00261ea7b7070
+2009-12-05T03:08:44.297728Z
+854
+chrisisbeef
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2997
+

+filters
+dir
+

+PreparedStringTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.485957Z
+84bce5c8cd9fec22e626bf113db9de50
+2009-12-09T05:00:23.011146Z
+906
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2163
+

+errors
+dir
+

+util
+dir
+

+waf
+dir
+

diff --git a/src/test/java/org/owasp/esapi/.svn/prop-base/StringUtilitiesTest.java.svn-base b/src/test/java/org/owasp/esapi/.svn/prop-base/StringUtilitiesTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/prop-base/StringUtilitiesTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/.svn/text-base/PreparedStringTest.java.svn-base b/src/test/java/org/owasp/esapi/.svn/text-base/PreparedStringTest.java.svn-base
new file mode 100644
index 0000000..b288c13
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/text-base/PreparedStringTest.java.svn-base
@@ -0,0 +1,65 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+
+package org.owasp.esapi;
+
+import junit.framework.TestCase;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.HTMLEntityCodec;
+
+public class PreparedStringTest extends TestCase {
+
+    private final static Codec htmlEntityCodec = new HTMLEntityCodec();
+
+    public void testPreparedString() {
+        PreparedString ps1 = new PreparedString( "Test ? is ?", htmlEntityCodec );
+        ps1.set( 1, "[]<>;\"\'PreparedString" );
+        ps1.set( 2, "cool" );
+        try {
+            PreparedString ps2 = new PreparedString( "Test ? is ?", htmlEntityCodec );
+            ps2.set( 2, "cool" );
+        } catch( Exception e ) {
+            fail(e.getMessage());
+        }
+
+        try {
+            PreparedString ps3 = new PreparedString( "Test ? is ?", htmlEntityCodec );
+            ps3.set( 1, "[]<>;\"\'PreparedString" );
+            ps3.set( 2, "cool" );
+            ps3.set( 3, "cool" );
+            fail("Was able to set parameters past the end of the parameter stack.");
+        } catch( Exception e ) {
+            // Success
+        }
+
+        try {
+            PreparedString ps4 = new PreparedString( "???", htmlEntityCodec );
+            ps4.set( 1, "1" );
+            ps4.set( 2, "2" );
+            ps4.set( 3, "3" );
+        } catch( Exception e ) {
+            fail(e.getMessage());
+        }
+
+        try {
+            PreparedString ps5 = new PreparedString( "??x", htmlEntityCodec );
+            ps5.set( 1, "1" );
+            ps5.set( 2, "2" );
+        } catch( Exception e ) {
+            fail(e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/.svn/text-base/SecurityConfigurationWrapper.java.svn-base b/src/test/java/org/owasp/esapi/.svn/text-base/SecurityConfigurationWrapper.java.svn-base
new file mode 100644
index 0000000..eb9c0c9
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/text-base/SecurityConfigurationWrapper.java.svn-base
@@ -0,0 +1,633 @@
+package org.owasp.esapi;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Simple wrapper implementation of {@link SecurityConfiguration}. 
+ * This allows for easy subclassing and property fixups for unit tests.
+ *
+ * Note that there are some compilers have issues with Override
+ * attributes on methods implementing a interface method with some
+ * compilers. Technically Override on such methods is a 1.6 feature so
+ * they are commented out here.
+ */
+public class SecurityConfigurationWrapper implements SecurityConfiguration
+{
+	private SecurityConfiguration wrapped;
+
+	/**
+	 * Constructor wrapping the given configuration.
+	 * @param wrapped The configuration to wrap.
+	 */
+	public SecurityConfigurationWrapper(SecurityConfiguration wrapped)
+	{
+		this.wrapped = wrapped;
+	}
+
+	/**
+	 * Access the wrapped configuration.
+	 * @return The wrapped configuration.
+	 */
+	public SecurityConfiguration getWrappedSecurityConfiguration()
+	{
+		return wrapped;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getApplicationName()
+	{
+		return wrapped.getApplicationName();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getLogImplementation()
+	{
+		return wrapped.getLogImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getAuthenticationImplementation()
+	{
+		return wrapped.getAuthenticationImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getEncoderImplementation()
+	{
+		return wrapped.getEncoderImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getAccessControlImplementation()
+	{
+		return wrapped.getAccessControlImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getIntrusionDetectionImplementation()
+	{
+		return wrapped.getIntrusionDetectionImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getRandomizerImplementation()
+	{
+		return wrapped.getRandomizerImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getEncryptionImplementation()
+	{
+		return wrapped.getEncryptionImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getValidationImplementation()
+	{
+		return wrapped.getValidationImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public Pattern getValidationPattern( String typeName )
+	{
+		return wrapped.getValidationPattern(typeName);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getExecutorImplementation()
+	{
+		return wrapped.getExecutorImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getHTTPUtilitiesImplementation()
+	{
+		return wrapped.getHTTPUtilitiesImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public byte[] getMasterKey()
+	{
+		return wrapped.getMasterKey();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getUploadDirectory()
+	{
+		return wrapped.getUploadDirectory();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getUploadTempDirectory()
+	{
+		return wrapped.getUploadTempDirectory();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getEncryptionKeyLength()
+	{
+		return wrapped.getEncryptionKeyLength();
+	}
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public byte[] getMasterSalt()
+	{
+		return wrapped.getMasterSalt();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public List getAllowedExecutables()
+	{
+		return wrapped.getAllowedExecutables();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public List getAllowedFileExtensions()
+	{
+		return wrapped.getAllowedFileExtensions();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getAllowedFileUploadSize()
+	{
+		return wrapped.getAllowedFileUploadSize();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getPasswordParameterName()
+	{
+		return wrapped.getPasswordParameterName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getUsernameParameterName()
+	{
+		return wrapped.getUsernameParameterName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getEncryptionAlgorithm()
+	{
+		return wrapped.getEncryptionAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getCipherTransformation()
+	{
+		return wrapped.getCipherTransformation();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String setCipherTransformation(String cipherXform)
+	{
+		return wrapped.setCipherTransformation(cipherXform);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean useMACforCipherText()
+	{
+		return wrapped.useMACforCipherText();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean overwritePlainText()
+	{
+		return wrapped.overwritePlainText();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getIVType()
+	{
+		return wrapped.getIVType();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getFixedIV()
+	{
+		return wrapped.getFixedIV();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getHashAlgorithm()
+	{
+		return wrapped.getHashAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getHashIterations()
+	{
+		return wrapped.getHashIterations();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getCharacterEncoding()
+	{
+		return wrapped.getCharacterEncoding();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getAllowMultipleEncoding()
+	{
+		return wrapped.getAllowMultipleEncoding();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getAllowMixedEncoding()
+	{
+		return wrapped.getAllowMixedEncoding();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public List getDefaultCanonicalizationCodecs()
+	{
+		return wrapped.getDefaultCanonicalizationCodecs();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getDigitalSignatureAlgorithm()
+	{
+		return wrapped.getDigitalSignatureAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getDigitalSignatureKeyLength()
+	{
+		return wrapped.getDigitalSignatureKeyLength();
+	}
+		   
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getRandomAlgorithm()
+	{
+		return wrapped.getRandomAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getAllowedLoginAttempts()
+	{
+		return wrapped.getAllowedLoginAttempts();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getMaxOldPasswordHashes()
+	{
+		return wrapped.getMaxOldPasswordHashes();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public Threshold getQuota(String eventName)
+	{
+		return wrapped.getQuota(eventName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getResourceFile( String filename )
+	{
+		return wrapped.getResourceFile(filename);
+	}
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceHttpOnlySession() 
+	{
+		return wrapped.getForceHttpOnlySession();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceSecureSession() 
+	{
+		return wrapped.getForceSecureSession();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceHttpOnlyCookies()
+	{
+		return wrapped.getForceHttpOnlyCookies();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceSecureCookies()
+	{
+		return wrapped.getForceSecureCookies();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getMaxHttpHeaderSize() {
+        return wrapped.getMaxHttpHeaderSize();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public InputStream getResourceStream( String filename ) throws IOException
+	{
+		return wrapped.getResourceStream(filename);
+	}
+
+    	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public void setResourceDirectory(String dir)
+	{
+		wrapped.setResourceDirectory(dir);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getResponseContentType()
+	{
+		return wrapped.getResponseContentType();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getHttpSessionIdName() {
+		return wrapped.getHttpSessionIdName();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public long getRememberTokenDuration()
+	{
+		return wrapped.getRememberTokenDuration();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getSessionIdleTimeoutLength()
+	{
+		return wrapped.getSessionIdleTimeoutLength();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getSessionAbsoluteTimeoutLength()
+	{
+		return wrapped.getSessionAbsoluteTimeoutLength();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getLogEncodingRequired()
+	{
+		return wrapped.getLogEncodingRequired();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getLogApplicationName()
+	{
+		return wrapped.getLogApplicationName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getLogServerIP()
+	{
+		return wrapped.getLogServerIP();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getLogLevel()
+	{
+		return wrapped.getLogLevel();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getLogFileName()
+	{
+		return wrapped.getLogFileName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getMaxLogFileSize()
+	{
+	 	return wrapped.getMaxLogFileSize();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getWorkingDirectory()
+	{
+		return wrapped.getWorkingDirectory();
+	}
+
+	/**
+     * {@inheritDoc}
+     */
+    // @Override
+    public List<String> getAdditionalAllowedCipherModes() {
+        return wrapped.getAdditionalAllowedCipherModes();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    // @Override
+    public List<String> getCombinedCipherModes() {
+        return wrapped.getCombinedCipherModes();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPreferredJCEProvider() {
+        return wrapped.getPreferredJCEProvider();
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getDisableIntrusionDetection() {
+		return wrapped.getDisableIntrusionDetection();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getKDFPseudoRandomFunction() {
+		return wrapped.getKDFPseudoRandomFunction();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean getLenientDatesAccepted() {
+		return wrapped.getLenientDatesAccepted();
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/.svn/text-base/StringUtilitiesTest.java.svn-base b/src/test/java/org/owasp/esapi/.svn/text-base/StringUtilitiesTest.java.svn-base
new file mode 100644
index 0000000..de82b65
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/text-base/StringUtilitiesTest.java.svn-base
@@ -0,0 +1,73 @@
+package org.owasp.esapi;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.owasp.esapi.StringUtilities;
+
+public class StringUtilitiesTest extends TestCase {
+
+	/**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+     * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(StringUtilitiesTest.class);
+        return suite;
+    }
+    
+    /** Test the getLevenshteinDistance() method. */
+    public void testGetLevenshteinDistance() {
+    	String src    = "GUMBO";
+    	String target = "GAMBOL";
+    	assertTrue( 2 == StringUtilities.getLevenshteinDistance(src, target) );
+    	assertTrue( 2 == StringUtilities.getLevenshteinDistance(target, src) );
+    	assertTrue( 0 == StringUtilities.getLevenshteinDistance(src, src) );
+    	assertTrue( 5 == StringUtilities.getLevenshteinDistance(src, "") );
+    	assertTrue( 6 == StringUtilities.getLevenshteinDistance("", target) );
+
+    	try {
+    		@SuppressWarnings("unused")
+			int ldist = StringUtilities.getLevenshteinDistance(null, "abc");
+    	} catch ( IllegalArgumentException ex ) {
+    		assertTrue( ex.getClass().getName().equals( IllegalArgumentException.class.getName() ));
+    	}
+
+    	try {
+    		@SuppressWarnings("unused")
+			int ldist = StringUtilities.getLevenshteinDistance("abc", null);
+    	} catch ( IllegalArgumentException ex ) {
+    		assertTrue( ex.getClass().getName().equals( IllegalArgumentException.class.getName() ));
+    	}
+    }
+    
+    /** Test the notNullOrEmpty() method. */
+    public void testNotNullOrEmpty() {
+    	String str = "A string";
+    	assertTrue( StringUtilities.notNullOrEmpty(str, false) );
+    	assertTrue( StringUtilities.notNullOrEmpty(str, true) );
+    	str = "   A  string  ";
+       	assertTrue( StringUtilities.notNullOrEmpty(str, false) );
+    	assertTrue( StringUtilities.notNullOrEmpty(str, true) );
+    	str = "   ";
+       	assertTrue( StringUtilities.notNullOrEmpty(str, false) );
+    	assertFalse( StringUtilities.notNullOrEmpty(str, true) );
+    	str = "";
+       	assertFalse( StringUtilities.notNullOrEmpty(str, false) );
+    	assertFalse( StringUtilities.notNullOrEmpty(str, true) );
+    	str = null;
+       	assertFalse( StringUtilities.notNullOrEmpty(str, false) );
+    	assertFalse( StringUtilities.notNullOrEmpty(str, true) );
+    }
+
+    public void testReplaceNull() {
+        assertEquals( "TEST", StringUtilities.replaceNull( "TEST", "ABCD" ) );
+        assertEquals( "REPLACED", StringUtilities.replaceNull( "NULL", "REPLACED" ) );
+        assertEquals( "Replaced", StringUtilities.replaceNull( null, "Replaced" ) );
+        assertEquals( "Replaced", StringUtilities.replaceNull( "         ", "Replaced" ) );
+        assertEquals( "   Test   ", StringUtilities.replaceNull( "   Test   ", "Replaced" ) );
+        assertEquals( "Replaced", StringUtilities.replaceNull( "     NULL ", "Replaced" ) );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/.svn/text-base/UserTest.java.svn-base b/src/test/java/org/owasp/esapi/.svn/text-base/UserTest.java.svn-base
new file mode 100644
index 0000000..ab31d48
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/text-base/UserTest.java.svn-base
@@ -0,0 +1,106 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class UserTest extends TestCase {
+
+	public UserTest(String testName) {
+		super(testName);
+	}
+
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UserTest.class);
+		return suite;
+	}
+	
+	public void testAllMethods() throws Exception {
+		// create a user to test Anonymous
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		
+			// Probably could skip the assignment here, but maybe someone had
+			// future plans to use this. So will just suppress warning for now.
+		@SuppressWarnings("unused")
+		User user = instance.createUser(accountName, password, password);
+		
+		// test the rest of the Anonymous user
+		try { User.ANONYMOUS.addRole(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.addRoles(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.changePassword(null, null, null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.disable(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.enable(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountId(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getCSRFToken(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getExpirationTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getFailedLoginCount(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLastFailedLoginTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLastLoginTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLastPasswordChangeTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getRoles(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getScreenName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.addSession(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.removeSession(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.incrementFailedLoginCount(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isAnonymous(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isEnabled(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isExpired(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isInRole(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isLocked(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isLoggedIn(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isSessionAbsoluteTimeout(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isSessionTimeout(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.lock(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.loginWithPassword(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.logout(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.removeRole(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.resetCSRFToken(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setAccountName(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setExpirationTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setRoles(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setScreenName(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.unlock(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.verifyPassword(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastFailedLoginTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastLoginTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastHostAddress(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastPasswordChangeTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getEventMap(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLocale(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLocale(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountName(); } catch( RuntimeException e ) {}
+	}
+}
+
+
diff --git a/src/test/java/org/owasp/esapi/.svn/text-base/ValidationErrorListTest.java.svn-base b/src/test/java/org/owasp/esapi/.svn/text-base/ValidationErrorListTest.java.svn-base
new file mode 100644
index 0000000..2880cd7
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/.svn/text-base/ValidationErrorListTest.java.svn-base
@@ -0,0 +1,140 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationErrorListTest extends TestCase {
+
+	/**
+	 * Instantiates a new executor test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public ValidationErrorListTest(String testName) {
+		super(testName);
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ValidationErrorListTest.class);
+		return suite;
+	}
+	
+	public void testAddError() throws Exception {
+		System.out.println("testAddError");
+		ValidationErrorList vel = new ValidationErrorList();
+		ValidationException vex = createValidationException();
+		vel.addError("context", vex );
+		try {
+			vel.addError(null, vex );
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			vel.addError("context1", null );
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			vel.addError("context", vex );  // add the same context again
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+	}
+
+	public void testErrors() throws Exception {
+		System.out.println("testErrors");
+		ValidationErrorList vel = new ValidationErrorList();
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertTrue( vel.errors().get(0) == vex );
+	}
+
+	public void testGetError() throws Exception {
+		System.out.println("testGetError");
+		ValidationErrorList vel = new ValidationErrorList();
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertTrue( vel.getError( "context" ) == vex );
+		assertTrue( vel.getError( "ridiculous" ) == null );
+	}
+
+	public void testIsEmpty() throws Exception {
+		System.out.println("testIsEmpty");
+		ValidationErrorList vel = new ValidationErrorList();
+		assertTrue( vel.isEmpty() );
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertFalse( vel.isEmpty() );
+	}
+
+	public void testSize() throws Exception {
+		System.out.println("testSize");
+		ValidationErrorList vel = new ValidationErrorList();
+		assertTrue( vel.size() == 0 );
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertTrue( vel.size() == 1 );
+	}
+
+	private ValidationException createValidationException() {
+		ValidationException vex = null;
+		try {
+			vex = new ValidationException("User message", "Log Message");
+		} catch( IntrusionException e ) {
+			// expected occasionally
+		}
+		return vex;
+	}
+	
+}
+
+
diff --git a/src/test/java/org/owasp/esapi/PreparedStringTest.java b/src/test/java/org/owasp/esapi/PreparedStringTest.java
new file mode 100644
index 0000000..b288c13
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/PreparedStringTest.java
@@ -0,0 +1,65 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+
+package org.owasp.esapi;
+
+import junit.framework.TestCase;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.HTMLEntityCodec;
+
+public class PreparedStringTest extends TestCase {
+
+    private final static Codec htmlEntityCodec = new HTMLEntityCodec();
+
+    public void testPreparedString() {
+        PreparedString ps1 = new PreparedString( "Test ? is ?", htmlEntityCodec );
+        ps1.set( 1, "[]<>;\"\'PreparedString" );
+        ps1.set( 2, "cool" );
+        try {
+            PreparedString ps2 = new PreparedString( "Test ? is ?", htmlEntityCodec );
+            ps2.set( 2, "cool" );
+        } catch( Exception e ) {
+            fail(e.getMessage());
+        }
+
+        try {
+            PreparedString ps3 = new PreparedString( "Test ? is ?", htmlEntityCodec );
+            ps3.set( 1, "[]<>;\"\'PreparedString" );
+            ps3.set( 2, "cool" );
+            ps3.set( 3, "cool" );
+            fail("Was able to set parameters past the end of the parameter stack.");
+        } catch( Exception e ) {
+            // Success
+        }
+
+        try {
+            PreparedString ps4 = new PreparedString( "???", htmlEntityCodec );
+            ps4.set( 1, "1" );
+            ps4.set( 2, "2" );
+            ps4.set( 3, "3" );
+        } catch( Exception e ) {
+            fail(e.getMessage());
+        }
+
+        try {
+            PreparedString ps5 = new PreparedString( "??x", htmlEntityCodec );
+            ps5.set( 1, "1" );
+            ps5.set( 2, "2" );
+        } catch( Exception e ) {
+            fail(e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java b/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java
new file mode 100644
index 0000000..eb9c0c9
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java
@@ -0,0 +1,633 @@
+package org.owasp.esapi;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Simple wrapper implementation of {@link SecurityConfiguration}. 
+ * This allows for easy subclassing and property fixups for unit tests.
+ *
+ * Note that there are some compilers have issues with Override
+ * attributes on methods implementing a interface method with some
+ * compilers. Technically Override on such methods is a 1.6 feature so
+ * they are commented out here.
+ */
+public class SecurityConfigurationWrapper implements SecurityConfiguration
+{
+	private SecurityConfiguration wrapped;
+
+	/**
+	 * Constructor wrapping the given configuration.
+	 * @param wrapped The configuration to wrap.
+	 */
+	public SecurityConfigurationWrapper(SecurityConfiguration wrapped)
+	{
+		this.wrapped = wrapped;
+	}
+
+	/**
+	 * Access the wrapped configuration.
+	 * @return The wrapped configuration.
+	 */
+	public SecurityConfiguration getWrappedSecurityConfiguration()
+	{
+		return wrapped;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getApplicationName()
+	{
+		return wrapped.getApplicationName();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getLogImplementation()
+	{
+		return wrapped.getLogImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getAuthenticationImplementation()
+	{
+		return wrapped.getAuthenticationImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getEncoderImplementation()
+	{
+		return wrapped.getEncoderImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getAccessControlImplementation()
+	{
+		return wrapped.getAccessControlImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getIntrusionDetectionImplementation()
+	{
+		return wrapped.getIntrusionDetectionImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getRandomizerImplementation()
+	{
+		return wrapped.getRandomizerImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getEncryptionImplementation()
+	{
+		return wrapped.getEncryptionImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getValidationImplementation()
+	{
+		return wrapped.getValidationImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public Pattern getValidationPattern( String typeName )
+	{
+		return wrapped.getValidationPattern(typeName);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getExecutorImplementation()
+	{
+		return wrapped.getExecutorImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getHTTPUtilitiesImplementation()
+	{
+		return wrapped.getHTTPUtilitiesImplementation();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public byte[] getMasterKey()
+	{
+		return wrapped.getMasterKey();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getUploadDirectory()
+	{
+		return wrapped.getUploadDirectory();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getUploadTempDirectory()
+	{
+		return wrapped.getUploadTempDirectory();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getEncryptionKeyLength()
+	{
+		return wrapped.getEncryptionKeyLength();
+	}
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public byte[] getMasterSalt()
+	{
+		return wrapped.getMasterSalt();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public List getAllowedExecutables()
+	{
+		return wrapped.getAllowedExecutables();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public List getAllowedFileExtensions()
+	{
+		return wrapped.getAllowedFileExtensions();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getAllowedFileUploadSize()
+	{
+		return wrapped.getAllowedFileUploadSize();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getPasswordParameterName()
+	{
+		return wrapped.getPasswordParameterName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getUsernameParameterName()
+	{
+		return wrapped.getUsernameParameterName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getEncryptionAlgorithm()
+	{
+		return wrapped.getEncryptionAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getCipherTransformation()
+	{
+		return wrapped.getCipherTransformation();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String setCipherTransformation(String cipherXform)
+	{
+		return wrapped.setCipherTransformation(cipherXform);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean useMACforCipherText()
+	{
+		return wrapped.useMACforCipherText();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean overwritePlainText()
+	{
+		return wrapped.overwritePlainText();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getIVType()
+	{
+		return wrapped.getIVType();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getFixedIV()
+	{
+		return wrapped.getFixedIV();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getHashAlgorithm()
+	{
+		return wrapped.getHashAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getHashIterations()
+	{
+		return wrapped.getHashIterations();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getCharacterEncoding()
+	{
+		return wrapped.getCharacterEncoding();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getAllowMultipleEncoding()
+	{
+		return wrapped.getAllowMultipleEncoding();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getAllowMixedEncoding()
+	{
+		return wrapped.getAllowMixedEncoding();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public List getDefaultCanonicalizationCodecs()
+	{
+		return wrapped.getDefaultCanonicalizationCodecs();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getDigitalSignatureAlgorithm()
+	{
+		return wrapped.getDigitalSignatureAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getDigitalSignatureKeyLength()
+	{
+		return wrapped.getDigitalSignatureKeyLength();
+	}
+		   
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getRandomAlgorithm()
+	{
+		return wrapped.getRandomAlgorithm();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getAllowedLoginAttempts()
+	{
+		return wrapped.getAllowedLoginAttempts();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getMaxOldPasswordHashes()
+	{
+		return wrapped.getMaxOldPasswordHashes();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public Threshold getQuota(String eventName)
+	{
+		return wrapped.getQuota(eventName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getResourceFile( String filename )
+	{
+		return wrapped.getResourceFile(filename);
+	}
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceHttpOnlySession() 
+	{
+		return wrapped.getForceHttpOnlySession();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceSecureSession() 
+	{
+		return wrapped.getForceSecureSession();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceHttpOnlyCookies()
+	{
+		return wrapped.getForceHttpOnlyCookies();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getForceSecureCookies()
+	{
+		return wrapped.getForceSecureCookies();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getMaxHttpHeaderSize() {
+        return wrapped.getMaxHttpHeaderSize();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public InputStream getResourceStream( String filename ) throws IOException
+	{
+		return wrapped.getResourceStream(filename);
+	}
+
+    	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public void setResourceDirectory(String dir)
+	{
+		wrapped.setResourceDirectory(dir);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getResponseContentType()
+	{
+		return wrapped.getResponseContentType();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getHttpSessionIdName() {
+		return wrapped.getHttpSessionIdName();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public long getRememberTokenDuration()
+	{
+		return wrapped.getRememberTokenDuration();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getSessionIdleTimeoutLength()
+	{
+		return wrapped.getSessionIdleTimeoutLength();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getSessionAbsoluteTimeoutLength()
+	{
+		return wrapped.getSessionAbsoluteTimeoutLength();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getLogEncodingRequired()
+	{
+		return wrapped.getLogEncodingRequired();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getLogApplicationName()
+	{
+		return wrapped.getLogApplicationName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getLogServerIP()
+	{
+		return wrapped.getLogServerIP();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getLogLevel()
+	{
+		return wrapped.getLogLevel();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getLogFileName()
+	{
+		return wrapped.getLogFileName();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public int getMaxLogFileSize()
+	{
+	 	return wrapped.getMaxLogFileSize();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public File getWorkingDirectory()
+	{
+		return wrapped.getWorkingDirectory();
+	}
+
+	/**
+     * {@inheritDoc}
+     */
+    // @Override
+    public List<String> getAdditionalAllowedCipherModes() {
+        return wrapped.getAdditionalAllowedCipherModes();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    // @Override
+    public List<String> getCombinedCipherModes() {
+        return wrapped.getCombinedCipherModes();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPreferredJCEProvider() {
+        return wrapped.getPreferredJCEProvider();
+    }
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public boolean getDisableIntrusionDetection() {
+		return wrapped.getDisableIntrusionDetection();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	// @Override
+	public String getKDFPseudoRandomFunction() {
+		return wrapped.getKDFPseudoRandomFunction();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean getLenientDatesAccepted() {
+		return wrapped.getLenientDatesAccepted();
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/StringUtilitiesTest.java b/src/test/java/org/owasp/esapi/StringUtilitiesTest.java
new file mode 100644
index 0000000..de82b65
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/StringUtilitiesTest.java
@@ -0,0 +1,73 @@
+package org.owasp.esapi;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.owasp.esapi.StringUtilities;
+
+public class StringUtilitiesTest extends TestCase {
+
+	/**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+     * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(StringUtilitiesTest.class);
+        return suite;
+    }
+    
+    /** Test the getLevenshteinDistance() method. */
+    public void testGetLevenshteinDistance() {
+    	String src    = "GUMBO";
+    	String target = "GAMBOL";
+    	assertTrue( 2 == StringUtilities.getLevenshteinDistance(src, target) );
+    	assertTrue( 2 == StringUtilities.getLevenshteinDistance(target, src) );
+    	assertTrue( 0 == StringUtilities.getLevenshteinDistance(src, src) );
+    	assertTrue( 5 == StringUtilities.getLevenshteinDistance(src, "") );
+    	assertTrue( 6 == StringUtilities.getLevenshteinDistance("", target) );
+
+    	try {
+    		@SuppressWarnings("unused")
+			int ldist = StringUtilities.getLevenshteinDistance(null, "abc");
+    	} catch ( IllegalArgumentException ex ) {
+    		assertTrue( ex.getClass().getName().equals( IllegalArgumentException.class.getName() ));
+    	}
+
+    	try {
+    		@SuppressWarnings("unused")
+			int ldist = StringUtilities.getLevenshteinDistance("abc", null);
+    	} catch ( IllegalArgumentException ex ) {
+    		assertTrue( ex.getClass().getName().equals( IllegalArgumentException.class.getName() ));
+    	}
+    }
+    
+    /** Test the notNullOrEmpty() method. */
+    public void testNotNullOrEmpty() {
+    	String str = "A string";
+    	assertTrue( StringUtilities.notNullOrEmpty(str, false) );
+    	assertTrue( StringUtilities.notNullOrEmpty(str, true) );
+    	str = "   A  string  ";
+       	assertTrue( StringUtilities.notNullOrEmpty(str, false) );
+    	assertTrue( StringUtilities.notNullOrEmpty(str, true) );
+    	str = "   ";
+       	assertTrue( StringUtilities.notNullOrEmpty(str, false) );
+    	assertFalse( StringUtilities.notNullOrEmpty(str, true) );
+    	str = "";
+       	assertFalse( StringUtilities.notNullOrEmpty(str, false) );
+    	assertFalse( StringUtilities.notNullOrEmpty(str, true) );
+    	str = null;
+       	assertFalse( StringUtilities.notNullOrEmpty(str, false) );
+    	assertFalse( StringUtilities.notNullOrEmpty(str, true) );
+    }
+
+    public void testReplaceNull() {
+        assertEquals( "TEST", StringUtilities.replaceNull( "TEST", "ABCD" ) );
+        assertEquals( "REPLACED", StringUtilities.replaceNull( "NULL", "REPLACED" ) );
+        assertEquals( "Replaced", StringUtilities.replaceNull( null, "Replaced" ) );
+        assertEquals( "Replaced", StringUtilities.replaceNull( "         ", "Replaced" ) );
+        assertEquals( "   Test   ", StringUtilities.replaceNull( "   Test   ", "Replaced" ) );
+        assertEquals( "Replaced", StringUtilities.replaceNull( "     NULL ", "Replaced" ) );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/UserTest.java b/src/test/java/org/owasp/esapi/UserTest.java
new file mode 100644
index 0000000..ab31d48
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/UserTest.java
@@ -0,0 +1,106 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class UserTest extends TestCase {
+
+	public UserTest(String testName) {
+		super(testName);
+	}
+
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UserTest.class);
+		return suite;
+	}
+	
+	public void testAllMethods() throws Exception {
+		// create a user to test Anonymous
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		
+			// Probably could skip the assignment here, but maybe someone had
+			// future plans to use this. So will just suppress warning for now.
+		@SuppressWarnings("unused")
+		User user = instance.createUser(accountName, password, password);
+		
+		// test the rest of the Anonymous user
+		try { User.ANONYMOUS.addRole(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.addRoles(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.changePassword(null, null, null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.disable(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.enable(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountId(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getCSRFToken(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getExpirationTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getFailedLoginCount(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLastFailedLoginTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLastLoginTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLastPasswordChangeTime(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getRoles(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getScreenName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.addSession(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.removeSession(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.incrementFailedLoginCount(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isAnonymous(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isEnabled(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isExpired(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isInRole(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isLocked(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isLoggedIn(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isSessionAbsoluteTimeout(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.isSessionTimeout(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.lock(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.loginWithPassword(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.logout(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.removeRole(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.resetCSRFToken(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setAccountName(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setExpirationTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setRoles(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setScreenName(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.unlock(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.verifyPassword(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastFailedLoginTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastLoginTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastHostAddress(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLastPasswordChangeTime(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getEventMap(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getLocale(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.setLocale(null); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountName(); } catch( RuntimeException e ) {}
+		try { User.ANONYMOUS.getAccountName(); } catch( RuntimeException e ) {}
+	}
+}
+
+
diff --git a/src/test/java/org/owasp/esapi/ValidationErrorListTest.java b/src/test/java/org/owasp/esapi/ValidationErrorListTest.java
new file mode 100644
index 0000000..2880cd7
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/ValidationErrorListTest.java
@@ -0,0 +1,140 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidationErrorListTest extends TestCase {
+
+	/**
+	 * Instantiates a new executor test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public ValidationErrorListTest(String testName) {
+		super(testName);
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ValidationErrorListTest.class);
+		return suite;
+	}
+	
+	public void testAddError() throws Exception {
+		System.out.println("testAddError");
+		ValidationErrorList vel = new ValidationErrorList();
+		ValidationException vex = createValidationException();
+		vel.addError("context", vex );
+		try {
+			vel.addError(null, vex );
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			vel.addError("context1", null );
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			vel.addError("context", vex );  // add the same context again
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+	}
+
+	public void testErrors() throws Exception {
+		System.out.println("testErrors");
+		ValidationErrorList vel = new ValidationErrorList();
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertTrue( vel.errors().get(0) == vex );
+	}
+
+	public void testGetError() throws Exception {
+		System.out.println("testGetError");
+		ValidationErrorList vel = new ValidationErrorList();
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertTrue( vel.getError( "context" ) == vex );
+		assertTrue( vel.getError( "ridiculous" ) == null );
+	}
+
+	public void testIsEmpty() throws Exception {
+		System.out.println("testIsEmpty");
+		ValidationErrorList vel = new ValidationErrorList();
+		assertTrue( vel.isEmpty() );
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertFalse( vel.isEmpty() );
+	}
+
+	public void testSize() throws Exception {
+		System.out.println("testSize");
+		ValidationErrorList vel = new ValidationErrorList();
+		assertTrue( vel.size() == 0 );
+		ValidationException vex = createValidationException();
+		vel.addError("context",  vex );
+		assertTrue( vel.size() == 1 );
+	}
+
+	private ValidationException createValidationException() {
+		ValidationException vex = null;
+		try {
+			vex = new ValidationException("User message", "Log Message");
+		} catch( IntrusionException e ) {
+			// expected occasionally
+		}
+		return vex;
+	}
+	
+}
+
+
diff --git a/src/test/java/org/owasp/esapi/codecs/.svn/all-wcprops b/src/test/java/org/owasp/esapi/codecs/.svn/all-wcprops
new file mode 100644
index 0000000..269910d
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/codecs
+END
+HashTrieTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/codecs/HashTrieTest.java
+END
+XMLEntityCodecTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/codecs/XMLEntityCodecTest.java
+END
+CodecTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/codecs/CodecTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/codecs/.svn/entries b/src/test/java/org/owasp/esapi/codecs/.svn/entries
new file mode 100644
index 0000000..7cfcf40
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/codecs
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-05-02T18:45:32.893121Z
+1387
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+XMLEntityCodecTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.185951Z
+4cb52fa666813795bd2e304edf14de91
+2009-11-28T05:08:08.098660Z
+839
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5896
+

+CodecTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.185951Z
+7bfade92fd3834aa16dc41f77ff3095d
+2010-05-02T18:45:32.893121Z
+1387
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+17532
+

+HashTrieTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.185951Z
+58895e346564075a72a8c689031ac07d
+2009-11-06T16:52:53.628473Z
+755
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5735
+

diff --git a/src/test/java/org/owasp/esapi/codecs/.svn/text-base/CodecTest.java.svn-base b/src/test/java/org/owasp/esapi/codecs/.svn/text-base/CodecTest.java.svn-base
new file mode 100644
index 0000000..9aaa760
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/.svn/text-base/CodecTest.java.svn-base
@@ -0,0 +1,624 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+
+/**
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public class CodecTest extends TestCase {
+
+    private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+    private static final Character LESS_THAN = Character.valueOf('<');
+    private static final Character SINGLE_QUOTE = Character.valueOf('\'');
+    private HTMLEntityCodec htmlCodec = new HTMLEntityCodec();
+    private PercentCodec percentCodec = new PercentCodec();
+    private JavaScriptCodec javaScriptCodec = new JavaScriptCodec();
+    private VBScriptCodec vbScriptCodec = new VBScriptCodec();
+    private CSSCodec cssCodec = new CSSCodec();
+    private MySQLCodec mySQLCodecANSI = new MySQLCodec( MySQLCodec.ANSI_MODE );
+    private MySQLCodec mySQLCodecStandard = new MySQLCodec( MySQLCodec.MYSQL_MODE );
+    private OracleCodec oracleCodec = new OracleCodec();
+    private UnixCodec unixCodec = new UnixCodec();
+    private WindowsCodec windowsCodec = new WindowsCodec();
+
+    /**
+     * Instantiates a new access reference map test.
+     * 
+     * @param testName
+     *            the test name
+     */
+    public CodecTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+        // none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+        // none
+    }
+
+    /**
+     * Suite.
+     * 
+     * @return the test
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(CodecTest.class);
+        return suite;
+    }
+
+	public void testHtmlEncode()
+	{
+        	assertEquals( "test", htmlCodec.encode( EMPTY_CHAR_ARRAY, "test") );
+	}
+
+	public void testPercentEncode()
+	{
+        	assertEquals( "%3C", percentCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+
+	public void testJavaScriptEncode()
+	{
+        	assertEquals( "\\x3C", javaScriptCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testVBScriptEncode()
+	{
+        	assertEquals( "chrw(60)", vbScriptCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testCSSEncode()
+	{
+        	assertEquals( "\\3c ", cssCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testCSSInvalidCodepointDecode()
+	{
+		assertEquals("\uFFFDg", cssCodec.decode("\\abcdefg") );
+	}
+
+	public void testMySQLANSCIEncode()
+	{
+        	assertEquals( "\'\'", mySQLCodecANSI.encode(EMPTY_CHAR_ARRAY, "\'") );
+	}
+
+	public void testMySQLStandardEncode()
+	{
+        	assertEquals( "\\<", mySQLCodecStandard.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testOracleEncode()
+	{
+        	assertEquals( "\'\'", oracleCodec.encode(EMPTY_CHAR_ARRAY, "\'") );
+	}
+
+	public void testUnixEncode()
+	{
+        	assertEquals( "\\<", unixCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testWindowsEncode()
+	{
+        	assertEquals( "^<", windowsCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	
+	public void testHtmlEncodeChar()
+	{
+        	assertEquals( "<", htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testHtmlEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "&#x100;";
+		String result;
+
+        	result = htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testHtmlEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "&#x100;";
+		String result;
+
+        	result = htmlCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testPercentEncodeChar()
+	{
+        	assertEquals( "%3C", percentCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testPercentEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "%C4%80";
+		String result;
+
+        	result = percentCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testPercentEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "%C4%80";
+		String result;
+
+        	result = percentCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testJavaScriptEncodeChar()
+	{
+        	assertEquals( "\\x3C", javaScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testJavaScriptEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\u0100";
+		String result;
+
+        	result = javaScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testJavaScriptEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\u0100";
+		String result;
+
+        	result = javaScriptCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+        
+	public void testVBScriptEncodeChar()
+	{
+        	assertEquals( "chrw(60)", vbScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testVBScriptEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		// FIXME I don't know vb...
+		// String expected = "\\u0100";
+		String result;
+
+        	result = vbScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	//assertEquals(expected,result);
+	}
+
+	public void testVBScriptEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		// FIXME I don't know vb...
+		// String expected = "chrw(0x100)";
+		String result;
+
+        	result = vbScriptCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	// assertEquals(expected,result);
+	}
+
+	public void testCSSEncodeChar()
+	{
+        	assertEquals( "\\3c ", cssCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testCSSEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\100 ";
+		String result;
+
+        	result = cssCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testCSSEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\100 ";
+		String result;
+
+        	result = cssCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testMySQLANSIEncodeChar()
+	{
+        	assertEquals( "\'\'", mySQLCodecANSI.encodeCharacter(EMPTY_CHAR_ARRAY, SINGLE_QUOTE));
+	}
+
+	public void testMySQLStandardEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = mySQLCodecStandard.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testMySQLStandardEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = mySQLCodecStandard.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testMySQLStandardEncodeChar()
+	{
+        	assertEquals( "\\<", mySQLCodecStandard.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testOracleEncodeChar()
+	{
+        	assertEquals( "\'\'", oracleCodec.encodeCharacter(EMPTY_CHAR_ARRAY, SINGLE_QUOTE) );
+	}
+
+	public void testUnixEncodeChar()
+	{
+        	assertEquals( "\\<", unixCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testUnixEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = unixCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testUnixEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = unixCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testWindowsEncodeChar()
+	{
+        	assertEquals( "^<", windowsCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testWindowsEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "^" + in;
+		String result;
+
+        	result = windowsCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testWindowsEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "^" + in;
+		String result;
+
+        	result = windowsCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+	
+	public void testHtmlDecodeDecimalEntities()
+	{
+        	assertEquals( "test!", htmlCodec.decode("test!") );
+	}
+
+	public void testHtmlDecodeHexEntitites()
+	{
+        	assertEquals( "test!", htmlCodec.decode("&#x74;&#x65;&#x73;&#x74;!") );
+	}
+
+	public void testHtmlDecodeInvalidAttribute()
+	{
+        	assertEquals( "&jeff;", htmlCodec.decode("&jeff;") );
+	}
+
+	public void testHtmlDecodeAmp()
+	{
+		assertEquals("&", htmlCodec.decode("&"));
+		assertEquals("&X", htmlCodec.decode("&X"));
+		assertEquals("&", htmlCodec.decode("&amp"));
+		assertEquals("&X", htmlCodec.decode("&ampX"));
+	}
+
+	public void testHtmlDecodeLt()
+	{
+		assertEquals("<", htmlCodec.decode("<"));
+		assertEquals("<X", htmlCodec.decode("<X"));
+		assertEquals("<", htmlCodec.decode("&lt"));
+		assertEquals("<X", htmlCodec.decode("&ltX"));
+	}
+
+	public void testHtmlDecodeSup1()
+	{
+		assertEquals("\u00B9", htmlCodec.decode("&sup1;"));
+		assertEquals("\u00B9X", htmlCodec.decode("&sup1;X"));
+		assertEquals("\u00B9", htmlCodec.decode("&sup1"));
+		assertEquals("\u00B9X", htmlCodec.decode("&sup1X"));
+	}
+
+	public void testHtmlDecodeSup2()
+	{
+		assertEquals("\u00B2", htmlCodec.decode("&sup2;"));
+		assertEquals("\u00B2X", htmlCodec.decode("&sup2;X"));
+		assertEquals("\u00B2", htmlCodec.decode("&sup2"));
+		assertEquals("\u00B2X", htmlCodec.decode("&sup2X"));
+	}
+
+	public void testHtmlDecodeSup3()
+	{
+		assertEquals("\u00B3", htmlCodec.decode("&sup3;"));
+		assertEquals("\u00B3X", htmlCodec.decode("&sup3;X"));
+		assertEquals("\u00B3", htmlCodec.decode("&sup3"));
+		assertEquals("\u00B3X", htmlCodec.decode("&sup3X"));
+	}
+
+	public void testHtmlDecodeSup()
+	{
+		assertEquals("\u2283", htmlCodec.decode("⊃"));
+		assertEquals("\u2283X", htmlCodec.decode("⊃X"));
+		assertEquals("\u2283", htmlCodec.decode("&sup"));
+		assertEquals("\u2283X", htmlCodec.decode("&supX"));
+	}
+
+	public void testHtmlDecodeSupe()
+	{
+		assertEquals("\u2287", htmlCodec.decode("⊇"));
+		assertEquals("\u2287X", htmlCodec.decode("⊇X"));
+		assertEquals("\u2287", htmlCodec.decode("&supe"));
+		assertEquals("\u2287X", htmlCodec.decode("&supeX"));
+	}
+
+	public void testHtmlDecodePi()
+	{
+		assertEquals("\u03C0", htmlCodec.decode("π"));
+		assertEquals("\u03C0X", htmlCodec.decode("πX"));
+		assertEquals("\u03C0", htmlCodec.decode("&pi"));
+		assertEquals("\u03C0X", htmlCodec.decode("&piX"));
+	}
+
+	public void testHtmlDecodePiv()
+	{
+		assertEquals("\u03D6", htmlCodec.decode("ϖ"));
+		assertEquals("\u03D6X", htmlCodec.decode("ϖX"));
+		assertEquals("\u03D6", htmlCodec.decode("&piv"));
+		assertEquals("\u03D6X", htmlCodec.decode("&pivX"));
+	}
+
+	public void testHtmlDecodeTheta()
+	{
+		assertEquals("\u03B8", htmlCodec.decode("θ"));
+		assertEquals("\u03B8X", htmlCodec.decode("θX"));
+		assertEquals("\u03B8", htmlCodec.decode("&theta"));
+		assertEquals("\u03B8X", htmlCodec.decode("&thetaX"));
+	}
+
+	public void testHtmlDecodeThetasym()
+	{
+		assertEquals("\u03D1", htmlCodec.decode("ϑ"));
+		assertEquals("\u03D1X", htmlCodec.decode("ϑX"));
+		assertEquals("\u03D1", htmlCodec.decode("&thetasym"));
+		assertEquals("\u03D1X", htmlCodec.decode("&thetasymX"));
+	}
+
+	public void testPercentDecode()
+	{
+        	assertEquals( "<", percentCodec.decode("%3c") );
+	}
+
+	public void testJavaScriptDecodeBackSlashHex()
+	{
+        	assertEquals( "<", javaScriptCodec.decode("\\x3c") );
+	}
+        
+	public void testVBScriptDecode()
+	{
+        	assertEquals( "<", vbScriptCodec.decode("\"<") );
+	}
+
+	public void testCSSDecode()
+	{
+        	assertEquals("<", cssCodec.decode("\\<") );
+	}
+
+	public void testCSSDecodeHexNoSpace()
+	{
+        	assertEquals("Axyz", cssCodec.decode("\\41xyz") );
+	}
+
+	public void testCSSDecodeZeroHexNoSpace()
+	{
+        	assertEquals("Aabc", cssCodec.decode("\\000041abc") );
+	}
+
+	public void testCSSDecodeHexSpace()
+	{
+        	assertEquals("Aabc", cssCodec.decode("\\41 abc") );
+	}
+
+	public void testCSSDecodeNL()
+	{
+        	assertEquals("abcxyz", cssCodec.decode("abc\\\nxyz") );
+	}
+
+	public void testCSSDecodeCRNL()
+	{
+        	assertEquals("abcxyz", cssCodec.decode("abc\\\r\nxyz") );
+	}
+
+	public void testMySQLANSIDecode()
+	{
+        	assertEquals( "\'", mySQLCodecANSI.decode("\'\'") );
+	}
+
+	public void testMySQLStandardDecode()
+	{
+        	assertEquals( "<", mySQLCodecStandard.decode("\\<") );
+	}
+
+	public void testOracleDecode()
+	{
+        	assertEquals( "\'", oracleCodec.decode("\'\'") );
+	}
+
+	public void testUnixDecode()
+	{
+        	assertEquals( "<", unixCodec.decode("\\<") );
+	}
+
+        public void testWindowsDecode()
+	{
+        	assertEquals( "<", windowsCodec.decode("^<") );
+	}
+	
+	public void testHtmlDecodeCharLessThan()
+	{
+        	assertEquals( LESS_THAN, htmlCodec.decodeCharacter(new PushbackString("<")) );
+	}
+
+	public void testPercentDecodeChar()
+	{
+        	assertEquals( LESS_THAN, percentCodec.decodeCharacter(new PushbackString("%3c") ));
+	}
+
+        public void testJavaScriptDecodeCharBackSlashHex()
+	{
+        	assertEquals( LESS_THAN, javaScriptCodec.decodeCharacter(new PushbackString("\\x3c") ));
+	}
+        
+	public void testVBScriptDecodeChar()
+	{
+        	assertEquals( LESS_THAN, vbScriptCodec.decodeCharacter(new PushbackString("\"<") ));
+	}
+
+	public void testCSSDecodeCharBackSlashHex()
+	{
+        	assertEquals( LESS_THAN, cssCodec.decodeCharacter(new PushbackString("\\3c") ));
+	}
+
+	public void testMySQLANSIDecodCharQuoteQuote()
+	{
+        	assertEquals( SINGLE_QUOTE, mySQLCodecANSI.decodeCharacter(new PushbackString("\'\'") ));
+	}
+
+        public void testMySQLStandardDecodeCharBackSlashLessThan()
+	{
+        	assertEquals( LESS_THAN, mySQLCodecStandard.decodeCharacter(new PushbackString("\\<") ));
+	}
+
+	public void testOracleDecodeCharBackSlashLessThan()
+	{
+        	assertEquals( SINGLE_QUOTE, oracleCodec.decodeCharacter(new PushbackString("\'\'") ));
+	}
+
+        public void testUnixDecodeCharBackSlashLessThan()
+	{
+        	assertEquals( LESS_THAN, unixCodec.decodeCharacter(new PushbackString("\\<") ));
+	}
+
+        public void testWindowsDecodeCharCarrotLessThan()
+	{
+        	assertEquals( LESS_THAN, windowsCodec.decodeCharacter(new PushbackString("^<") ));
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/codecs/.svn/text-base/HashTrieTest.java.svn-base b/src/test/java/org/owasp/esapi/codecs/.svn/text-base/HashTrieTest.java.svn-base
new file mode 100644
index 0000000..b2db910
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/.svn/text-base/HashTrieTest.java.svn-base
@@ -0,0 +1,213 @@
+package org.owasp.esapi.codecs;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class HashTrieTest extends TestCase
+{
+	private static final Class<HashTrieTest> CLASS = HashTrieTest.class;
+
+	public HashTrieTest(String testName)
+	{
+		super(testName);
+	}
+
+	public void testSingleInsertLookup()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("true", Boolean.TRUE);
+		assertEquals(Boolean.TRUE, trie.get("true"));
+		assertNull(trie.get("not there"));
+		assertNull(trie.get("tru"));
+		assertNull(trie.get("trueX"));
+		assertEquals("true".length(), trie.getMaxKeyLength());
+	}
+
+	public void testEmpty()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		assertNull(trie.get("true"));
+		assertNull(trie.get("false"));
+		assertNull(trie.get(""));
+		assertTrue(trie.getMaxKeyLength()<0);
+	}
+
+	public void testTwoInsertLookup()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(Boolean.TRUE, trie.get("true"));
+		assertEquals(Boolean.FALSE, trie.get("false"));
+		assertEquals("false".length(),trie.getMaxKeyLength());
+	}
+
+	public void testMatchingPrefix()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("pretrue", Boolean.TRUE);
+		trie.put("prefalse", Boolean.FALSE);
+		assertEquals(Boolean.TRUE, trie.get("pretrue"));
+		assertEquals(Boolean.FALSE, trie.get("prefalse"));
+	}
+
+	public void testPrefixIsValidKey()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("pre", Boolean.TRUE);
+		trie.put("prefalse", Boolean.FALSE);
+		assertEquals(Boolean.TRUE, trie.get("pre"));
+		assertEquals(Boolean.FALSE, trie.get("prefalse"));
+	}
+
+	public void testDuplicateAdd()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		assertNull(trie.put("dup", Boolean.TRUE));
+		assertTrue(trie.put("dup", Boolean.FALSE));
+		assertFalse(trie.get("dup"));
+	}
+
+	public void testTwoInsertLongestLookup()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		Entry<CharSequence,Boolean> entry;
+
+		trie.put("true", Boolean.TRUE);
+		trie.put("true idea", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+
+		assertNotNull((entry = trie.getLongestMatch("true")));
+		assertEquals("true", entry.getKey());
+		assertTrue(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("false")));
+		assertEquals("false", entry.getKey());
+		assertFalse(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("truer")));
+		assertEquals("true", entry.getKey());
+		assertTrue(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("true to form")));
+		assertEquals("true", entry.getKey());
+		assertTrue(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("false result")));
+		assertEquals("false", entry.getKey());
+		assertFalse(entry.getValue());
+
+		assertNull(trie.getLongestMatch("not there"));
+		assertNull(trie.getLongestMatch("tru"));
+		assertNull(trie.getLongestMatch("fals"));
+	}
+
+	public void testContainsKey()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertTrue(trie.containsKey("true"));
+		assertTrue(trie.containsKey("false"));
+		assertFalse(trie.containsKey("not there"));
+	}
+
+	public void testContainsValue()
+	{
+		HashTrie<Integer> trie = new HashTrie<Integer>();
+
+		trie.put("one", 1);
+		trie.put("two", 2);
+		assertTrue(trie.containsValue(1));
+		assertTrue(trie.containsValue(2));
+		assertFalse(trie.containsValue(3));
+	}
+
+	public void testKeySet()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashSet<CharSequence> expected = new HashSet<CharSequence>(2);
+
+		expected.add("true");
+		expected.add("false");
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(expected,trie.keySet());
+	}
+
+	public void testValues()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		ArrayList<Boolean> actual;
+		ArrayList<Boolean> expected = new ArrayList<Boolean>(2);
+
+		expected.add(Boolean.TRUE);
+		expected.add(Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		actual = new ArrayList<Boolean>(trie.values());
+		Collections.sort(actual);
+		Collections.sort(expected);
+		assertEquals(expected,actual);
+	}
+
+	public void testEntrySet()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashMap<CharSequence,Boolean> equivMap = new HashMap<CharSequence,Boolean>(2);
+
+		equivMap.put("true",Boolean.TRUE);
+		equivMap.put("false",Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(equivMap.entrySet(),trie.entrySet());
+	}
+
+	public void testEquals()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashMap<CharSequence,Boolean> equivMap = new HashMap<CharSequence,Boolean>(2);
+
+		equivMap.put("true",Boolean.TRUE);
+		equivMap.put("false",Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertTrue(trie.equals(equivMap));
+	}
+
+	public void testHashCode()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashMap<CharSequence,Boolean> equivMap = new HashMap<CharSequence,Boolean>(2);
+
+		equivMap.put("true",Boolean.TRUE);
+		equivMap.put("false",Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(equivMap.hashCode(),trie.hashCode());
+	}
+
+	/**
+	 * Create a test suite with just this test.
+	 * @return A test swuite with just this test.
+	 */
+	public static Test suite()
+	{
+		TestSuite suite = new TestSuite(CLASS);
+		return suite;
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/codecs/.svn/text-base/XMLEntityCodecTest.java.svn-base b/src/test/java/org/owasp/esapi/codecs/.svn/text-base/XMLEntityCodecTest.java.svn-base
new file mode 100644
index 0000000..10424c6
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/.svn/text-base/XMLEntityCodecTest.java.svn-base
@@ -0,0 +1,253 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ */
+package org.owasp.esapi.codecs;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class XMLEntityCodecTest extends TestCase
+{
+	private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+	private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+	private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t";
+	private static final Set<Character> UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR);
+	private XMLEntityCodec codec = null;
+
+	protected void setUp()
+	{
+		codec = new XMLEntityCodec();
+	}
+
+	protected void tearDown()
+	{
+		codec = null;
+	}
+
+	public void testEncodeUnencoded()
+	{
+		StringBuilder sb = new StringBuilder("AB_YZ");
+		String str;
+
+		for(char ch : UNENCODED_SET)
+		{
+			sb.setCharAt(2,ch);
+			str = sb.toString();
+			assertEquals(str, codec.encode(EMPTY_CHAR_ARRAY, str));
+		}
+	}
+
+	public void testEncodeOthers()
+	{
+		StringBuilder inSb = new StringBuilder("AB_YZ");
+		StringBuilder outSb = new StringBuilder("AB&#x");
+		String in;
+		String expected;
+		String result;
+		int outSbBaseLen = outSb.length();
+		String out;
+
+		for(int c=Character.MIN_VALUE;c<=Character.MAX_VALUE;c++)
+		{
+			char ch = (char)c;
+			if(UNENCODED_SET.contains(ch))
+				continue;
+			inSb.setCharAt(2,ch);
+			in = inSb.toString();
+			outSb.append(Integer.toHexString(c));
+			outSb.append(";YZ");
+			expected = outSb.toString();
+			result = codec.encode(EMPTY_CHAR_ARRAY,in);
+			assertEquals(expected, result);
+			outSb.setLength(outSbBaseLen);
+		}
+	}
+
+	public void testDecodeUnencoded()
+	{
+		StringBuilder sb = new StringBuilder("AB_YZ");
+		String str;
+
+		for(char ch : UNENCODED_SET)
+		{
+			sb.setCharAt(2,ch);
+			str = sb.toString();
+			assertEquals(str, codec.decode(str));
+		}
+	}
+
+	public void testDecodeHex()
+	{
+		StringBuilder expectedSb = new StringBuilder("AB_YZ");
+		StringBuilder inSb = new StringBuilder("AB&#x");
+		String in;
+		String expected;
+		String result;
+		int inSbBaseLen = inSb.length();
+
+		for(int c=Character.MIN_VALUE;c<=Character.MAX_VALUE;c++)
+		{
+			char ch = (char)c;
+			expectedSb.setCharAt(2,ch);
+			expected = expectedSb.toString();
+			inSb.append(Integer.toHexString(c));
+			inSb.append(";YZ");
+			in = inSb.toString();
+			result = codec.decode(in);
+			assertEquals(expected, result);
+			inSb.setLength(inSbBaseLen);
+		}
+	}
+
+	public void testDecodeDec()
+	{
+		StringBuilder expectedSb = new StringBuilder("AB_YZ");
+		StringBuilder inSb = new StringBuilder("AB&#");
+		String in;
+		String expected;
+		String result;
+		int inSbBaseLen = inSb.length();
+
+		for(int c=Character.MIN_VALUE;c<=Character.MAX_VALUE;c++)
+		{
+			char ch = (char)c;
+			expectedSb.setCharAt(2,ch);
+			expected = expectedSb.toString();
+			inSb.append(Integer.toString(c));
+			inSb.append(";YZ");
+			in = inSb.toString();
+			result = codec.decode(in);
+			assertEquals(expected, result);
+			inSb.setLength(inSbBaseLen);
+		}
+	}
+
+	public void testDecodeLt()
+	{
+		String in = "AB<YZ";
+		String expected = "AB<YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeGt()
+	{
+		String in = "AB>YZ";
+		String expected = "AB>YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeAmp()
+	{
+		String in = "AB&YZ";
+		String expected = "AB&YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeApos()
+	{
+		String in = "AB'YZ";
+		String expected = "AB'YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeQuot()
+	{
+		String in = "AB"YZ";
+		String expected = "AB\"YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedTail()
+	{
+		String in = "AB"";
+		String expected = "AB\"";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedHead()
+	{
+		String in = ""YZ";
+		String expected = "\"YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedLone()
+	{
+		String in = """;
+		String expected = "\"";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedNoSemiColonTail()
+	{
+		String in = "AB&quot";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedNoSemiColonHead()
+	{
+		String in = "&quotYZ";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedNoSemiColonLone()
+	{
+		String in = "&quot";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedInvalidTail()
+	{
+		String in = "AB£";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedInvalidHead()
+	{
+		String in = "£YZ";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedInvalidLone()
+	{
+		String in = "£";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/codecs/CodecTest.java b/src/test/java/org/owasp/esapi/codecs/CodecTest.java
new file mode 100644
index 0000000..9aaa760
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/CodecTest.java
@@ -0,0 +1,624 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.codecs;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+
+/**
+ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
+ *         href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @since June 1, 2007
+ */
+public class CodecTest extends TestCase {
+
+    private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+    private static final Character LESS_THAN = Character.valueOf('<');
+    private static final Character SINGLE_QUOTE = Character.valueOf('\'');
+    private HTMLEntityCodec htmlCodec = new HTMLEntityCodec();
+    private PercentCodec percentCodec = new PercentCodec();
+    private JavaScriptCodec javaScriptCodec = new JavaScriptCodec();
+    private VBScriptCodec vbScriptCodec = new VBScriptCodec();
+    private CSSCodec cssCodec = new CSSCodec();
+    private MySQLCodec mySQLCodecANSI = new MySQLCodec( MySQLCodec.ANSI_MODE );
+    private MySQLCodec mySQLCodecStandard = new MySQLCodec( MySQLCodec.MYSQL_MODE );
+    private OracleCodec oracleCodec = new OracleCodec();
+    private UnixCodec unixCodec = new UnixCodec();
+    private WindowsCodec windowsCodec = new WindowsCodec();
+
+    /**
+     * Instantiates a new access reference map test.
+     * 
+     * @param testName
+     *            the test name
+     */
+    public CodecTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+        // none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+        // none
+    }
+
+    /**
+     * Suite.
+     * 
+     * @return the test
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(CodecTest.class);
+        return suite;
+    }
+
+	public void testHtmlEncode()
+	{
+        	assertEquals( "test", htmlCodec.encode( EMPTY_CHAR_ARRAY, "test") );
+	}
+
+	public void testPercentEncode()
+	{
+        	assertEquals( "%3C", percentCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+
+	public void testJavaScriptEncode()
+	{
+        	assertEquals( "\\x3C", javaScriptCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testVBScriptEncode()
+	{
+        	assertEquals( "chrw(60)", vbScriptCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testCSSEncode()
+	{
+        	assertEquals( "\\3c ", cssCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testCSSInvalidCodepointDecode()
+	{
+		assertEquals("\uFFFDg", cssCodec.decode("\\abcdefg") );
+	}
+
+	public void testMySQLANSCIEncode()
+	{
+        	assertEquals( "\'\'", mySQLCodecANSI.encode(EMPTY_CHAR_ARRAY, "\'") );
+	}
+
+	public void testMySQLStandardEncode()
+	{
+        	assertEquals( "\\<", mySQLCodecStandard.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testOracleEncode()
+	{
+        	assertEquals( "\'\'", oracleCodec.encode(EMPTY_CHAR_ARRAY, "\'") );
+	}
+
+	public void testUnixEncode()
+	{
+        	assertEquals( "\\<", unixCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	public void testWindowsEncode()
+	{
+        	assertEquals( "^<", windowsCodec.encode(EMPTY_CHAR_ARRAY, "<") );
+	}
+
+	
+	public void testHtmlEncodeChar()
+	{
+        	assertEquals( "<", htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testHtmlEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "&#x100;";
+		String result;
+
+        	result = htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testHtmlEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "&#x100;";
+		String result;
+
+        	result = htmlCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testPercentEncodeChar()
+	{
+        	assertEquals( "%3C", percentCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testPercentEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "%C4%80";
+		String result;
+
+        	result = percentCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testPercentEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "%C4%80";
+		String result;
+
+        	result = percentCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+		// UTF-8 encoded and then percent escaped
+        	assertEquals(expected, result);
+	}
+
+	public void testJavaScriptEncodeChar()
+	{
+        	assertEquals( "\\x3C", javaScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testJavaScriptEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\u0100";
+		String result;
+
+        	result = javaScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testJavaScriptEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\u0100";
+		String result;
+
+        	result = javaScriptCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+        
+	public void testVBScriptEncodeChar()
+	{
+        	assertEquals( "chrw(60)", vbScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testVBScriptEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		// FIXME I don't know vb...
+		// String expected = "\\u0100";
+		String result;
+
+        	result = vbScriptCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	//assertEquals(expected,result);
+	}
+
+	public void testVBScriptEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		// FIXME I don't know vb...
+		// String expected = "chrw(0x100)";
+		String result;
+
+        	result = vbScriptCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	// assertEquals(expected,result);
+	}
+
+	public void testCSSEncodeChar()
+	{
+        	assertEquals( "\\3c ", cssCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testCSSEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\100 ";
+		String result;
+
+        	result = cssCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testCSSEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\100 ";
+		String result;
+
+        	result = cssCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testMySQLANSIEncodeChar()
+	{
+        	assertEquals( "\'\'", mySQLCodecANSI.encodeCharacter(EMPTY_CHAR_ARRAY, SINGLE_QUOTE));
+	}
+
+	public void testMySQLStandardEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = mySQLCodecStandard.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testMySQLStandardEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = mySQLCodecStandard.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testMySQLStandardEncodeChar()
+	{
+        	assertEquals( "\\<", mySQLCodecStandard.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testOracleEncodeChar()
+	{
+        	assertEquals( "\'\'", oracleCodec.encodeCharacter(EMPTY_CHAR_ARRAY, SINGLE_QUOTE) );
+	}
+
+	public void testUnixEncodeChar()
+	{
+        	assertEquals( "\\<", unixCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testUnixEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = unixCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testUnixEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "\\" + in;
+		String result;
+
+        	result = unixCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testWindowsEncodeChar()
+	{
+        	assertEquals( "^<", windowsCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) );
+	}
+
+	public void testWindowsEncodeChar0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "^" + in;
+		String result;
+
+        	result = windowsCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+
+	public void testWindowsEncodeStr0x100()
+	{
+		char in = 0x100;
+		String inStr = Character.toString(in);
+		String expected = "^" + in;
+		String result;
+
+        	result = windowsCodec.encode(EMPTY_CHAR_ARRAY, inStr);
+		// this should be escaped
+        	assertFalse(inStr.equals(result));
+        	assertEquals(expected,result);
+	}
+	
+	public void testHtmlDecodeDecimalEntities()
+	{
+        	assertEquals( "test!", htmlCodec.decode("test!") );
+	}
+
+	public void testHtmlDecodeHexEntitites()
+	{
+        	assertEquals( "test!", htmlCodec.decode("&#x74;&#x65;&#x73;&#x74;!") );
+	}
+
+	public void testHtmlDecodeInvalidAttribute()
+	{
+        	assertEquals( "&jeff;", htmlCodec.decode("&jeff;") );
+	}
+
+	public void testHtmlDecodeAmp()
+	{
+		assertEquals("&", htmlCodec.decode("&"));
+		assertEquals("&X", htmlCodec.decode("&X"));
+		assertEquals("&", htmlCodec.decode("&amp"));
+		assertEquals("&X", htmlCodec.decode("&ampX"));
+	}
+
+	public void testHtmlDecodeLt()
+	{
+		assertEquals("<", htmlCodec.decode("<"));
+		assertEquals("<X", htmlCodec.decode("<X"));
+		assertEquals("<", htmlCodec.decode("&lt"));
+		assertEquals("<X", htmlCodec.decode("&ltX"));
+	}
+
+	public void testHtmlDecodeSup1()
+	{
+		assertEquals("\u00B9", htmlCodec.decode("&sup1;"));
+		assertEquals("\u00B9X", htmlCodec.decode("&sup1;X"));
+		assertEquals("\u00B9", htmlCodec.decode("&sup1"));
+		assertEquals("\u00B9X", htmlCodec.decode("&sup1X"));
+	}
+
+	public void testHtmlDecodeSup2()
+	{
+		assertEquals("\u00B2", htmlCodec.decode("&sup2;"));
+		assertEquals("\u00B2X", htmlCodec.decode("&sup2;X"));
+		assertEquals("\u00B2", htmlCodec.decode("&sup2"));
+		assertEquals("\u00B2X", htmlCodec.decode("&sup2X"));
+	}
+
+	public void testHtmlDecodeSup3()
+	{
+		assertEquals("\u00B3", htmlCodec.decode("&sup3;"));
+		assertEquals("\u00B3X", htmlCodec.decode("&sup3;X"));
+		assertEquals("\u00B3", htmlCodec.decode("&sup3"));
+		assertEquals("\u00B3X", htmlCodec.decode("&sup3X"));
+	}
+
+	public void testHtmlDecodeSup()
+	{
+		assertEquals("\u2283", htmlCodec.decode("⊃"));
+		assertEquals("\u2283X", htmlCodec.decode("⊃X"));
+		assertEquals("\u2283", htmlCodec.decode("&sup"));
+		assertEquals("\u2283X", htmlCodec.decode("&supX"));
+	}
+
+	public void testHtmlDecodeSupe()
+	{
+		assertEquals("\u2287", htmlCodec.decode("⊇"));
+		assertEquals("\u2287X", htmlCodec.decode("⊇X"));
+		assertEquals("\u2287", htmlCodec.decode("&supe"));
+		assertEquals("\u2287X", htmlCodec.decode("&supeX"));
+	}
+
+	public void testHtmlDecodePi()
+	{
+		assertEquals("\u03C0", htmlCodec.decode("π"));
+		assertEquals("\u03C0X", htmlCodec.decode("πX"));
+		assertEquals("\u03C0", htmlCodec.decode("&pi"));
+		assertEquals("\u03C0X", htmlCodec.decode("&piX"));
+	}
+
+	public void testHtmlDecodePiv()
+	{
+		assertEquals("\u03D6", htmlCodec.decode("ϖ"));
+		assertEquals("\u03D6X", htmlCodec.decode("ϖX"));
+		assertEquals("\u03D6", htmlCodec.decode("&piv"));
+		assertEquals("\u03D6X", htmlCodec.decode("&pivX"));
+	}
+
+	public void testHtmlDecodeTheta()
+	{
+		assertEquals("\u03B8", htmlCodec.decode("θ"));
+		assertEquals("\u03B8X", htmlCodec.decode("θX"));
+		assertEquals("\u03B8", htmlCodec.decode("&theta"));
+		assertEquals("\u03B8X", htmlCodec.decode("&thetaX"));
+	}
+
+	public void testHtmlDecodeThetasym()
+	{
+		assertEquals("\u03D1", htmlCodec.decode("ϑ"));
+		assertEquals("\u03D1X", htmlCodec.decode("ϑX"));
+		assertEquals("\u03D1", htmlCodec.decode("&thetasym"));
+		assertEquals("\u03D1X", htmlCodec.decode("&thetasymX"));
+	}
+
+	public void testPercentDecode()
+	{
+        	assertEquals( "<", percentCodec.decode("%3c") );
+	}
+
+	public void testJavaScriptDecodeBackSlashHex()
+	{
+        	assertEquals( "<", javaScriptCodec.decode("\\x3c") );
+	}
+        
+	public void testVBScriptDecode()
+	{
+        	assertEquals( "<", vbScriptCodec.decode("\"<") );
+	}
+
+	public void testCSSDecode()
+	{
+        	assertEquals("<", cssCodec.decode("\\<") );
+	}
+
+	public void testCSSDecodeHexNoSpace()
+	{
+        	assertEquals("Axyz", cssCodec.decode("\\41xyz") );
+	}
+
+	public void testCSSDecodeZeroHexNoSpace()
+	{
+        	assertEquals("Aabc", cssCodec.decode("\\000041abc") );
+	}
+
+	public void testCSSDecodeHexSpace()
+	{
+        	assertEquals("Aabc", cssCodec.decode("\\41 abc") );
+	}
+
+	public void testCSSDecodeNL()
+	{
+        	assertEquals("abcxyz", cssCodec.decode("abc\\\nxyz") );
+	}
+
+	public void testCSSDecodeCRNL()
+	{
+        	assertEquals("abcxyz", cssCodec.decode("abc\\\r\nxyz") );
+	}
+
+	public void testMySQLANSIDecode()
+	{
+        	assertEquals( "\'", mySQLCodecANSI.decode("\'\'") );
+	}
+
+	public void testMySQLStandardDecode()
+	{
+        	assertEquals( "<", mySQLCodecStandard.decode("\\<") );
+	}
+
+	public void testOracleDecode()
+	{
+        	assertEquals( "\'", oracleCodec.decode("\'\'") );
+	}
+
+	public void testUnixDecode()
+	{
+        	assertEquals( "<", unixCodec.decode("\\<") );
+	}
+
+        public void testWindowsDecode()
+	{
+        	assertEquals( "<", windowsCodec.decode("^<") );
+	}
+	
+	public void testHtmlDecodeCharLessThan()
+	{
+        	assertEquals( LESS_THAN, htmlCodec.decodeCharacter(new PushbackString("<")) );
+	}
+
+	public void testPercentDecodeChar()
+	{
+        	assertEquals( LESS_THAN, percentCodec.decodeCharacter(new PushbackString("%3c") ));
+	}
+
+        public void testJavaScriptDecodeCharBackSlashHex()
+	{
+        	assertEquals( LESS_THAN, javaScriptCodec.decodeCharacter(new PushbackString("\\x3c") ));
+	}
+        
+	public void testVBScriptDecodeChar()
+	{
+        	assertEquals( LESS_THAN, vbScriptCodec.decodeCharacter(new PushbackString("\"<") ));
+	}
+
+	public void testCSSDecodeCharBackSlashHex()
+	{
+        	assertEquals( LESS_THAN, cssCodec.decodeCharacter(new PushbackString("\\3c") ));
+	}
+
+	public void testMySQLANSIDecodCharQuoteQuote()
+	{
+        	assertEquals( SINGLE_QUOTE, mySQLCodecANSI.decodeCharacter(new PushbackString("\'\'") ));
+	}
+
+        public void testMySQLStandardDecodeCharBackSlashLessThan()
+	{
+        	assertEquals( LESS_THAN, mySQLCodecStandard.decodeCharacter(new PushbackString("\\<") ));
+	}
+
+	public void testOracleDecodeCharBackSlashLessThan()
+	{
+        	assertEquals( SINGLE_QUOTE, oracleCodec.decodeCharacter(new PushbackString("\'\'") ));
+	}
+
+        public void testUnixDecodeCharBackSlashLessThan()
+	{
+        	assertEquals( LESS_THAN, unixCodec.decodeCharacter(new PushbackString("\\<") ));
+	}
+
+        public void testWindowsDecodeCharCarrotLessThan()
+	{
+        	assertEquals( LESS_THAN, windowsCodec.decodeCharacter(new PushbackString("^<") ));
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/codecs/HashTrieTest.java b/src/test/java/org/owasp/esapi/codecs/HashTrieTest.java
new file mode 100644
index 0000000..b2db910
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/HashTrieTest.java
@@ -0,0 +1,213 @@
+package org.owasp.esapi.codecs;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class HashTrieTest extends TestCase
+{
+	private static final Class<HashTrieTest> CLASS = HashTrieTest.class;
+
+	public HashTrieTest(String testName)
+	{
+		super(testName);
+	}
+
+	public void testSingleInsertLookup()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("true", Boolean.TRUE);
+		assertEquals(Boolean.TRUE, trie.get("true"));
+		assertNull(trie.get("not there"));
+		assertNull(trie.get("tru"));
+		assertNull(trie.get("trueX"));
+		assertEquals("true".length(), trie.getMaxKeyLength());
+	}
+
+	public void testEmpty()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		assertNull(trie.get("true"));
+		assertNull(trie.get("false"));
+		assertNull(trie.get(""));
+		assertTrue(trie.getMaxKeyLength()<0);
+	}
+
+	public void testTwoInsertLookup()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(Boolean.TRUE, trie.get("true"));
+		assertEquals(Boolean.FALSE, trie.get("false"));
+		assertEquals("false".length(),trie.getMaxKeyLength());
+	}
+
+	public void testMatchingPrefix()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("pretrue", Boolean.TRUE);
+		trie.put("prefalse", Boolean.FALSE);
+		assertEquals(Boolean.TRUE, trie.get("pretrue"));
+		assertEquals(Boolean.FALSE, trie.get("prefalse"));
+	}
+
+	public void testPrefixIsValidKey()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("pre", Boolean.TRUE);
+		trie.put("prefalse", Boolean.FALSE);
+		assertEquals(Boolean.TRUE, trie.get("pre"));
+		assertEquals(Boolean.FALSE, trie.get("prefalse"));
+	}
+
+	public void testDuplicateAdd()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		assertNull(trie.put("dup", Boolean.TRUE));
+		assertTrue(trie.put("dup", Boolean.FALSE));
+		assertFalse(trie.get("dup"));
+	}
+
+	public void testTwoInsertLongestLookup()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		Entry<CharSequence,Boolean> entry;
+
+		trie.put("true", Boolean.TRUE);
+		trie.put("true idea", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+
+		assertNotNull((entry = trie.getLongestMatch("true")));
+		assertEquals("true", entry.getKey());
+		assertTrue(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("false")));
+		assertEquals("false", entry.getKey());
+		assertFalse(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("truer")));
+		assertEquals("true", entry.getKey());
+		assertTrue(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("true to form")));
+		assertEquals("true", entry.getKey());
+		assertTrue(entry.getValue());
+
+		assertNotNull((entry = trie.getLongestMatch("false result")));
+		assertEquals("false", entry.getKey());
+		assertFalse(entry.getValue());
+
+		assertNull(trie.getLongestMatch("not there"));
+		assertNull(trie.getLongestMatch("tru"));
+		assertNull(trie.getLongestMatch("fals"));
+	}
+
+	public void testContainsKey()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertTrue(trie.containsKey("true"));
+		assertTrue(trie.containsKey("false"));
+		assertFalse(trie.containsKey("not there"));
+	}
+
+	public void testContainsValue()
+	{
+		HashTrie<Integer> trie = new HashTrie<Integer>();
+
+		trie.put("one", 1);
+		trie.put("two", 2);
+		assertTrue(trie.containsValue(1));
+		assertTrue(trie.containsValue(2));
+		assertFalse(trie.containsValue(3));
+	}
+
+	public void testKeySet()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashSet<CharSequence> expected = new HashSet<CharSequence>(2);
+
+		expected.add("true");
+		expected.add("false");
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(expected,trie.keySet());
+	}
+
+	public void testValues()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		ArrayList<Boolean> actual;
+		ArrayList<Boolean> expected = new ArrayList<Boolean>(2);
+
+		expected.add(Boolean.TRUE);
+		expected.add(Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		actual = new ArrayList<Boolean>(trie.values());
+		Collections.sort(actual);
+		Collections.sort(expected);
+		assertEquals(expected,actual);
+	}
+
+	public void testEntrySet()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashMap<CharSequence,Boolean> equivMap = new HashMap<CharSequence,Boolean>(2);
+
+		equivMap.put("true",Boolean.TRUE);
+		equivMap.put("false",Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(equivMap.entrySet(),trie.entrySet());
+	}
+
+	public void testEquals()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashMap<CharSequence,Boolean> equivMap = new HashMap<CharSequence,Boolean>(2);
+
+		equivMap.put("true",Boolean.TRUE);
+		equivMap.put("false",Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertTrue(trie.equals(equivMap));
+	}
+
+	public void testHashCode()
+	{
+		HashTrie<Boolean> trie = new HashTrie<Boolean>();
+		HashMap<CharSequence,Boolean> equivMap = new HashMap<CharSequence,Boolean>(2);
+
+		equivMap.put("true",Boolean.TRUE);
+		equivMap.put("false",Boolean.FALSE);
+		trie.put("true", Boolean.TRUE);
+		trie.put("false", Boolean.FALSE);
+		assertEquals(equivMap.hashCode(),trie.hashCode());
+	}
+
+	/**
+	 * Create a test suite with just this test.
+	 * @return A test swuite with just this test.
+	 */
+	public static Test suite()
+	{
+		TestSuite suite = new TestSuite(CLASS);
+		return suite;
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/codecs/XMLEntityCodecTest.java b/src/test/java/org/owasp/esapi/codecs/XMLEntityCodecTest.java
new file mode 100644
index 0000000..10424c6
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/codecs/XMLEntityCodecTest.java
@@ -0,0 +1,253 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ */
+package org.owasp.esapi.codecs;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.owasp.esapi.util.CollectionsUtil;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class XMLEntityCodecTest extends TestCase
+{
+	private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+	private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+	private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t";
+	private static final Set<Character> UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR);
+	private XMLEntityCodec codec = null;
+
+	protected void setUp()
+	{
+		codec = new XMLEntityCodec();
+	}
+
+	protected void tearDown()
+	{
+		codec = null;
+	}
+
+	public void testEncodeUnencoded()
+	{
+		StringBuilder sb = new StringBuilder("AB_YZ");
+		String str;
+
+		for(char ch : UNENCODED_SET)
+		{
+			sb.setCharAt(2,ch);
+			str = sb.toString();
+			assertEquals(str, codec.encode(EMPTY_CHAR_ARRAY, str));
+		}
+	}
+
+	public void testEncodeOthers()
+	{
+		StringBuilder inSb = new StringBuilder("AB_YZ");
+		StringBuilder outSb = new StringBuilder("AB&#x");
+		String in;
+		String expected;
+		String result;
+		int outSbBaseLen = outSb.length();
+		String out;
+
+		for(int c=Character.MIN_VALUE;c<=Character.MAX_VALUE;c++)
+		{
+			char ch = (char)c;
+			if(UNENCODED_SET.contains(ch))
+				continue;
+			inSb.setCharAt(2,ch);
+			in = inSb.toString();
+			outSb.append(Integer.toHexString(c));
+			outSb.append(";YZ");
+			expected = outSb.toString();
+			result = codec.encode(EMPTY_CHAR_ARRAY,in);
+			assertEquals(expected, result);
+			outSb.setLength(outSbBaseLen);
+		}
+	}
+
+	public void testDecodeUnencoded()
+	{
+		StringBuilder sb = new StringBuilder("AB_YZ");
+		String str;
+
+		for(char ch : UNENCODED_SET)
+		{
+			sb.setCharAt(2,ch);
+			str = sb.toString();
+			assertEquals(str, codec.decode(str));
+		}
+	}
+
+	public void testDecodeHex()
+	{
+		StringBuilder expectedSb = new StringBuilder("AB_YZ");
+		StringBuilder inSb = new StringBuilder("AB&#x");
+		String in;
+		String expected;
+		String result;
+		int inSbBaseLen = inSb.length();
+
+		for(int c=Character.MIN_VALUE;c<=Character.MAX_VALUE;c++)
+		{
+			char ch = (char)c;
+			expectedSb.setCharAt(2,ch);
+			expected = expectedSb.toString();
+			inSb.append(Integer.toHexString(c));
+			inSb.append(";YZ");
+			in = inSb.toString();
+			result = codec.decode(in);
+			assertEquals(expected, result);
+			inSb.setLength(inSbBaseLen);
+		}
+	}
+
+	public void testDecodeDec()
+	{
+		StringBuilder expectedSb = new StringBuilder("AB_YZ");
+		StringBuilder inSb = new StringBuilder("AB&#");
+		String in;
+		String expected;
+		String result;
+		int inSbBaseLen = inSb.length();
+
+		for(int c=Character.MIN_VALUE;c<=Character.MAX_VALUE;c++)
+		{
+			char ch = (char)c;
+			expectedSb.setCharAt(2,ch);
+			expected = expectedSb.toString();
+			inSb.append(Integer.toString(c));
+			inSb.append(";YZ");
+			in = inSb.toString();
+			result = codec.decode(in);
+			assertEquals(expected, result);
+			inSb.setLength(inSbBaseLen);
+		}
+	}
+
+	public void testDecodeLt()
+	{
+		String in = "AB<YZ";
+		String expected = "AB<YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeGt()
+	{
+		String in = "AB>YZ";
+		String expected = "AB>YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeAmp()
+	{
+		String in = "AB&YZ";
+		String expected = "AB&YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeApos()
+	{
+		String in = "AB'YZ";
+		String expected = "AB'YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeQuot()
+	{
+		String in = "AB"YZ";
+		String expected = "AB\"YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedTail()
+	{
+		String in = "AB"";
+		String expected = "AB\"";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedHead()
+	{
+		String in = ""YZ";
+		String expected = "\"YZ";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedLone()
+	{
+		String in = """;
+		String expected = "\"";
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedNoSemiColonTail()
+	{
+		String in = "AB&quot";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedNoSemiColonHead()
+	{
+		String in = "&quotYZ";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedNoSemiColonLone()
+	{
+		String in = "&quot";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedInvalidTail()
+	{
+		String in = "AB£";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedInvalidHead()
+	{
+		String in = "£YZ";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+
+	public void testDecodeNamedInvalidLone()
+	{
+		String in = "£";
+		String expected = in;
+		String result = codec.decode(in);
+		assertEquals(expected,result);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/all-wcprops b/src/test/java/org/owasp/esapi/crypto/.svn/all-wcprops
new file mode 100644
index 0000000..38afa81
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/all-wcprops
@@ -0,0 +1,53 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto
+END
+CipherTextTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java
+END
+CryptoHelperTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java
+END
+CryptoTokenTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java
+END
+CipherSpecTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java
+END
+ESAPICryptoMACByPassTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/ESAPICryptoMACByPassTest.java
+END
+PlainTextTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/PlainTextTest.java
+END
+SecurityProviderLoaderTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java
+END
+CipherTextSerializerTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto/CipherTextSerializerTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/entries b/src/test/java/org/owasp/esapi/crypto/.svn/entries
new file mode 100644
index 0000000..25d4835
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/entries
@@ -0,0 +1,300 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/crypto
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+CipherTextTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.261952Z
+7b365d8ce3f47574a0083bc266cb0433
+2013-08-31T22:37:45.146375Z
+1890
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+16519
+

+CryptoHelperTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.261952Z
+00678aa41776fa1ff755ca5d0a27a6e4
+2013-08-31T22:37:45.146375Z
+1890
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6807
+

+CryptoTokenTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.261952Z
+1bfe24383ee6dadd2f28ed13495088ae
+2013-08-29T01:00:55.710187Z
+1886
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15403
+

+CipherSpecTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.261952Z
+bac35b1f65bdf5fbd505ff5c7272a163
+2011-02-04T05:43:00.800460Z
+1704
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9551
+

+ESAPICryptoMACByPassTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.261952Z
+abe281882892ea18df7a36bed7032491
+2013-09-02T21:47:22.321274Z
+1897
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10357
+

+PlainTextTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.261952Z
+ca810bd8b9f31adba4572f6c87f4a061
+2010-02-06T17:42:36.199078Z
+1129
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4741
+

+SecurityProviderLoaderTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.265952Z
+9544230b2b928caf19a133d3efe6bdc8
+2010-03-20T04:05:33.563954Z
+1215
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5969
+

+CipherTextSerializerTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.265952Z
+ca75bbe6ad158e1dd7f799c88413da03
+2013-08-31T22:37:45.146375Z
+1890
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3093
+

diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CipherSpecTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CipherSpecTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CipherSpecTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CipherTextSerializerTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CipherTextSerializerTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CipherTextSerializerTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoHelperTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoHelperTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoHelperTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoTokenTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoTokenTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/CryptoTokenTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/SecurityProviderLoaderTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/SecurityProviderLoaderTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/prop-base/SecurityProviderLoaderTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherSpecTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherSpecTest.java.svn-base
new file mode 100644
index 0000000..b842aae
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherSpecTest.java.svn-base
@@ -0,0 +1,273 @@
+package org.owasp.esapi.crypto;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import javax.crypto.Cipher;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherSpec;
+
+/** JUnit test to test CipherSpec class. */
+public class CipherSpecTest extends TestCase {
+
+	private Cipher dfltAESCipher = null;
+	private Cipher dfltECBCipher = null;	// will be "AES/ECB/NoPadding";
+	private Cipher dfltOtherCipher = null;
+	private CipherSpec cipherSpec = null;
+	private byte[] myIV = null;
+
+	@Before public void setUp() throws Exception {
+			// This will throw ConfigurationException if IV type is not set to
+			// 'fixed', which it's not. (We have it set to 'random'.)
+		// myIV = Hex.decode( ESAPI.securityConfiguration().getFixedIV() );
+		myIV = Hex.decode( "0x000102030405060708090a0b0c0d0e0f" );
+
+		dfltAESCipher   = Cipher.getInstance("AES");
+		dfltECBCipher   = Cipher.getInstance("AES/ECB/NoPadding");
+		dfltOtherCipher = Cipher.getInstance("Blowfish/OFB8/PKCS5Padding");
+
+		assertTrue( dfltAESCipher != null );
+		assertTrue( dfltECBCipher != null );
+		assertTrue( dfltOtherCipher != null );
+
+		cipherSpec = new CipherSpec(dfltOtherCipher);
+		assertTrue( cipherSpec != null );
+	}
+
+	@After public void tearDown() throws Exception {
+    	// none
+	}
+	
+	/** Test CipherSpec(String cipherXform, int keySize, int blockSize, final byte[] iv) */
+	@Test public void testCipherSpecStringIntIntByteArray() {
+		
+		cipherSpec = new CipherSpec( "AES/CBC/NoPadding",  128,  8, myIV);
+		assertTrue( cipherSpec != null );
+		cipherSpec = null;
+		boolean caughtException = false;
+		try {
+				// Invalid cipher xform -- empty
+			cipherSpec = new CipherSpec( "",  128,  8, myIV);
+		} catch( Throwable t ) {
+			caughtException = true;
+		}
+		assertTrue( caughtException && (cipherSpec == null) );
+		caughtException = false;
+		try {
+				// Invalid cipher xform -- missing padding scheme
+			cipherSpec = new CipherSpec("AES/CBC", 128, 8, myIV);
+		} catch( Throwable t ) {
+		    caughtException = true;
+		}
+        assertTrue( caughtException && (cipherSpec == null) );
+	}
+
+	/** CipherSpec(final Cipher cipher, int keySize) */
+	@Test public void testCipherSpecCipherInt() {
+    	cipherSpec = new CipherSpec(dfltOtherCipher, 112);
+    	assertTrue( cipherSpec != null );
+    	assertTrue( cipherSpec.getCipherAlgorithm().equals("Blowfish"));
+    	assertTrue( cipherSpec.getCipherMode().equals("OFB8"));
+    	
+    	cipherSpec = new CipherSpec(dfltAESCipher, 256);
+    	assertTrue( cipherSpec != null );
+    	assertTrue( cipherSpec.getCipherAlgorithm().equals("AES"));
+    	assertTrue( cipherSpec.getCipherMode().equals("ECB") );
+    	assertTrue( cipherSpec.getPaddingScheme().equals("NoPadding") );
+    	// System.out.println("testCipherSpecInt(): " + cipherSpec);
+	}
+
+	/** Test CipherSpec(final byte[] iv) */
+	@Test public void testCipherSpecByteArray() {
+		assertTrue( myIV != null );
+		assertTrue( myIV.length > 0 );
+		cipherSpec = new CipherSpec(myIV);
+		assertTrue( cipherSpec.getKeySize() == 
+						ESAPI.securityConfiguration().getEncryptionKeyLength() );
+		assertTrue( cipherSpec.getCipherTransformation().equals(
+						ESAPI.securityConfiguration().getCipherTransformation() ) );
+	}
+
+	/** Test CipherSpec() */
+	@Test public void testCipherSpec() {
+		cipherSpec = new CipherSpec( dfltECBCipher );
+		assertTrue( cipherSpec.getCipherTransformation().equals("AES/ECB/NoPadding") );
+		assertTrue( cipherSpec.getIV() == null );
+	
+		cipherSpec = new CipherSpec(dfltOtherCipher);
+		assertTrue( cipherSpec.getCipherMode().equals("OFB8") );
+	}
+
+	/** Test setCipherTransformation(String cipherXform) */
+	@Test public void testSetCipherTransformation() {
+		cipherSpec = new CipherSpec();
+		cipherSpec.setCipherTransformation("AlgName/Mode/Padding");
+		cipherSpec.getCipherAlgorithm().equals("AlgName/Mode/Padding");
+		
+		try {
+				// Don't use null here as compiling JUnit tests disables assertion
+				// checking so we get a NullPointerException here instead.
+			cipherSpec.setCipherTransformation(""); // Throws IllegalArgumentException
+		} catch (IllegalArgumentException e) {
+			assertTrue(true);	// Doesn't work w/ @Test(expected=IllegalArgumentException.class)
+		}
+	}
+
+	/** Test getCipherTransformation() */
+	@Test public void testGetCipherTransformation() {
+		assertTrue( (new CipherSpec()).getCipherTransformation().equals("AES/CBC/PKCS5Padding") );
+	}
+
+	/** Test setKeySize() */
+	@Test public void testSetKeySize() {
+		assertTrue( (new CipherSpec()).setKeySize(56).getKeySize() == 56 );
+	}
+
+	/** Test getKeySize() */
+	@Test public void testGetKeySize() {
+		assertTrue( (new CipherSpec()).getKeySize() ==
+			ESAPI.securityConfiguration().getEncryptionKeyLength() );
+	}
+
+	/** Test setBlockSize() */
+	@Test public void testSetBlockSize() {
+		try {
+			cipherSpec.setBlockSize(0); // Throws AssertionError
+		} catch (AssertionError e) {
+			assertTrue(true);	// Doesn't work w/ @Test(expected=AssertionError.class)
+		}
+		try {
+			cipherSpec.setBlockSize(-1); // Throws AssertionError
+		} catch (AssertionError e) {
+			assertTrue(true);	// Doesn't work w/ @Test(expected=AssertionError.class)
+		}
+		assertTrue( cipherSpec.setBlockSize(4).getBlockSize() == 4 );
+	}
+
+	/** Test getBlockSize() */
+	@Test public void testGetBlockSize() {
+		assertTrue( cipherSpec.getBlockSize() == 8 );
+	}
+
+	/** Test getCipherAlgorithm() */
+	@Test public void testGetCipherAlgorithm() {
+		assertTrue( cipherSpec.getCipherAlgorithm().equals("Blowfish") );
+	}
+
+	/** Test getCipherMode */
+	@Test public void testGetCipherMode() {
+		assertTrue( cipherSpec.getCipherMode().equals("OFB8") );
+	}
+
+	/** Test getPaddingScheme() */
+	@Test public void testGetPaddingScheme() {
+		assertTrue( cipherSpec.getPaddingScheme().equals("PKCS5Padding") );
+	}
+
+	/** Test setIV() */
+	@Test public void testSetIV() {
+		try {
+			// Test that ECB mode allows a null IV
+			cipherSpec = new CipherSpec(dfltECBCipher);
+			cipherSpec.setIV(null);
+			assertTrue(true);
+		} catch ( AssertionError e) {
+			assertFalse("Test failed; unexpected exception", false);
+		}
+		try {
+			// Test that CBC mode does allows a null IV
+			cipherSpec = new CipherSpec(dfltAESCipher);
+			cipherSpec.setIV(null);
+			assertFalse("Test failed; Expected exception not thrown", false);
+		} catch ( AssertionError e) {
+			assertTrue(true);
+		}
+	}
+
+	/** Test requiresIV() */
+	@Test public void testRequiresIV() {
+		assertTrue( (new CipherSpec(dfltECBCipher)).requiresIV() == false );
+		cipherSpec = new CipherSpec(dfltAESCipher);
+		assertTrue( cipherSpec.getCipherMode().equals("ECB") );
+		assertTrue( cipherSpec.requiresIV() == false );
+		assertTrue( new CipherSpec(dfltOtherCipher).requiresIV() );
+	}
+	
+	/** Test serialization */
+	@Test public void testSerialization() {
+        String filename = "cipherspec.ser";
+        File serializedFile = new File(filename);
+        boolean success = false;
+        try {
+            // Delete any old serialized file. If it fails, it's not
+            // a big deal. If we can't overwrite it later, we'll get
+            // an IOException.
+            //
+            // NOTE: FindBugs complains we are not checking return value here.
+            //       Guess what? We don't care!!!
+            serializedFile.delete();
+
+            
+            cipherSpec = new CipherSpec( "AES/CBC/NoPadding",  128,  8, myIV);
+            FileOutputStream fos = new FileOutputStream(filename);
+            ObjectOutputStream out = new ObjectOutputStream(fos);
+            out.writeObject(cipherSpec);
+            out.close();
+            fos.close();
+
+            FileInputStream fis = new FileInputStream(filename);
+            ObjectInputStream in = new ObjectInputStream(fis);
+            CipherSpec restoredCipherSpec = (CipherSpec)in.readObject();
+            in.close();
+            fis.close();
+
+            // check that cipherSpec and restoredCipherSpec are equal. Just
+            // compare them via their string representations.
+            assertEquals("Serialized restored CipherSpec differs from saved CipherSpec",
+            			 cipherSpec.toString(), restoredCipherSpec.toString() );
+            
+            success = true;
+        } catch(IOException ex) {
+            ex.printStackTrace(System.err);
+            fail("testSerialization(): Unexpected IOException: " + ex);
+        } catch(ClassNotFoundException ex) {
+            ex.printStackTrace(System.err);
+            fail("testSerialization(): Unexpected ClassNotFoundException: " + ex);
+        } finally {
+            // If test succeeds, remove the file. If it fails, leave it behind
+            // for further analysis.
+            if ( success && serializedFile.exists() ) {
+                boolean deleted = serializedFile.delete();
+                if ( !deleted ) {
+                    try {
+                        System.err.println("Unable to delete file: " + serializedFile.getCanonicalPath() );
+                    } catch (IOException e) {
+                        ; // Ignore
+                    }
+                }
+            }
+        }
+	}
+	
+    /**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+     */
+    public static junit.framework.Test suite() {
+        TestSuite suite = new TestSuite(CipherSpecTest.class);
+
+        return suite;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextSerializerTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextSerializerTest.java.svn-base
new file mode 100644
index 0000000..9f86b9f
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextSerializerTest.java.svn-base
@@ -0,0 +1,83 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.EncryptionException;
+
+public class CipherTextSerializerTest {
+    private Cipher encryptor = null;
+    private IvParameterSpec ivSpec = null;  // Note: FindBugs reports false positive
+                                            // about this being unread field. See
+    										// testAsSerializedByteArray().
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        encryptor = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        byte[] ivBytes = null;
+        ivBytes = ESAPI.randomizer().getRandomBytes(encryptor.getBlockSize());
+        ivSpec = new IvParameterSpec(ivBytes);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    	System.out.flush();
+    }
+
+    @Test
+    public final void testAsSerializedByteArray() {
+    	System.out.println("CipherTextSerializerTest.testAsSerializedByteArray() ...");
+        CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+        cipherSpec.setIV(ivSpec.getIV());
+        SecretKey key;
+        try {
+            key = CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+            encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+
+            byte[] raw = encryptor.doFinal("Hello".getBytes("UTF8"));
+            CipherText ct = ESAPI.encryptor().encrypt(key, new PlainText("Hello") );
+            assertTrue( ct != null );   // Here to eliminate false positive from FindBugs.
+            CipherTextSerializer cts = new CipherTextSerializer( ct );
+            byte[] serializedBytes = cts.asSerializedByteArray();
+            CipherText result = CipherText.fromPortableSerializedBytes(serializedBytes);
+            PlainText pt = ESAPI.encryptor().decrypt(key, result);
+            assertTrue( "Hello".equals( pt.toString() ) );
+        } catch (Exception e) {
+            fail("Test failed: Caught exception: " + e.getClass().getName() + "; msg was: " + e);
+            e.printStackTrace(System.err);
+        }
+    }
+
+    @Test
+    public final void testAsCipherText() {
+        try {
+        	System.out.println("CipherTextSerializerTest.testAsCipherText() ...");
+            CipherText ct = ESAPI.encryptor().encrypt( new PlainText("Hello") );
+            CipherTextSerializer cts = new CipherTextSerializer( ct );
+            CipherText result = cts.asCipherText();
+            assertTrue( ct.equals(result) );
+            PlainText pt = ESAPI.encryptor().decrypt(result);
+            assertTrue( "Hello".equals( pt.toString() ) );
+        } catch (EncryptionException e) {
+            fail("Caught EncryptionException; exception msg: " + e);
+        }
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextTest.java.svn-base
new file mode 100644
index 0000000..18c7fb5
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CipherTextTest.java.svn-base
@@ -0,0 +1,390 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+import junit.framework.Assert;
+import junit.framework.JUnit4TestAdapter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.crypto.CipherSpec;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.crypto.CryptoPolicy;
+
+public class CipherTextTest {
+
+    private static final boolean POST_CLEANUP = true;
+    
+	private CipherSpec cipherSpec_ = null;
+    private Cipher encryptor = null;
+    private Cipher decryptor = null;
+    private IvParameterSpec ivSpec = null;
+	
+    @BeforeClass public static void preCleanup() {
+    	try {
+            // These two calls have side-effects that cause FindBugs to complain.
+    		removeFile("ciphertext.ser");
+    		removeFile("ciphertext-portable.ser");
+    		// Do NOT remove this file...
+    		//		src/test/resource/ESAPI2.0-ciphertext-portable.ser
+    	} catch(Exception ex) {
+    		;	// Do nothing
+    	}
+    }
+    
+	@Before
+	public void setUp() throws Exception {
+        encryptor = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        decryptor = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        byte[] ivBytes = null;
+        ivBytes = ESAPI.randomizer().getRandomBytes(encryptor.getBlockSize());
+        ivSpec = new IvParameterSpec(ivBytes);
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+	
+	@AfterClass public static void postCleanup() {
+	    if ( POST_CLEANUP ) {
+	            // These two calls have side-effects that cause FindBugs to complain.
+	        removeFile("ciphertext.ser");
+	        removeFile("ciphertext-portable.ser");
+	    }
+	}
+
+	/** Test the default CTOR */
+	@Test
+	public final void testCipherText() {
+		CipherText ct =  new CipherText();
+
+		cipherSpec_ = new CipherSpec();
+		assertTrue( ct.getCipherTransformation().equals( cipherSpec_.getCipherTransformation()));
+		assertTrue( ct.getBlockSize() == cipherSpec_.getBlockSize() );
+	}
+
+	@Test
+	public final void testCipherTextCipherSpec() {
+		cipherSpec_ = new CipherSpec("DESede/OFB8/NoPadding", 112);
+		CipherText ct = new CipherText( cipherSpec_ );
+		assertTrue( ct.getRawCipherText() == null );
+		assertTrue( ct.getCipherAlgorithm().equals("DESede") );
+		assertTrue( ct.getKeySize() == cipherSpec_.getKeySize() );
+	}
+
+	@Test
+	public final void testCipherTextCipherSpecByteArray()
+	{
+		try {
+			CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] raw = encryptor.doFinal("Hello".getBytes("UTF8"));
+			CipherText ct = new CipherText(cipherSpec, raw);
+			assertTrue( ct != null );
+			byte[] ctRaw = ct.getRawCipherText();
+			assertTrue( ctRaw != null );
+			assertArrayEquals(raw, ctRaw);
+			assertTrue( ct.getCipherTransformation().equals(cipherSpec.getCipherTransformation()) );;
+			assertTrue( ct.getCipherAlgorithm().equals(cipherSpec.getCipherAlgorithm()) );
+			assertTrue( ct.getPaddingScheme().equals(cipherSpec.getPaddingScheme()) );
+			assertTrue( ct.getBlockSize() == cipherSpec.getBlockSize() );
+			assertTrue( ct.getKeySize() == cipherSpec.getKeySize() );
+			byte[] ctIV = ct.getIV();
+			byte[] csIV = cipherSpec.getIV();
+			assertArrayEquals(ctIV, csIV);
+		} catch( Exception ex) {
+			// As far as test coverage goes, we really don't want this to be covered.
+			fail("Caught unexpected exception: " + ex.getClass().getName() +
+					    "; exception message was: " + ex.getMessage());
+		}
+	}
+
+
+	@Test
+	public final void testDecryptionUsingCipherText() {
+		try {
+			CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			assertTrue( cipherSpec.getIV() != null );
+			assertTrue( cipherSpec.getIV().length > 0 );
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] ctraw = encryptor.doFinal("Hello".getBytes("UTF8"));
+			CipherText ct = new CipherText(cipherSpec, ctraw);
+			assertTrue( ct.getCipherMode().equals("CBC") );
+			assertTrue( ct.requiresIV() ); // CBC mode requires an IV.
+			String b64ctraw = ct.getBase64EncodedRawCipherText();
+			assertTrue( b64ctraw != null);
+			assertArrayEquals( ESAPI.encoder().decodeFromBase64(b64ctraw), ctraw );
+			decryptor.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ct.getIV()));
+			byte[] ptraw = decryptor.doFinal(ESAPI.encoder().decodeFromBase64(b64ctraw));
+			assertTrue( ptraw != null );
+			assertTrue( ptraw.length > 0 );
+			String plaintext = new String( ptraw, "UTF-8");
+			assertTrue( plaintext.equals("Hello") );
+			assertArrayEquals( ct.getRawCipherText(), ctraw );
+			
+			byte[] ivAndRaw = ESAPI.encoder().decodeFromBase64( ct.getEncodedIVCipherText() );
+			assertTrue( ivAndRaw.length > ctraw.length );
+			assertTrue( ct.getBlockSize() == ( ivAndRaw.length - ctraw.length ) );
+		} catch( Exception ex) {
+		    // Note: FindBugs reports a false positive here...
+		    //    REC_CATCH_EXCEPTION: Exception is caught when Exception is not thrown
+		    // but exceptions really can be thrown. This probably is because FindBugs
+		    // examines the byte-code rather than the source code. However "fixing" this
+		    // so that it doesn't complain will make the test much more complicated as there
+		    // are about 3 or 4 different exception types.
+		    //
+		    // On a completely different note, as far as test coverage metrics goes,
+			// we really don't care if this is covered or nit as it is not our intent
+		    // to be causing exceptions here.
+			ex.printStackTrace(System.err);
+			fail("Caught unexpected exception: " + ex.getClass().getName() +
+					"; exception message was: " + ex.getMessage());
+		}
+	}
+
+	@Test
+	public final void testMIC() {
+		try {
+			CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] ctraw = encryptor.doFinal("Hello".getBytes("UTF8"));
+			CipherText ct = new CipherText(cipherSpec, ctraw);
+			assertTrue( ct.getIV() != null && ct.getIV().length > 0 );
+			SecretKey authKey = CryptoHelper.computeDerivedKey(key, key.getEncoded().length * 8, "authenticity");
+			ct.computeAndStoreMAC( authKey ); 
+			try {
+				ct.setIVandCiphertext(ivSpec.getIV(), ctraw);	// Expected to log & throw.
+			} catch( Exception ex ) {
+				assertTrue( ex instanceof EncryptionException );
+			}
+			try {
+				ct.setCiphertext(ctraw);	// Expected to log and throw message about
+											// not being able to store raw ciphertext.
+			} catch( Exception ex ) {
+				assertTrue( ex instanceof EncryptionException );
+			}
+			decryptor.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec( ct.getIV() ) );
+			byte[] ptraw = decryptor.doFinal( ct.getRawCipherText() );
+			assertTrue( ptraw != null && ptraw.length > 0 );
+			ct.validateMAC( authKey );
+		} catch( Exception ex) {
+			// As far as test coverage goes, we really don't want this to be covered.
+			ex.printStackTrace(System.err);
+			fail("Caught unexpected exception: " + ex.getClass().getName() +
+					"; exception message was: " + ex.getMessage());
+		}
+	}
+
+	/** Test <i>portable</i> serialization. */
+	@Test public final void testPortableSerialization() {
+	    System.out.println("CipherTextTest.testPortableSerialization()starting...");
+	    String filename = "ciphertext-portable.ser";
+	    File serializedFile = new File(filename);
+	    serializedFile.delete();    // Delete any old serialized file.
+
+	    int keySize = 128;
+	    if ( CryptoPolicy.isUnlimitedStrengthCryptoAvailable() ) {
+	        keySize = 256;
+	    }
+	    CipherSpec cipherSpec = new CipherSpec(encryptor, keySize);
+	    cipherSpec.setIV(ivSpec.getIV());
+	    SecretKey key;
+	    try {
+	        key = CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), keySize);
+
+	        encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+	        byte[] raw = encryptor.doFinal("This is my secret message!!!".getBytes("UTF8"));
+	        CipherText ciphertext = new CipherText(cipherSpec, raw);
+	        	// TODO: Replace this w/ call to KeyDerivationFunction as this is
+	        	//		 deprecated! Shame on me!
+	        SecretKey authKey = CryptoHelper.computeDerivedKey(key, key.getEncoded().length * 8, "authenticity");
+	        ciphertext.computeAndStoreMAC( authKey );
+//          System.err.println("Original ciphertext being serialized: " + ciphertext);
+	        byte[] serializedBytes = ciphertext.asPortableSerializedByteArray();
+	        
+	        FileOutputStream fos = new FileOutputStream(serializedFile);
+            fos.write(serializedBytes);
+                // Note: FindBugs complains that this test may fail to close
+                // the fos output stream. We don't really care.
+            fos.close();
+            
+            // NOTE: FindBugs complains about this (OS_OPEN_STREAM). It apparently
+            //       is too lame to know that 'fis.read()' is a serious side-effect.
+            FileInputStream fis = new FileInputStream(serializedFile);
+            int avail = fis.available();
+            byte[] bytes = new byte[avail];
+            fis.read(bytes, 0, avail);
+            
+            // Sleep one second to prove that the timestamp on the original
+            // CipherText object is the one that we use and not just the
+            // current time. Only after that, do we restore the serialized bytes.
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                ;    // Ignore
+            }       
+            CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(bytes);
+//          System.err.println("Restored ciphertext: " + restoredCipherText);
+            assertTrue( ciphertext.equals(restoredCipherText));
+	    } catch (EncryptionException e) {
+	        Assert.fail("Caught EncryptionException: " + e);
+        } catch (FileNotFoundException e) {
+            Assert.fail("Caught FileNotFoundException: " + e);
+        } catch (IOException e) {
+            Assert.fail("Caught IOException: " + e);
+        } catch (Exception e) {
+            Assert.fail("Caught Exception: " + e);
+        } finally {
+            // FindBugs complains that we are ignoring this return value. We really don't care.
+            serializedFile.delete();
+        }
+	}
+	
+	/** Test <i>portable</i> serialization for backward compatibility with ESAPI 2.0. */
+	@Test public final void testPortableSerializationBackwardCompatibility() {
+	    System.out.println("testPortableSerializationBackwardCompatibility()starting...");
+	    String filename = "src/test/resources/ESAPI2.0-ciphertext-portable.ser";  // Do NOT remove
+	    File serializedFile = new File(filename);
+
+	    try {
+	    	// String expectedMsg = "This is my secret message!!!";
+            
+            // NOTE: FindBugs complains about this (OS_OPEN_STREAM). It apparently
+            //       is too lame to know that 'fis.read()' is a serious side-effect.
+            FileInputStream fis = new FileInputStream(serializedFile);
+            int avail = fis.available();
+            byte[] bytes = new byte[avail];
+            fis.read(bytes, 0, avail);
+            // We can't go as far and decrypt it because the file was encrypted using a
+            // temporary session key.
+            CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(bytes);
+            assertTrue( restoredCipherText != null );
+            int retrievedKdfVersion = restoredCipherText.getKDFVersion();
+	    } catch (EncryptionException e) {
+	        Assert.fail("Caught EncryptionException: " + e);
+        } catch (FileNotFoundException e) {
+            Assert.fail("Caught FileNotFoundException: " + e);
+        } catch (IOException e) {
+            Assert.fail("Caught IOException: " + e);
+        } catch (Exception e) {
+            Assert.fail("Caught Exception: " + e);
+        } finally {
+        	; // Do NOT delete the file.
+        }
+	}
+	
+	/** Test Java serialization. */
+	@Test public final void testJavaSerialization() {
+        String filename = "ciphertext.ser";
+        File serializedFile = new File(filename);
+        try {
+            serializedFile.delete();	// Delete any old serialized file.
+            
+            CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] raw = encryptor.doFinal("This is my secret message!!!".getBytes("UTF8"));
+			CipherText ciphertext = new CipherText(cipherSpec, raw);
+
+            FileOutputStream fos = new FileOutputStream(filename);
+            ObjectOutputStream out = new ObjectOutputStream(fos);
+            out.writeObject(ciphertext);
+            out.close();
+            fos.close();
+
+            FileInputStream fis = new FileInputStream(filename);
+            ObjectInputStream in = new ObjectInputStream(fis);
+            CipherText restoredCipherText = (CipherText)in.readObject();
+            in.close();
+            fis.close();
+
+            // check that ciphertext and restoredCipherText are equal. Requires
+            // multiple checks. (Hmmm... maybe overriding equals() and hashCode()
+            // is in order???)
+            assertEquals("1: Serialized restored CipherText differs from saved CipherText",
+            			 ciphertext.toString(), restoredCipherText.toString());
+            assertArrayEquals("2: Serialized restored CipherText differs from saved CipherText",
+            			 ciphertext.getIV(), restoredCipherText.getIV());
+            assertEquals("3: Serialized restored CipherText differs from saved CipherText",
+            			 ciphertext.getBase64EncodedRawCipherText(),
+            			 restoredCipherText.getBase64EncodedRawCipherText());
+            
+        } catch(IOException ex) {
+            ex.printStackTrace(System.err);
+            fail("testJavaSerialization(): Unexpected IOException: " + ex);
+        } catch(ClassNotFoundException ex) {
+            ex.printStackTrace(System.err);
+            fail("testJavaSerialization(): Unexpected ClassNotFoundException: " + ex);
+        } catch (EncryptionException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected EncryptionException: " + ex);
+		} catch (IllegalBlockSizeException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected IllegalBlockSizeException: " + ex);
+		} catch (BadPaddingException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected BadPaddingException: " + ex);
+		} catch (InvalidKeyException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected InvalidKeyException: " + ex);
+		} catch (InvalidAlgorithmParameterException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected InvalidAlgorithmParameterException: " + ex);
+		}  finally {
+		    // FindBugs complains that we are ignoring this return value. We really don't care.
+            serializedFile.delete();
+        }
+	}
+	
+	/**
+	 * Run all the test cases in this suite.
+	 * This is to allow running from {@code org.owasp.esapi.AllTests} which
+	 * uses a JUnit 3 test runner.
+	 */
+	public static junit.framework.Test suite() {
+		return new JUnit4TestAdapter(CipherTextTest.class);
+	}
+	
+	private static void removeFile(String fname) {
+    	try {
+    		if ( fname != null ) {
+    			File f = new File(fname);
+    			// Findbugs complains about ignoring this return value. Too bad.
+    			f.delete();
+    		}
+    	} catch(Exception ex) {
+    		;	// Do nothing
+    	}
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CryptoHelperTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CryptoHelperTest.java.svn-base
new file mode 100644
index 0000000..678b0ac
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CryptoHelperTest.java.svn-base
@@ -0,0 +1,189 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.util.Random;
+
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+
+import junit.framework.JUnit4TestAdapter;
+
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.errors.EncryptionException;
+
+public class CryptoHelperTest {
+
+    @Test
+    public final void testGenerateSecretKeySunnyDay() {
+        try {
+            SecretKey key = CryptoHelper.generateSecretKey("AES", 128);
+            assertTrue(key.getAlgorithm().equals("AES"));
+            assertTrue(128 / 8 == key.getEncoded().length);
+        } catch (EncryptionException e) {
+            // OK if not covered in code coverage -- not expected.
+            fail("Caught unexpected EncryptionException; msg was "
+                    + e.getMessage());
+        }
+    }
+
+    @Test(expected = EncryptionException.class)
+    public final void testGenerateSecretKeyEncryptionException()
+            throws EncryptionException {
+        SecretKey key = CryptoHelper.generateSecretKey("NoSuchAlg", 128);
+        assertTrue(key == null); // Not reached!
+    }
+
+    @Test
+    public final void testOverwriteByteArrayByte() {
+        byte[] secret = "secret password".getBytes();
+        int len = secret.length;
+        CryptoHelper.overwrite(secret, (byte) 'x');
+        assertTrue(secret.length == len); // Length unchanged
+        assertTrue(checkByteArray(secret, (byte) 'x')); // Filled with 'x'
+    }
+
+    @Test
+    public final void testCopyByteArraySunnyDay() {
+        byte[] src = new byte[20];
+        fillByteArray(src, (byte) 'A');
+        byte[] dest = new byte[20];
+        fillByteArray(dest, (byte) 'B');
+        CryptoHelper.copyByteArray(src, dest);
+        assertTrue(checkByteArray(src, (byte) 'A')); // Still filled with 'A'
+        assertTrue(checkByteArray(dest, (byte) 'A')); // Now filled with 'B'
+    }
+
+    @Test(expected = NullPointerException.class)
+    public final void testCopyByteArraySrcNullPointerException() {
+        byte[] ba = new byte[16];
+        CryptoHelper.copyByteArray(null, ba, ba.length);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public final void testCopyByteArrayDestNullPointerException() {
+        byte[] ba = new byte[16];
+        CryptoHelper.copyByteArray(ba, null, ba.length);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public final void testCopyByteArrayIndexOutOfBoundsException() {
+        byte[] ba8 = new byte[8];
+        byte[] ba16 = new byte[16];
+        CryptoHelper.copyByteArray(ba8, ba16, ba16.length);
+    }
+
+    @Test
+    public final void testArrayCompare() {
+        byte[] ba1 = new byte[32];
+        byte[] ba2 = new byte[32];
+        byte[] ba3 = new byte[48];
+
+        // Note: Don't need cryptographically secure random numbers for this!
+        Random prng = new Random();
+
+        prng.nextBytes(ba1);
+        prng.nextBytes(ba2);
+        prng.nextBytes(ba3);
+
+        /*
+         * Unfortunately, can't rely no the nanosecond timer because as the
+         * Javadoc for System.nanoTime() states, " No guarantees are made
+         * about how frequently values change", so this is not very reliable.
+         * 
+         * However, on can uncomment the code and observe that elapsed times
+         * are generally less than 10 millionth of a second. I suppose if we
+         * declared a large enough epsilon, we could make it work, but it is
+         * easier to convince yourself from the CryptoHelper.arrayCompare() code
+         * itself that it always goes through all the bits of the byte array
+         * if it compares any bits at all.
+         */
+        
+//        long start, stop, diff;
+
+//        start = System.nanoTime();
+        assertTrue(CryptoHelper.arrayCompare(null, null));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertTrue(CryptoHelper.arrayCompare(ba1, ba1));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertFalse(CryptoHelper.arrayCompare(ba1, ba2));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertFalse(CryptoHelper.arrayCompare(ba1, ba3));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertFalse(CryptoHelper.arrayCompare(ba1, null));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+        
+        ba2 = ba1;
+//        start = System.nanoTime();
+        assertTrue(CryptoHelper.arrayCompare(ba1, ba2));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+    }
+
+    @Test
+    public final void testIsValidKDFVersion() {
+    	assertTrue( CryptoHelper.isValidKDFVersion(20110203, false, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(20130830, false, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(33330303, false, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(99991231, false, false));
+
+    	assertFalse( CryptoHelper.isValidKDFVersion(0, false, false));
+    	assertFalse( CryptoHelper.isValidKDFVersion(99991232, false, false));
+    	assertFalse( CryptoHelper.isValidKDFVersion(20110202, false, false));
+
+    	assertTrue( CryptoHelper.isValidKDFVersion(20110203, true, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(KeyDerivationFunction.kdfVersion, true, false));
+    	assertFalse( CryptoHelper.isValidKDFVersion(KeyDerivationFunction.kdfVersion + 1, true, false));
+
+    	try {
+        	CryptoHelper.isValidKDFVersion(77777777, true, true);
+        	fail("Failed to CryptoHelper.isValidKDFVersion() failed to throw IllegalArgumentException.");
+    	}
+    	catch (Exception e) {
+    		assertTrue( e instanceof IllegalArgumentException);
+    	}
+    }
+    
+    private void fillByteArray(byte[] ba, byte b) {
+        for (int i = 0; i < ba.length; i++) {
+            ba[i] = b;
+        }
+    }
+
+    private boolean checkByteArray(byte[] ba, byte b) {
+        for (int i = 0; i < ba.length; i++) {
+            if (ba[i] != b) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Run all the test cases in this suite. This is to allow running from
+     * {@code org.owasp.esapi.AllTests} which uses a JUnit 3 test runner.
+     */
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(CryptoHelperTest.class);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CryptoTokenTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CryptoTokenTest.java.svn-base
new file mode 100644
index 0000000..f819865
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/CryptoTokenTest.java.svn-base
@@ -0,0 +1,401 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.util.Date;
+import java.util.Map;
+
+import javax.crypto.SecretKey;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.ValidationException;
+
+public class CryptoTokenTest {
+    
+    private SecretKey skey1 = null;
+    private SecretKey skey2 = null;
+
+    @Before
+    public void setUp() throws Exception {
+        skey1 = CryptoHelper.generateSecretKey("AES", 128);
+        skey2 = CryptoHelper.generateSecretKey("AES", 128);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public final void testCryptoToken() {
+        // Test with default CTOR
+        CryptoToken ctok = new CryptoToken();
+        CTORtest( ctok, null );
+    }
+
+    @Test
+    public final void testCryptoTokenSecretKey() {
+     // Test with default CTOR
+        CryptoToken ctok = new CryptoToken(skey1);
+        CTORtest( ctok, skey1 );
+    }
+
+    private void CTORtest(CryptoToken ctok, SecretKey sk) {
+        String token = null;
+        try {
+            if ( sk == null ) {
+                token = ctok.getToken();    // Use default key, Encryptor.MasterKey
+            } else {
+                token = ctok.getToken(sk);
+            }
+        } catch (EncryptionException e) {
+            fail("Caught unexpected exception on getToken() call: " + e);
+        }
+        assertNotNull(token);
+        assertEquals( ctok.getUserAccountName(), CryptoToken.ANONYMOUS_USER);
+        assertFalse( ctok.isExpired() );
+        long expTime1 = ctok.getExpiration();
+        
+        CryptoToken ctok2 = null;
+        try {
+            if ( sk == null ) {
+                ctok2 = new CryptoToken( token );    // Use default key, Encryptor.MasterKey
+            } else {
+                ctok2 = new CryptoToken( sk, token );
+            }
+        } catch (EncryptionException e) {
+            e.printStackTrace(System.err);
+            fail("Caught unexpected exception on CryptoToken CTOR: " + e);
+        }
+        long expTime2 = ctok2.getExpiration();
+        assertTrue("Expected expiration for ctok2 (" + new Date(expTime2) +
+                   ") to be later than of ctok (" + new Date(expTime1) + ").",
+                   ( expTime2 >= expTime1 ) );
+    }
+
+    @Test
+    public final void testCryptoTokenSecretKeyString() {
+        CryptoToken ctok1 = new CryptoToken(skey1);
+        try {
+            ctok1.setUserAccountName("kevin.w.wall at gmail.com");
+        } catch (ValidationException e) {
+            fail("Failed to set user account name because of ValidationException: " + e);
+        }
+        try {
+            ctok1.setAttribute("role-name", "admin");
+            ctok1.setAttribute("company", "Qwest");
+        } catch (ValidationException e) {
+            fail("Failed to set 'role-name' or 'company' attribute because of ValidationException: " + e);
+        }
+        String token1 = null;
+        String token2 = null;
+        boolean passedFirst = false;
+        try {
+            token1 = ctok1.getToken();
+            passedFirst = true;
+            token2 = ctok1.getToken(skey2);
+            assertFalse("Tokens unexpectedly equal!", token1.equals(token2) );
+        } catch (EncryptionException e) {
+            fail("Failed to retrieve " + (passedFirst ? "1st" : "2nd" ) + " encrypted token");
+        }
+        CryptoToken ctok2 = null;
+        try {
+            ctok2 = new CryptoToken(skey1, token1);
+            token2 = ctok2.getToken();
+            ctok2.setAttribute("company", "CenturyLink");
+        } catch (EncryptionException e) {
+            fail("Failed to decrypt token1 or re-encrypt token; exception: " + e);
+        } catch (ValidationException e) {
+            fail("Failed with ValidationException on resetting 'company' attribute: " + e);
+        }
+        String userName = ctok2.getUserAccountName();
+        String roleAttr = ctok2.getAttribute("role-name");
+        String company  = ctok2.getAttribute("company");
+        assertEquals( userName, "kevin.w.wall at gmail.com");
+        assertEquals( roleAttr, "admin");
+        assertEquals( company, "CenturyLink");
+    }
+
+    @Test
+    public final void testExpiration() {
+        CryptoToken ctok = new CryptoToken();
+        ctok.setExpiration(2);  // 2 seconds
+        CryptoToken ctok2 = null;
+        try {
+            ctok2 = new CryptoToken( ctok.getToken() );
+        } catch (EncryptionException e1) {
+            fail("Failed to decrypt token");
+        }
+        assertFalse( ctok.isExpired() );
+        assertFalse( ctok2.isExpired() );
+        nap(2);
+
+        assertTrue( ctok.isExpired() );
+        assertTrue( ctok2.isExpired() );
+        
+        try {
+            ctok2.updateToken(2);
+        } catch (EncryptionException e) {
+            fail("EncryptionException for token ctok2 by adding additional 2 sec; exception: " + e);
+        } catch (ValidationException e) {
+            // This would be caused if the token would already be expired even AFTER adding
+            // an additional 2 seconds. We don't expect this, but it could happen if the OS
+            // causes this process to stall for a bit while running higher priority processes.
+            // We don't expect this here though. (Have a test for that below.)
+            fail("Failed to update token ctok2 by adding additional 2 sec; exception: " + e);
+        }
+        assertFalse( ctok2.isExpired() );
+        nap(3);
+        try {
+            ctok2.updateToken(1);
+            fail("Expected ValidationException!");
+        } catch (EncryptionException e) {
+            fail("EncryptionException for token ctok2 by adding additional 2 sec; exception: " + e);
+        } catch (ValidationException e) {
+            // Probably a bad idea to test this in the following manner as
+            // message could change whenever.
+            // assertEquals( e.getMessage(), "Token timed out.");
+            ;   // Ignore -- in this case, we expect it!
+        }
+    }
+
+    @Test
+    public final void testSetUserAccountName() {
+        CryptoToken ctok = new CryptoToken();
+        try {
+            ctok.setUserAccountName("kevin.w.wall at gmail.com");
+            ctok.setUserAccountName("kevin");
+            ctok.setUserAccountName("name-with-hyphen");
+            ctok.setUserAccountName("x");
+            ctok.setUserAccountName("X");
+        } catch (ValidationException e) {
+            fail("Failed to set user account name because of ValidationException: " + e);
+        }
+        try {
+            ctok.setUserAccountName("");    // Can't be empty
+            fail("Failed to throw expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        }
+        try {
+            ctok.setUserAccountName(null);    // Can't be null
+                // Should get one of these, depending on whether or not assertions are enabled.
+            fail("Failed to throw expected AssertionError or NullPointerException");
+        } catch (ValidationException e) {
+            fail("Wrong type of exception thrown (ValidationException): " + e);
+        } catch (NullPointerException e) {
+            ;   // Success
+        } catch (AssertionError e) {
+            ;   // Success
+        }
+        try {
+            ctok.setUserAccountName("1773g4l");    // Can't start w/ numeric
+            fail("Failed to throw expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        }
+        try {
+            ctok.setUserAccountName("invalid/char");    // '/' is not valid.
+            fail("Failed to throw expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        }
+        
+    }
+
+    @Test
+    public final void testSetExpirationDate() {
+        CryptoToken ctok = new CryptoToken();
+        try {
+            ctok.setExpiration(null);
+            fail("Expected IllegalArgumentException on ctok.setExpiration(null).");
+        } catch (IllegalArgumentException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        try {
+            Date now = new Date();
+            nap(1);
+            ctok.setExpiration(now);
+            fail("Expected IllegalArgumentException on ctok.setExpiration(Date) w/ Date in past.");
+        } catch (IllegalArgumentException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        try {
+            ctok.setExpiration(-1);
+            fail("Expected IllegalArgumentException on ctok.setExpiration(int) w/ negative interval.");
+        } catch (IllegalArgumentException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        try {
+            Date maxDate = new Date( Long.MAX_VALUE - 1 );
+            ctok.setExpiration( maxDate );
+            ctok.updateToken(1);
+            fail("Expected ArithmeticException on ctok.setExpiration(int).");
+        } catch (ArithmeticException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }   
+    }
+
+
+    @Test
+    public final void testSetAndGetAttribute() {
+        CryptoToken ctok = new CryptoToken();
+
+        // Test case where attr name is empty string. Expect ValidationException
+        try {
+            ctok.setAttribute("", "someValue");
+            fail("Expected ValidationException on ctok.setAttribute().");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+        
+        // Test case where attr name does not match regex "[A-Za-z0-9_.-]+".
+        // Expect ValidationException.
+        try {
+            ctok.setAttribute("/my/attr/", "someValue");
+            fail("Expected ValidationException on ctok.setAttribute() w/ invalid name.");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+        
+        // Test case where attr VALUE is not. Expect ValidationException.
+        try {
+            ctok.setAttribute("myAttr", null);
+            fail("Expected ValidationException on ctok.setAttribute() w/ null value.");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        // Test cases that should work. Specifically we want to test cases
+        // where attribute values contains each of the values that will
+        // be quoted, namely:   '\', '=', and ';'
+        try {
+            String complexValue = "kwwall;1291183520293;abc=x=yx;xyz=;efg=a;a;;bbb=quotes\\tuff";
+            
+            ctok.setAttribute("..--__", ""); // Ugly, but legal attr name; empty is legal value.
+            ctok.setAttribute("attr1", "\\");
+            ctok.setAttribute("attr2", ";");
+            ctok.setAttribute("attr3", "=");
+            ctok.setAttribute("complexAttr", complexValue);
+            String tokenVal = ctok.getToken();
+            assertNotNull("tokenVal should not be null", tokenVal);
+            
+            CryptoToken ctok2 = new CryptoToken(tokenVal);
+            String weirdAttr = ctok2.getAttribute("..--__");
+            assertTrue("Expecting empty string for value of weird attr, but got: " + weirdAttr,
+                       weirdAttr.equals(""));
+
+            String attr1 = ctok2.getAttribute("attr1");
+            assertTrue("attr1 has unexpected value of " + attr1, attr1.equals("\\") );
+
+            String attr2 = ctok2.getAttribute("attr2");
+            assertTrue("attr2 has unexpected value of " + attr2, attr2.equals(";") );
+
+            String attr3 = ctok2.getAttribute("attr3");
+            assertTrue("attr3 has unexpected value of " + attr3, attr3.equals("=") );
+
+            String complexAttr = ctok2.getAttribute("complexAttr");
+            assertNotNull(complexAttr);
+            assertTrue("complexAttr has unexpected value of " + complexAttr, complexAttr.equals(complexValue) );
+
+        } catch (ValidationException e) {
+            fail("Caught unexpected ValidationException: " + e);
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            fail("Caught unexpected exception: " + e);
+        }
+    }
+
+    // Test the following two methods in CryptoToken:
+    // public void addAttributes(final Map<String, String> attrs) throws ValidationException
+    // public Map<String, String> getAttributes()
+    @Test
+    public final void testAddandGetAttributes() {
+        CryptoToken ctok = new CryptoToken();       
+        Map<String, String> origAttrs = null;
+
+        try {
+            ctok.setAttribute("attr1", "value1");
+            ctok.setAttribute("attr2", "value2");
+            origAttrs = ctok.getAttributes();
+            origAttrs.put("attr2", "NewValue2");
+            String val = ctok.getAttribute("attr2");
+            assertTrue("Attribute map not cloned; crypto token attr changed!",
+                       val.equals("value2") );  // Confirm original attr2 did not change
+            
+            origAttrs.put("attr3", "value3");
+            origAttrs.put("attr4", "value4");
+            ctok.addAttributes(origAttrs);
+        } catch (ValidationException e) {
+            fail("Caught unexpected ValidationException: " + e);
+        }
+        try {
+            String token = ctok.getToken();
+            ctok = new CryptoToken(token);
+        } catch (EncryptionException e) {
+            fail("Caught unexpected EncryptionException: " + e);
+        }
+        
+        Map<String, String> extractedAttrs = ctok.getAttributes();
+        assertTrue("Expected extracted attrs to be equal to original attrs",
+                   origAttrs.equals(extractedAttrs));
+                
+        origAttrs.put("/illegalAttrName/", "someValue");
+        try {
+            ctok.addAttributes(origAttrs);
+            fail("Expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            fail("Caught unexpected exception: " + e);
+        }
+        
+        origAttrs.clear();
+        CryptoToken ctok2 = null;
+        try {
+            ctok.clearAttributes();     // Clear any attributes
+            ctok2 = new CryptoToken( ctok.getToken() );
+        } catch (EncryptionException e) {
+            fail("Unexpected EncryptionException");
+        }
+        
+        try {
+            ctok2.addAttributes(origAttrs);     // Add (empty) attribute map
+        } catch (ValidationException e) {
+            fail("Unexpected ValidationException");
+        }
+        extractedAttrs = ctok2.getAttributes();
+        assertTrue("Expected extracted attributes to be empty", extractedAttrs.isEmpty() );
+    }
+
+    // Sleep n seconds.
+    private static void nap(int n) {
+        try {
+            System.out.println("Sleeping " + n + " seconds...");
+            Thread.sleep( n * 1000 );
+        } catch (InterruptedException e) {
+            ;   // Ignore
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/ESAPICryptoMACByPassTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/ESAPICryptoMACByPassTest.java.svn-base
new file mode 100644
index 0000000..e54ad33
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/ESAPICryptoMACByPassTest.java.svn-base
@@ -0,0 +1,231 @@
+/*
+ * OWASP Enterprise Security API (ESAPI) - Google issue # 306.
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2013 - The OWASP Foundation
+ * ESAPI is published by OWASP under the new BSD license. You should read
+ * and accept the LICENSE before you use, modify, and/or redistribute this
+ * software.
+ * 
+ * Full credit for this JUnit to illustrate what is now Google Issue # 306
+ * goes to Philippe Arteau <philippe.arteau at gmail.com>. Originally
+ * published 2013/08/21 to ESAPI-DEV mailing list. Shows that both
+ * ESAPI 2.0 and 2.0.1 is vulnerable. Minor tweaks by Kevin W. Wall.
+ *
+ * Original class name: SignatureByPassTest.
+ * 
+ * NOTE: If this test fails, your version of ESAPI is vulnerable (or you have
+ * 		 it configured not to require a MAC which you should NOT do).
+ */
+
+package org.owasp.esapi.crypto;
+
+import org.apache.commons.codec.binary.Hex;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.util.ByteConversionUtil;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Date;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class ESAPICryptoMACByPassTest {
+
+    @Before
+    public void setUp() throws NoSuchAlgorithmException, InvalidKeySpecException {
+    	; // Do any prerequisite setup here.
+    }
+
+    @Test
+    public void testMacBypass() throws EncryptionException, NoSuchFieldException, IllegalAccessException {
+
+        byte[] bkey = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xA,0x0B,0x0C,0x0D,0x0E,0x0F}; //Truly random key. ;-)
+        SecretKey sk = new SecretKeySpec(bkey,"AES");
+
+        //Encryption with MAC
+        String originalMessage = "Cryptography!?!?";
+        System.out.printf("Encrypting the message '%s'\n", originalMessage);
+        	// Until there is a better way to do this. But this is much easier
+        	// than having to set up a custom ESAPI.properties file just for
+        	// this test.
+        	//
+        	// NOTE: Philippe Arteau's original exploit used "AES/OFB/NoPadding"
+            //       so that one could see that the effect of the decryption would
+        	//		 be "Craptography!?!?". However, if you wish to do that, then
+        	//		 you must also change the ESAPI.properties file used by ESAPI
+        	//		 JUnit tests sp that "OFB" is accepted as an allowed cipher
+        	//		 mode. The easiest way to do that (if you are still not convinced)
+        	//		 is to add "OFB" mode to Encryptor.cipher_modes.additional_allowed;
+        	//		 e.g.,  Encryptor.cipher_modes.additional_allowed=CBC,OFB
+        	//
+        String origCipherXform =
+        	ESAPI.securityConfiguration().setCipherTransformation("AES/CBC/NoPadding");
+        CipherText ct = ESAPI.encryptor().encrypt(sk,new PlainText(originalMessage));
+
+        //Serialize the ciphertext in order to send it over the wire..
+        byte[] serializedCt = ct.asPortableSerializedByteArray();
+
+        //Malicious modification
+        byte[] modifiedCt = tamperCipherText(serializedCt);
+
+        //Decrypt
+        CipherText modifierCtObj = CipherText.fromPortableSerializedBytes(modifiedCt);
+        try {
+        	ESAPI.securityConfiguration().setCipherTransformation(origCipherXform);
+        		// This decryption should fail by throwing an EncryptionException
+        		// if ESAPI crypto is NOT vulnerable and you never get to the
+        		// subsequent lines in the try block.
+            PlainText pt = ESAPI.encryptor().decrypt(sk,modifierCtObj);
+            System.out.printf("Decrypting to '%s' (probably will look like garbage!)\n", new String(pt.asBytes()));
+            System.out.println("This ESAPI version vulnerable to MAC by-pass described in Google issue # 306! Upgrade to latest version.");
+            fail("This ESAPI version is vulnerable to MAC by-pass described in Google issue # 306! Upgrade to latest version.");
+        } catch(EncryptionException eex) {
+        	String errMsg = eex.getMessage();
+        		// See private String DECRYPTION_FAILED in JavaEncryptor.
+        	String expectedError = "Decryption failed; see logs for details.";
+        	assertTrue( errMsg.equals(expectedError) );
+        	System.out.println("testMacByPass(): Attempted decryption after MAC tampering failed.");
+            System.out.println("Fix of issue # 306 successful. Crypto MAC by-pass test failed; exception was: [" + eex + "]");
+        }
+    }
+
+    /**
+     * The modification of the ciphertext is done in a separate method to show that only the generate CipherText object is use.
+     * @param serializeCt
+     */
+    private byte[] tamperCipherText(byte[] serializeCt) throws EncryptionException, NoSuchFieldException, IllegalAccessException {
+        CipherText ct = CipherText.fromPortableSerializedBytes(serializeCt);
+
+        //Ciphertext metadata
+        //System.out.println(ct.toString());
+
+        byte[] cipherTextMod = ct.getRawCipherText();
+
+        System.out.printf("Original ciphertext\t'%s'\n",String.valueOf(Hex.encodeHex(cipherTextMod)));
+
+        cipherTextMod[2] ^= 'y' ^ 'a'; //Alter the 3rd character
+
+        System.out.printf("Modify ciphertext\t'%s'\n",String.valueOf(Hex.encodeHex(cipherTextMod)));
+
+        //MAC ... what MAC ?
+        Field f2 = ct.getClass().getDeclaredField("separate_mac_");
+        f2.setAccessible(true);
+        f2.set(ct,null); //mac byte array set to null
+
+        //Changing CT
+        Field f3 = ct.getClass().getDeclaredField("raw_ciphertext_");
+        f3.setAccessible(true);
+        f3.set(ct,cipherTextMod);
+
+        //return ct.asPortableSerializedByteArray(); //Will complain about missing mac
+        //return new CipherTextSerializer(ct).asSerializedByteArray(); //NPE on mac.length
+        return serialize(ct); //Modify version of CipherTextSerializer.asSerializedByteArray()
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    // The following code is a modified version of CipherTextSerializer
+    /////////////////////////////////////////////////////////////////////
+
+    private byte[] serialize(CipherText cipherText_) {
+        int kdfInfo = cipherText_.getKDFInfo();
+
+        long timestamp = cipherText_.getEncryptionTimestamp();
+        String cipherXform = cipherText_.getCipherTransformation();
+
+        short keySize = (short) cipherText_.getKeySize();
+
+        short blockSize = (short)cipherText_.getBlockSize();
+        byte[] iv = cipherText_.getIV();
+
+        short ivLen = (short)iv.length;
+        byte[] rawCiphertext = cipherText_.getRawCipherText();
+        int ciphertextLen = rawCiphertext.length;
+
+        byte[] mac = cipherText_.getSeparateMAC();
+
+        short macLen = 0;//(short)mac.length; //<-------------- The only modification to the serialization
+
+        return computeSerialization(kdfInfo, timestamp, cipherXform, keySize, blockSize, ivLen, iv, ciphertextLen, rawCiphertext, macLen, mac);
+    }
+
+    private byte[] computeSerialization(int kdfInfo, long timestamp, String cipherXform, short keySize, short blockSize, short ivLen, byte[] iv, int ciphertextLen, byte[] rawCiphertext, short macLen, byte[] mac)
+    {
+        debug("computeSerialization: kdfInfo = " + kdfInfo);
+        debug("computeSerialization: timestamp = " + new Date(timestamp));
+        debug("computeSerialization: cipherXform = " + cipherXform);
+        debug("computeSerialization: keySize = " + keySize);
+        debug("computeSerialization: blockSize = " + blockSize);
+        debug("computeSerialization: ivLen = " + ivLen);
+        debug("computeSerialization: ciphertextLen = " + ciphertextLen);
+        debug("computeSerialization: macLen = " + macLen);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeInt(baos, kdfInfo);
+        writeLong(baos, timestamp);
+        String[] parts = cipherXform.split("/");
+        assert (parts.length == 3) : "Malformed cipher transformation";
+        writeString(baos, cipherXform);
+        writeShort(baos, keySize);
+        writeShort(baos, blockSize);
+        writeShort(baos, ivLen);
+        if (ivLen > 0) baos.write(iv, 0, iv.length);
+        writeInt(baos, ciphertextLen);
+        baos.write(rawCiphertext, 0, rawCiphertext.length);
+        writeShort(baos, macLen);
+        if (macLen > 0) baos.write(mac, 0, mac.length);
+        return baos.toByteArray();
+    }
+
+    private static void debug(String msg) {
+        // System.err.println(msg);
+    }
+
+    private void writeShort(ByteArrayOutputStream baos, short s) {
+        byte[] shortAsByteArray = ByteConversionUtil.fromShort(s);
+        assert (shortAsByteArray.length == 2);
+        baos.write(shortAsByteArray, 0, 2);
+    }
+
+    private void writeInt(ByteArrayOutputStream baos, int i) {
+        byte[] intAsByteArray = ByteConversionUtil.fromInt(i);
+        baos.write(intAsByteArray, 0, 4);
+    }
+
+    private void writeLong(ByteArrayOutputStream baos, long l) {
+        byte[] longAsByteArray = ByteConversionUtil.fromLong(l);
+        assert (longAsByteArray.length == 8);
+        baos.write(longAsByteArray, 0, 8);
+    }
+
+    private void writeString(ByteArrayOutputStream baos, String str)
+    {
+        try
+        {
+            assert ((str != null) && (str.length() > 0));
+            byte[] bytes = str.getBytes("UTF8");
+            assert (bytes.length < 32767) : "writeString: String exceeds max length";
+            writeShort(baos, (short)bytes.length);
+            baos.write(bytes, 0, bytes.length);
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            System.err.println("writeString: " + e.getMessage());
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/PlainTextTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/PlainTextTest.java.svn-base
new file mode 100644
index 0000000..b7e1af0
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/PlainTextTest.java.svn-base
@@ -0,0 +1,133 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.Test;
+import org.owasp.esapi.crypto.PlainText;
+
+public class PlainTextTest {
+	
+	private String unicodeStr = "A\u00ea\u00f1\u00fcC";	// I.e., "AêñüC"
+	private String altString  = "AêñüC";				// Same as above.
+	
+	/* NOTE: This test will not work on Windows unless executed under
+	 * Eclipse and the file is stored / treated as a UTF-8 encoded file
+	 * rather than the Windows native OS encoding of Windows-1252 (aka,
+	 * CP-1252). Therefore this test case has a check to not run the test
+	 * unless
+	 *     unicodeStr.equals(altString)
+	 * is true. If not the test is skipped and a message is printed to stderr.
+	 * Jim Manico made an attempt to address this (see private email to
+	 * kevin.w.wall at gmail.com on 11/26/2009, subject "Re: [OWASP-ESAPI] Unit
+	 * Tests Status") to correct this problem by setting some SVN attribute
+	 * to standardize all source files to UTF-8, but not all Subversion clients
+	 * are either honoring this or perhaps Windows just overrides this. Either
+	 * way, this test (which used to be an assertTrue() expression) was
+	 * introduced to account for this.
+	 */
+	@Test
+	public final void testUnicodeString() {
+	    // These 2 strings are *meant* to be equal. If they are not, please
+	    // do *NOT* change the test. It's a Windows thing. Sorry. Change your
+	    // OS instead. ;-)
+	    if ( ! unicodeStr.equals(altString) ) {
+	        System.err.println("Skipping JUnit test case " +
+	                           "PlainTextTest.testUnicodeString() on OS " +
+	                           System.getProperty("os.name") );
+	        return;
+	    }
+		try {
+			byte[] utf8Bytes = unicodeStr.getBytes("UTF-8");
+			PlainText pt1 = new PlainText(unicodeStr);
+			PlainText pt2 = new PlainText(altString);
+			
+			assertTrue( pt1.equals(pt1) );   // Equals self
+			assertFalse( pt1.equals(null) );
+			assertTrue( pt1.equals(pt2) );
+			assertFalse( pt1.equals( unicodeStr ) );
+			assertTrue( pt1.length() == utf8Bytes.length );
+			assertTrue( Arrays.equals(utf8Bytes, pt1.asBytes()) );			
+			assertTrue( pt1.hashCode() == unicodeStr.hashCode() );
+			
+		} catch (UnsupportedEncodingException e) {
+			fail("No UTF-8 byte encoding: " + e);
+			e.printStackTrace(System.err);
+		}
+	}
+	
+	@Test
+	public final void testNullCase() {
+	    int counter = 0;
+	    try {
+            byte[] bytes = null;
+            PlainText pt = new PlainText(bytes);
+            assertTrue( pt != null );   // Should never get to here.
+            fail("testNullCase(): Expected NullPointerException or AssertionError");
+        } catch (NullPointerException e) {
+            // Will get this case if assertions are not enabled for PlainText.
+            // System.err.println("Caught NullPointerException; exception was: " + e);
+            // e.printStackTrace(System.err);
+            counter++;
+        } catch (AssertionError e) {
+            // Will get this case if assertions *are* enabled for PlainText.
+            // System.err.println("Caught AssertionError; exception was: " + e);
+            // e.printStackTrace(System.err);
+            counter++;
+        } finally {
+            assertTrue( counter > 0 );
+        }
+	}
+
+	@Test
+	public final void testEmptyString() {
+		PlainText mt  = new PlainText("");
+		assertTrue( mt.length() == 0 );
+		byte[] ba = mt.asBytes();
+		assertTrue( ba != null && ba.length == 0 );
+	}
+
+	@Test
+	public final void testOverwrite() {
+		try {
+			byte[] origBytes = unicodeStr.getBytes("UTF-8");
+			PlainText pt = new PlainText(origBytes);
+			assertTrue( pt.toString().equals(unicodeStr) );
+			assertTrue( Arrays.equals(origBytes, pt.asBytes()) );			
+			assertTrue( pt.hashCode() == unicodeStr.hashCode() );
+			
+			int origLen = origBytes.length;
+
+			pt.overwrite();
+	        byte[] overwrittenBytes = pt.asBytes();
+			assertTrue( overwrittenBytes != null );
+			assertFalse( Arrays.equals( origBytes, overwrittenBytes ) );
+
+			// Ensure that ALL the bytes overwritten with '*'.
+			int afterLen = overwrittenBytes.length;
+			assertTrue( origLen == afterLen );
+			int sum = 0;
+			for( int i = 0; i < afterLen; i++ ) {
+			    if ( overwrittenBytes[i] == '*' ) {
+			        sum++;
+			    }
+			}
+			assertTrue( afterLen == sum );
+		} catch (UnsupportedEncodingException e) {
+			fail("No UTF-8 byte encoding: " + e);
+			e.printStackTrace(System.err);
+		}
+	}
+
+	/**
+	 * Run all the test cases in this suite.
+	 * This is to allow running from {@code org.owasp.esapi.AllTests} which
+	 * uses a JUnit 3 test runner.
+	 */
+	public static junit.framework.Test suite() {
+		return new JUnit4TestAdapter(PlainTextTest.class);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/.svn/text-base/SecurityProviderLoaderTest.java.svn-base b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/SecurityProviderLoaderTest.java.svn-base
new file mode 100644
index 0000000..cba2da0
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/.svn/text-base/SecurityProviderLoaderTest.java.svn-base
@@ -0,0 +1,139 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Test for class {@code SecurityProviderLoader}. Note that these tests
+ * use Bouncy Castle's JCE provider so a version their jar must be added
+ * to your class path. If you wish to add it via Maven, you can do so by
+ * adding this to your <b><i>pom.xml</i></b>:
+ * <pre>
+ * <dependency>
+ *      <groupId>org.bouncycastle</groupId>
+ *      <artifactId>bcprov-jdk15</artifactId>
+ *      <version>1.44</version>
+ * </dependency>
+ * </pre>
+ * It has been tested with Bouncy Castle 1.44, but any later version should
+ * do as well.
+ * @author kevin.w.wall at gmail.com
+ */
+public class SecurityProviderLoaderTest {
+
+    private static boolean HAS_BOUNCY_CASTLE = false;
+    
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        try {
+            Class<?> providerClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
+            Provider cryptoProvider = (Provider)providerClass.newInstance();
+            assertTrue( cryptoProvider != null );
+            HAS_BOUNCY_CASTLE = true;
+        } catch(Exception ex) {
+            // Note: FindBugs reports a false positive here...
+            //    REC_CATCH_EXCEPTION: Exception is caught when Exception is not thrown
+            // but exceptions really can be thrown.
+            HAS_BOUNCY_CASTLE = false;
+        }
+    }
+
+    @Test
+    public final void testInsertProviderAt() {
+        if ( ! HAS_BOUNCY_CASTLE ) {
+            System.out.println("SecurityProviderLoaderTest.testInsertProviderAt(): " +
+                               "Skipping test -- must have Bouncy Castle JCE provider in classpath.");
+            return;
+        }
+
+        try {
+            SecurityProviderLoader.insertProviderAt("BC", 1);
+            assertTrue(true);
+        } catch (NoSuchProviderException e) {
+            fail("Caught NoSuchProviderException trying to load Bouncy Castle; exception was: " + e);
+        }
+    }
+
+    @Test
+    public final void testLoadESAPIPreferredJCEProvider() {
+        // Note: OK if empty string or unset, in fact default is empty string.
+        String preferredProvider = ESAPI.securityConfiguration().getPreferredJCEProvider();
+        try {
+            SecurityProviderLoader.loadESAPIPreferredJCEProvider();
+            assertTrue(true);
+        } catch (NoSuchProviderException e) {
+            fail("Caught NoSuchProviderException trying to preferred JCE provider " +
+                 preferredProvider + "; exception was: " + e);
+        }
+    }
+    
+    @Test(expected=NoSuchProviderException.class)
+    public final void testNoSuchProviderException() throws NoSuchProviderException {
+        SecurityProviderLoader.insertProviderAt("DrBobsSecretSnakeOilElixirCryptoJCE", 5);
+    }
+
+    @Test(expected=NoSuchProviderException.class)
+    public final void testBogusProviderWithFQCN() throws NoSuchProviderException {
+        SecurityProviderLoader.insertProviderAt("com.snakeoil.DrBobsSecretSnakeOilElixirCryptoJCE", 5);
+    }
+    
+    @Test
+    public final void testWithBouncyCastle() {
+        if ( ! HAS_BOUNCY_CASTLE ) {
+            System.out.println("SecurityProviderLoaderTest.testInsertProviderAt(): " +
+                               "Skipping test -- must have Bouncy Castle JCE provider in classpath.");
+            return;
+        }
+
+        try {
+            SecurityProviderLoader.insertProviderAt("BC", 1);
+            assertTrue(true);
+        } catch (NoSuchProviderException e) {
+            fail("Caught NoSuchProviderException trying to load Bouncy Castle; exception was: " + e);
+        }
+        
+        // First encrypt w/ preferred cipher transformation (AES/CBC/PKCS5Padding).
+        try {
+            PlainText clearMsg = new PlainText("This is top secret! We are all out of towels!");
+            String origMsg = clearMsg.toString(); // Must keep 'cuz by default, clearMsg is overwritten.
+            CipherText ct = ESAPI.encryptor().encrypt(clearMsg);
+            assertEquals( "*********************************************", clearMsg.toString() );
+            PlainText plain = ESAPI.encryptor().decrypt(ct);
+            assertEquals( origMsg, plain.toString() );
+        } catch (EncryptionException e) {
+            fail("Encryption w/ Bouncy Castle failed with EncryptionException for preferred " +
+                 "cipher transformation; exception was: " + e);
+        }
+        
+        // Next, try a "combined mode" cipher mode available in Bouncy Castle.
+        String origCipherXform = null;
+        try {
+            origCipherXform = ESAPI.securityConfiguration().setCipherTransformation("AES/GCM/NoPadding");
+            PlainText clearMsg = new PlainText("This is top secret! We are all out of towels!");
+            String origMsg = clearMsg.toString(); // Must keep 'cuz by default, clearMsg is overwritten.
+            CipherText ct = ESAPI.encryptor().encrypt(clearMsg);
+            PlainText plain = ESAPI.encryptor().decrypt(ct);
+            assertEquals( origMsg, plain.toString() );
+            // Verify that no MAC is calculated for GCM cipher mode. There is no method to
+            // validate this, so we look at the String representation of this CipherText
+            // object and pick it out of there.
+            String str = ct.toString();
+            assertTrue( str.matches(".*, MAC is absent;.*") );
+        } catch (EncryptionException e) {
+            fail("Encryption w/ Bouncy Castle failed with EncryptionException for preferred " +
+                 "cipher transformation; exception was: " + e);
+        } finally {
+            ESAPI.securityConfiguration().setCipherTransformation(origCipherXform);
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java b/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java
new file mode 100644
index 0000000..b842aae
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java
@@ -0,0 +1,273 @@
+package org.owasp.esapi.crypto;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import javax.crypto.Cipher;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherSpec;
+
+/** JUnit test to test CipherSpec class. */
+public class CipherSpecTest extends TestCase {
+
+	private Cipher dfltAESCipher = null;
+	private Cipher dfltECBCipher = null;	// will be "AES/ECB/NoPadding";
+	private Cipher dfltOtherCipher = null;
+	private CipherSpec cipherSpec = null;
+	private byte[] myIV = null;
+
+	@Before public void setUp() throws Exception {
+			// This will throw ConfigurationException if IV type is not set to
+			// 'fixed', which it's not. (We have it set to 'random'.)
+		// myIV = Hex.decode( ESAPI.securityConfiguration().getFixedIV() );
+		myIV = Hex.decode( "0x000102030405060708090a0b0c0d0e0f" );
+
+		dfltAESCipher   = Cipher.getInstance("AES");
+		dfltECBCipher   = Cipher.getInstance("AES/ECB/NoPadding");
+		dfltOtherCipher = Cipher.getInstance("Blowfish/OFB8/PKCS5Padding");
+
+		assertTrue( dfltAESCipher != null );
+		assertTrue( dfltECBCipher != null );
+		assertTrue( dfltOtherCipher != null );
+
+		cipherSpec = new CipherSpec(dfltOtherCipher);
+		assertTrue( cipherSpec != null );
+	}
+
+	@After public void tearDown() throws Exception {
+    	// none
+	}
+	
+	/** Test CipherSpec(String cipherXform, int keySize, int blockSize, final byte[] iv) */
+	@Test public void testCipherSpecStringIntIntByteArray() {
+		
+		cipherSpec = new CipherSpec( "AES/CBC/NoPadding",  128,  8, myIV);
+		assertTrue( cipherSpec != null );
+		cipherSpec = null;
+		boolean caughtException = false;
+		try {
+				// Invalid cipher xform -- empty
+			cipherSpec = new CipherSpec( "",  128,  8, myIV);
+		} catch( Throwable t ) {
+			caughtException = true;
+		}
+		assertTrue( caughtException && (cipherSpec == null) );
+		caughtException = false;
+		try {
+				// Invalid cipher xform -- missing padding scheme
+			cipherSpec = new CipherSpec("AES/CBC", 128, 8, myIV);
+		} catch( Throwable t ) {
+		    caughtException = true;
+		}
+        assertTrue( caughtException && (cipherSpec == null) );
+	}
+
+	/** CipherSpec(final Cipher cipher, int keySize) */
+	@Test public void testCipherSpecCipherInt() {
+    	cipherSpec = new CipherSpec(dfltOtherCipher, 112);
+    	assertTrue( cipherSpec != null );
+    	assertTrue( cipherSpec.getCipherAlgorithm().equals("Blowfish"));
+    	assertTrue( cipherSpec.getCipherMode().equals("OFB8"));
+    	
+    	cipherSpec = new CipherSpec(dfltAESCipher, 256);
+    	assertTrue( cipherSpec != null );
+    	assertTrue( cipherSpec.getCipherAlgorithm().equals("AES"));
+    	assertTrue( cipherSpec.getCipherMode().equals("ECB") );
+    	assertTrue( cipherSpec.getPaddingScheme().equals("NoPadding") );
+    	// System.out.println("testCipherSpecInt(): " + cipherSpec);
+	}
+
+	/** Test CipherSpec(final byte[] iv) */
+	@Test public void testCipherSpecByteArray() {
+		assertTrue( myIV != null );
+		assertTrue( myIV.length > 0 );
+		cipherSpec = new CipherSpec(myIV);
+		assertTrue( cipherSpec.getKeySize() == 
+						ESAPI.securityConfiguration().getEncryptionKeyLength() );
+		assertTrue( cipherSpec.getCipherTransformation().equals(
+						ESAPI.securityConfiguration().getCipherTransformation() ) );
+	}
+
+	/** Test CipherSpec() */
+	@Test public void testCipherSpec() {
+		cipherSpec = new CipherSpec( dfltECBCipher );
+		assertTrue( cipherSpec.getCipherTransformation().equals("AES/ECB/NoPadding") );
+		assertTrue( cipherSpec.getIV() == null );
+	
+		cipherSpec = new CipherSpec(dfltOtherCipher);
+		assertTrue( cipherSpec.getCipherMode().equals("OFB8") );
+	}
+
+	/** Test setCipherTransformation(String cipherXform) */
+	@Test public void testSetCipherTransformation() {
+		cipherSpec = new CipherSpec();
+		cipherSpec.setCipherTransformation("AlgName/Mode/Padding");
+		cipherSpec.getCipherAlgorithm().equals("AlgName/Mode/Padding");
+		
+		try {
+				// Don't use null here as compiling JUnit tests disables assertion
+				// checking so we get a NullPointerException here instead.
+			cipherSpec.setCipherTransformation(""); // Throws IllegalArgumentException
+		} catch (IllegalArgumentException e) {
+			assertTrue(true);	// Doesn't work w/ @Test(expected=IllegalArgumentException.class)
+		}
+	}
+
+	/** Test getCipherTransformation() */
+	@Test public void testGetCipherTransformation() {
+		assertTrue( (new CipherSpec()).getCipherTransformation().equals("AES/CBC/PKCS5Padding") );
+	}
+
+	/** Test setKeySize() */
+	@Test public void testSetKeySize() {
+		assertTrue( (new CipherSpec()).setKeySize(56).getKeySize() == 56 );
+	}
+
+	/** Test getKeySize() */
+	@Test public void testGetKeySize() {
+		assertTrue( (new CipherSpec()).getKeySize() ==
+			ESAPI.securityConfiguration().getEncryptionKeyLength() );
+	}
+
+	/** Test setBlockSize() */
+	@Test public void testSetBlockSize() {
+		try {
+			cipherSpec.setBlockSize(0); // Throws AssertionError
+		} catch (AssertionError e) {
+			assertTrue(true);	// Doesn't work w/ @Test(expected=AssertionError.class)
+		}
+		try {
+			cipherSpec.setBlockSize(-1); // Throws AssertionError
+		} catch (AssertionError e) {
+			assertTrue(true);	// Doesn't work w/ @Test(expected=AssertionError.class)
+		}
+		assertTrue( cipherSpec.setBlockSize(4).getBlockSize() == 4 );
+	}
+
+	/** Test getBlockSize() */
+	@Test public void testGetBlockSize() {
+		assertTrue( cipherSpec.getBlockSize() == 8 );
+	}
+
+	/** Test getCipherAlgorithm() */
+	@Test public void testGetCipherAlgorithm() {
+		assertTrue( cipherSpec.getCipherAlgorithm().equals("Blowfish") );
+	}
+
+	/** Test getCipherMode */
+	@Test public void testGetCipherMode() {
+		assertTrue( cipherSpec.getCipherMode().equals("OFB8") );
+	}
+
+	/** Test getPaddingScheme() */
+	@Test public void testGetPaddingScheme() {
+		assertTrue( cipherSpec.getPaddingScheme().equals("PKCS5Padding") );
+	}
+
+	/** Test setIV() */
+	@Test public void testSetIV() {
+		try {
+			// Test that ECB mode allows a null IV
+			cipherSpec = new CipherSpec(dfltECBCipher);
+			cipherSpec.setIV(null);
+			assertTrue(true);
+		} catch ( AssertionError e) {
+			assertFalse("Test failed; unexpected exception", false);
+		}
+		try {
+			// Test that CBC mode does allows a null IV
+			cipherSpec = new CipherSpec(dfltAESCipher);
+			cipherSpec.setIV(null);
+			assertFalse("Test failed; Expected exception not thrown", false);
+		} catch ( AssertionError e) {
+			assertTrue(true);
+		}
+	}
+
+	/** Test requiresIV() */
+	@Test public void testRequiresIV() {
+		assertTrue( (new CipherSpec(dfltECBCipher)).requiresIV() == false );
+		cipherSpec = new CipherSpec(dfltAESCipher);
+		assertTrue( cipherSpec.getCipherMode().equals("ECB") );
+		assertTrue( cipherSpec.requiresIV() == false );
+		assertTrue( new CipherSpec(dfltOtherCipher).requiresIV() );
+	}
+	
+	/** Test serialization */
+	@Test public void testSerialization() {
+        String filename = "cipherspec.ser";
+        File serializedFile = new File(filename);
+        boolean success = false;
+        try {
+            // Delete any old serialized file. If it fails, it's not
+            // a big deal. If we can't overwrite it later, we'll get
+            // an IOException.
+            //
+            // NOTE: FindBugs complains we are not checking return value here.
+            //       Guess what? We don't care!!!
+            serializedFile.delete();
+
+            
+            cipherSpec = new CipherSpec( "AES/CBC/NoPadding",  128,  8, myIV);
+            FileOutputStream fos = new FileOutputStream(filename);
+            ObjectOutputStream out = new ObjectOutputStream(fos);
+            out.writeObject(cipherSpec);
+            out.close();
+            fos.close();
+
+            FileInputStream fis = new FileInputStream(filename);
+            ObjectInputStream in = new ObjectInputStream(fis);
+            CipherSpec restoredCipherSpec = (CipherSpec)in.readObject();
+            in.close();
+            fis.close();
+
+            // check that cipherSpec and restoredCipherSpec are equal. Just
+            // compare them via their string representations.
+            assertEquals("Serialized restored CipherSpec differs from saved CipherSpec",
+            			 cipherSpec.toString(), restoredCipherSpec.toString() );
+            
+            success = true;
+        } catch(IOException ex) {
+            ex.printStackTrace(System.err);
+            fail("testSerialization(): Unexpected IOException: " + ex);
+        } catch(ClassNotFoundException ex) {
+            ex.printStackTrace(System.err);
+            fail("testSerialization(): Unexpected ClassNotFoundException: " + ex);
+        } finally {
+            // If test succeeds, remove the file. If it fails, leave it behind
+            // for further analysis.
+            if ( success && serializedFile.exists() ) {
+                boolean deleted = serializedFile.delete();
+                if ( !deleted ) {
+                    try {
+                        System.err.println("Unable to delete file: " + serializedFile.getCanonicalPath() );
+                    } catch (IOException e) {
+                        ; // Ignore
+                    }
+                }
+            }
+        }
+	}
+	
+    /**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+     */
+    public static junit.framework.Test suite() {
+        TestSuite suite = new TestSuite(CipherSpecTest.class);
+
+        return suite;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/crypto/CipherTextSerializerTest.java b/src/test/java/org/owasp/esapi/crypto/CipherTextSerializerTest.java
new file mode 100644
index 0000000..9f86b9f
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/CipherTextSerializerTest.java
@@ -0,0 +1,83 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.EncryptionException;
+
+public class CipherTextSerializerTest {
+    private Cipher encryptor = null;
+    private IvParameterSpec ivSpec = null;  // Note: FindBugs reports false positive
+                                            // about this being unread field. See
+    										// testAsSerializedByteArray().
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        encryptor = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        byte[] ivBytes = null;
+        ivBytes = ESAPI.randomizer().getRandomBytes(encryptor.getBlockSize());
+        ivSpec = new IvParameterSpec(ivBytes);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    	System.out.flush();
+    }
+
+    @Test
+    public final void testAsSerializedByteArray() {
+    	System.out.println("CipherTextSerializerTest.testAsSerializedByteArray() ...");
+        CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+        cipherSpec.setIV(ivSpec.getIV());
+        SecretKey key;
+        try {
+            key = CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+            encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+
+            byte[] raw = encryptor.doFinal("Hello".getBytes("UTF8"));
+            CipherText ct = ESAPI.encryptor().encrypt(key, new PlainText("Hello") );
+            assertTrue( ct != null );   // Here to eliminate false positive from FindBugs.
+            CipherTextSerializer cts = new CipherTextSerializer( ct );
+            byte[] serializedBytes = cts.asSerializedByteArray();
+            CipherText result = CipherText.fromPortableSerializedBytes(serializedBytes);
+            PlainText pt = ESAPI.encryptor().decrypt(key, result);
+            assertTrue( "Hello".equals( pt.toString() ) );
+        } catch (Exception e) {
+            fail("Test failed: Caught exception: " + e.getClass().getName() + "; msg was: " + e);
+            e.printStackTrace(System.err);
+        }
+    }
+
+    @Test
+    public final void testAsCipherText() {
+        try {
+        	System.out.println("CipherTextSerializerTest.testAsCipherText() ...");
+            CipherText ct = ESAPI.encryptor().encrypt( new PlainText("Hello") );
+            CipherTextSerializer cts = new CipherTextSerializer( ct );
+            CipherText result = cts.asCipherText();
+            assertTrue( ct.equals(result) );
+            PlainText pt = ESAPI.encryptor().decrypt(result);
+            assertTrue( "Hello".equals( pt.toString() ) );
+        } catch (EncryptionException e) {
+            fail("Caught EncryptionException; exception msg: " + e);
+        }
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java b/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java
new file mode 100644
index 0000000..18c7fb5
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java
@@ -0,0 +1,390 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+import junit.framework.Assert;
+import junit.framework.JUnit4TestAdapter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.crypto.CipherSpec;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.crypto.CryptoPolicy;
+
+public class CipherTextTest {
+
+    private static final boolean POST_CLEANUP = true;
+    
+	private CipherSpec cipherSpec_ = null;
+    private Cipher encryptor = null;
+    private Cipher decryptor = null;
+    private IvParameterSpec ivSpec = null;
+	
+    @BeforeClass public static void preCleanup() {
+    	try {
+            // These two calls have side-effects that cause FindBugs to complain.
+    		removeFile("ciphertext.ser");
+    		removeFile("ciphertext-portable.ser");
+    		// Do NOT remove this file...
+    		//		src/test/resource/ESAPI2.0-ciphertext-portable.ser
+    	} catch(Exception ex) {
+    		;	// Do nothing
+    	}
+    }
+    
+	@Before
+	public void setUp() throws Exception {
+        encryptor = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        decryptor = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        byte[] ivBytes = null;
+        ivBytes = ESAPI.randomizer().getRandomBytes(encryptor.getBlockSize());
+        ivSpec = new IvParameterSpec(ivBytes);
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+	
+	@AfterClass public static void postCleanup() {
+	    if ( POST_CLEANUP ) {
+	            // These two calls have side-effects that cause FindBugs to complain.
+	        removeFile("ciphertext.ser");
+	        removeFile("ciphertext-portable.ser");
+	    }
+	}
+
+	/** Test the default CTOR */
+	@Test
+	public final void testCipherText() {
+		CipherText ct =  new CipherText();
+
+		cipherSpec_ = new CipherSpec();
+		assertTrue( ct.getCipherTransformation().equals( cipherSpec_.getCipherTransformation()));
+		assertTrue( ct.getBlockSize() == cipherSpec_.getBlockSize() );
+	}
+
+	@Test
+	public final void testCipherTextCipherSpec() {
+		cipherSpec_ = new CipherSpec("DESede/OFB8/NoPadding", 112);
+		CipherText ct = new CipherText( cipherSpec_ );
+		assertTrue( ct.getRawCipherText() == null );
+		assertTrue( ct.getCipherAlgorithm().equals("DESede") );
+		assertTrue( ct.getKeySize() == cipherSpec_.getKeySize() );
+	}
+
+	@Test
+	public final void testCipherTextCipherSpecByteArray()
+	{
+		try {
+			CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] raw = encryptor.doFinal("Hello".getBytes("UTF8"));
+			CipherText ct = new CipherText(cipherSpec, raw);
+			assertTrue( ct != null );
+			byte[] ctRaw = ct.getRawCipherText();
+			assertTrue( ctRaw != null );
+			assertArrayEquals(raw, ctRaw);
+			assertTrue( ct.getCipherTransformation().equals(cipherSpec.getCipherTransformation()) );;
+			assertTrue( ct.getCipherAlgorithm().equals(cipherSpec.getCipherAlgorithm()) );
+			assertTrue( ct.getPaddingScheme().equals(cipherSpec.getPaddingScheme()) );
+			assertTrue( ct.getBlockSize() == cipherSpec.getBlockSize() );
+			assertTrue( ct.getKeySize() == cipherSpec.getKeySize() );
+			byte[] ctIV = ct.getIV();
+			byte[] csIV = cipherSpec.getIV();
+			assertArrayEquals(ctIV, csIV);
+		} catch( Exception ex) {
+			// As far as test coverage goes, we really don't want this to be covered.
+			fail("Caught unexpected exception: " + ex.getClass().getName() +
+					    "; exception message was: " + ex.getMessage());
+		}
+	}
+
+
+	@Test
+	public final void testDecryptionUsingCipherText() {
+		try {
+			CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			assertTrue( cipherSpec.getIV() != null );
+			assertTrue( cipherSpec.getIV().length > 0 );
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] ctraw = encryptor.doFinal("Hello".getBytes("UTF8"));
+			CipherText ct = new CipherText(cipherSpec, ctraw);
+			assertTrue( ct.getCipherMode().equals("CBC") );
+			assertTrue( ct.requiresIV() ); // CBC mode requires an IV.
+			String b64ctraw = ct.getBase64EncodedRawCipherText();
+			assertTrue( b64ctraw != null);
+			assertArrayEquals( ESAPI.encoder().decodeFromBase64(b64ctraw), ctraw );
+			decryptor.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ct.getIV()));
+			byte[] ptraw = decryptor.doFinal(ESAPI.encoder().decodeFromBase64(b64ctraw));
+			assertTrue( ptraw != null );
+			assertTrue( ptraw.length > 0 );
+			String plaintext = new String( ptraw, "UTF-8");
+			assertTrue( plaintext.equals("Hello") );
+			assertArrayEquals( ct.getRawCipherText(), ctraw );
+			
+			byte[] ivAndRaw = ESAPI.encoder().decodeFromBase64( ct.getEncodedIVCipherText() );
+			assertTrue( ivAndRaw.length > ctraw.length );
+			assertTrue( ct.getBlockSize() == ( ivAndRaw.length - ctraw.length ) );
+		} catch( Exception ex) {
+		    // Note: FindBugs reports a false positive here...
+		    //    REC_CATCH_EXCEPTION: Exception is caught when Exception is not thrown
+		    // but exceptions really can be thrown. This probably is because FindBugs
+		    // examines the byte-code rather than the source code. However "fixing" this
+		    // so that it doesn't complain will make the test much more complicated as there
+		    // are about 3 or 4 different exception types.
+		    //
+		    // On a completely different note, as far as test coverage metrics goes,
+			// we really don't care if this is covered or nit as it is not our intent
+		    // to be causing exceptions here.
+			ex.printStackTrace(System.err);
+			fail("Caught unexpected exception: " + ex.getClass().getName() +
+					"; exception message was: " + ex.getMessage());
+		}
+	}
+
+	@Test
+	public final void testMIC() {
+		try {
+			CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] ctraw = encryptor.doFinal("Hello".getBytes("UTF8"));
+			CipherText ct = new CipherText(cipherSpec, ctraw);
+			assertTrue( ct.getIV() != null && ct.getIV().length > 0 );
+			SecretKey authKey = CryptoHelper.computeDerivedKey(key, key.getEncoded().length * 8, "authenticity");
+			ct.computeAndStoreMAC( authKey ); 
+			try {
+				ct.setIVandCiphertext(ivSpec.getIV(), ctraw);	// Expected to log & throw.
+			} catch( Exception ex ) {
+				assertTrue( ex instanceof EncryptionException );
+			}
+			try {
+				ct.setCiphertext(ctraw);	// Expected to log and throw message about
+											// not being able to store raw ciphertext.
+			} catch( Exception ex ) {
+				assertTrue( ex instanceof EncryptionException );
+			}
+			decryptor.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec( ct.getIV() ) );
+			byte[] ptraw = decryptor.doFinal( ct.getRawCipherText() );
+			assertTrue( ptraw != null && ptraw.length > 0 );
+			ct.validateMAC( authKey );
+		} catch( Exception ex) {
+			// As far as test coverage goes, we really don't want this to be covered.
+			ex.printStackTrace(System.err);
+			fail("Caught unexpected exception: " + ex.getClass().getName() +
+					"; exception message was: " + ex.getMessage());
+		}
+	}
+
+	/** Test <i>portable</i> serialization. */
+	@Test public final void testPortableSerialization() {
+	    System.out.println("CipherTextTest.testPortableSerialization()starting...");
+	    String filename = "ciphertext-portable.ser";
+	    File serializedFile = new File(filename);
+	    serializedFile.delete();    // Delete any old serialized file.
+
+	    int keySize = 128;
+	    if ( CryptoPolicy.isUnlimitedStrengthCryptoAvailable() ) {
+	        keySize = 256;
+	    }
+	    CipherSpec cipherSpec = new CipherSpec(encryptor, keySize);
+	    cipherSpec.setIV(ivSpec.getIV());
+	    SecretKey key;
+	    try {
+	        key = CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), keySize);
+
+	        encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+	        byte[] raw = encryptor.doFinal("This is my secret message!!!".getBytes("UTF8"));
+	        CipherText ciphertext = new CipherText(cipherSpec, raw);
+	        	// TODO: Replace this w/ call to KeyDerivationFunction as this is
+	        	//		 deprecated! Shame on me!
+	        SecretKey authKey = CryptoHelper.computeDerivedKey(key, key.getEncoded().length * 8, "authenticity");
+	        ciphertext.computeAndStoreMAC( authKey );
+//          System.err.println("Original ciphertext being serialized: " + ciphertext);
+	        byte[] serializedBytes = ciphertext.asPortableSerializedByteArray();
+	        
+	        FileOutputStream fos = new FileOutputStream(serializedFile);
+            fos.write(serializedBytes);
+                // Note: FindBugs complains that this test may fail to close
+                // the fos output stream. We don't really care.
+            fos.close();
+            
+            // NOTE: FindBugs complains about this (OS_OPEN_STREAM). It apparently
+            //       is too lame to know that 'fis.read()' is a serious side-effect.
+            FileInputStream fis = new FileInputStream(serializedFile);
+            int avail = fis.available();
+            byte[] bytes = new byte[avail];
+            fis.read(bytes, 0, avail);
+            
+            // Sleep one second to prove that the timestamp on the original
+            // CipherText object is the one that we use and not just the
+            // current time. Only after that, do we restore the serialized bytes.
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                ;    // Ignore
+            }       
+            CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(bytes);
+//          System.err.println("Restored ciphertext: " + restoredCipherText);
+            assertTrue( ciphertext.equals(restoredCipherText));
+	    } catch (EncryptionException e) {
+	        Assert.fail("Caught EncryptionException: " + e);
+        } catch (FileNotFoundException e) {
+            Assert.fail("Caught FileNotFoundException: " + e);
+        } catch (IOException e) {
+            Assert.fail("Caught IOException: " + e);
+        } catch (Exception e) {
+            Assert.fail("Caught Exception: " + e);
+        } finally {
+            // FindBugs complains that we are ignoring this return value. We really don't care.
+            serializedFile.delete();
+        }
+	}
+	
+	/** Test <i>portable</i> serialization for backward compatibility with ESAPI 2.0. */
+	@Test public final void testPortableSerializationBackwardCompatibility() {
+	    System.out.println("testPortableSerializationBackwardCompatibility()starting...");
+	    String filename = "src/test/resources/ESAPI2.0-ciphertext-portable.ser";  // Do NOT remove
+	    File serializedFile = new File(filename);
+
+	    try {
+	    	// String expectedMsg = "This is my secret message!!!";
+            
+            // NOTE: FindBugs complains about this (OS_OPEN_STREAM). It apparently
+            //       is too lame to know that 'fis.read()' is a serious side-effect.
+            FileInputStream fis = new FileInputStream(serializedFile);
+            int avail = fis.available();
+            byte[] bytes = new byte[avail];
+            fis.read(bytes, 0, avail);
+            // We can't go as far and decrypt it because the file was encrypted using a
+            // temporary session key.
+            CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(bytes);
+            assertTrue( restoredCipherText != null );
+            int retrievedKdfVersion = restoredCipherText.getKDFVersion();
+	    } catch (EncryptionException e) {
+	        Assert.fail("Caught EncryptionException: " + e);
+        } catch (FileNotFoundException e) {
+            Assert.fail("Caught FileNotFoundException: " + e);
+        } catch (IOException e) {
+            Assert.fail("Caught IOException: " + e);
+        } catch (Exception e) {
+            Assert.fail("Caught Exception: " + e);
+        } finally {
+        	; // Do NOT delete the file.
+        }
+	}
+	
+	/** Test Java serialization. */
+	@Test public final void testJavaSerialization() {
+        String filename = "ciphertext.ser";
+        File serializedFile = new File(filename);
+        try {
+            serializedFile.delete();	// Delete any old serialized file.
+            
+            CipherSpec cipherSpec = new CipherSpec(encryptor, 128);
+			cipherSpec.setIV(ivSpec.getIV());
+			SecretKey key =
+				CryptoHelper.generateSecretKey(cipherSpec.getCipherAlgorithm(), 128);
+			encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec);
+			byte[] raw = encryptor.doFinal("This is my secret message!!!".getBytes("UTF8"));
+			CipherText ciphertext = new CipherText(cipherSpec, raw);
+
+            FileOutputStream fos = new FileOutputStream(filename);
+            ObjectOutputStream out = new ObjectOutputStream(fos);
+            out.writeObject(ciphertext);
+            out.close();
+            fos.close();
+
+            FileInputStream fis = new FileInputStream(filename);
+            ObjectInputStream in = new ObjectInputStream(fis);
+            CipherText restoredCipherText = (CipherText)in.readObject();
+            in.close();
+            fis.close();
+
+            // check that ciphertext and restoredCipherText are equal. Requires
+            // multiple checks. (Hmmm... maybe overriding equals() and hashCode()
+            // is in order???)
+            assertEquals("1: Serialized restored CipherText differs from saved CipherText",
+            			 ciphertext.toString(), restoredCipherText.toString());
+            assertArrayEquals("2: Serialized restored CipherText differs from saved CipherText",
+            			 ciphertext.getIV(), restoredCipherText.getIV());
+            assertEquals("3: Serialized restored CipherText differs from saved CipherText",
+            			 ciphertext.getBase64EncodedRawCipherText(),
+            			 restoredCipherText.getBase64EncodedRawCipherText());
+            
+        } catch(IOException ex) {
+            ex.printStackTrace(System.err);
+            fail("testJavaSerialization(): Unexpected IOException: " + ex);
+        } catch(ClassNotFoundException ex) {
+            ex.printStackTrace(System.err);
+            fail("testJavaSerialization(): Unexpected ClassNotFoundException: " + ex);
+        } catch (EncryptionException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected EncryptionException: " + ex);
+		} catch (IllegalBlockSizeException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected IllegalBlockSizeException: " + ex);
+		} catch (BadPaddingException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected BadPaddingException: " + ex);
+		} catch (InvalidKeyException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected InvalidKeyException: " + ex);
+		} catch (InvalidAlgorithmParameterException ex) {
+			ex.printStackTrace(System.err);
+			fail("testJavaSerialization(): Unexpected InvalidAlgorithmParameterException: " + ex);
+		}  finally {
+		    // FindBugs complains that we are ignoring this return value. We really don't care.
+            serializedFile.delete();
+        }
+	}
+	
+	/**
+	 * Run all the test cases in this suite.
+	 * This is to allow running from {@code org.owasp.esapi.AllTests} which
+	 * uses a JUnit 3 test runner.
+	 */
+	public static junit.framework.Test suite() {
+		return new JUnit4TestAdapter(CipherTextTest.class);
+	}
+	
+	private static void removeFile(String fname) {
+    	try {
+    		if ( fname != null ) {
+    			File f = new File(fname);
+    			// Findbugs complains about ignoring this return value. Too bad.
+    			f.delete();
+    		}
+    	} catch(Exception ex) {
+    		;	// Do nothing
+    	}
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java
new file mode 100644
index 0000000..678b0ac
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java
@@ -0,0 +1,189 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.util.Random;
+
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+
+import junit.framework.JUnit4TestAdapter;
+
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.errors.EncryptionException;
+
+public class CryptoHelperTest {
+
+    @Test
+    public final void testGenerateSecretKeySunnyDay() {
+        try {
+            SecretKey key = CryptoHelper.generateSecretKey("AES", 128);
+            assertTrue(key.getAlgorithm().equals("AES"));
+            assertTrue(128 / 8 == key.getEncoded().length);
+        } catch (EncryptionException e) {
+            // OK if not covered in code coverage -- not expected.
+            fail("Caught unexpected EncryptionException; msg was "
+                    + e.getMessage());
+        }
+    }
+
+    @Test(expected = EncryptionException.class)
+    public final void testGenerateSecretKeyEncryptionException()
+            throws EncryptionException {
+        SecretKey key = CryptoHelper.generateSecretKey("NoSuchAlg", 128);
+        assertTrue(key == null); // Not reached!
+    }
+
+    @Test
+    public final void testOverwriteByteArrayByte() {
+        byte[] secret = "secret password".getBytes();
+        int len = secret.length;
+        CryptoHelper.overwrite(secret, (byte) 'x');
+        assertTrue(secret.length == len); // Length unchanged
+        assertTrue(checkByteArray(secret, (byte) 'x')); // Filled with 'x'
+    }
+
+    @Test
+    public final void testCopyByteArraySunnyDay() {
+        byte[] src = new byte[20];
+        fillByteArray(src, (byte) 'A');
+        byte[] dest = new byte[20];
+        fillByteArray(dest, (byte) 'B');
+        CryptoHelper.copyByteArray(src, dest);
+        assertTrue(checkByteArray(src, (byte) 'A')); // Still filled with 'A'
+        assertTrue(checkByteArray(dest, (byte) 'A')); // Now filled with 'B'
+    }
+
+    @Test(expected = NullPointerException.class)
+    public final void testCopyByteArraySrcNullPointerException() {
+        byte[] ba = new byte[16];
+        CryptoHelper.copyByteArray(null, ba, ba.length);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public final void testCopyByteArrayDestNullPointerException() {
+        byte[] ba = new byte[16];
+        CryptoHelper.copyByteArray(ba, null, ba.length);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public final void testCopyByteArrayIndexOutOfBoundsException() {
+        byte[] ba8 = new byte[8];
+        byte[] ba16 = new byte[16];
+        CryptoHelper.copyByteArray(ba8, ba16, ba16.length);
+    }
+
+    @Test
+    public final void testArrayCompare() {
+        byte[] ba1 = new byte[32];
+        byte[] ba2 = new byte[32];
+        byte[] ba3 = new byte[48];
+
+        // Note: Don't need cryptographically secure random numbers for this!
+        Random prng = new Random();
+
+        prng.nextBytes(ba1);
+        prng.nextBytes(ba2);
+        prng.nextBytes(ba3);
+
+        /*
+         * Unfortunately, can't rely no the nanosecond timer because as the
+         * Javadoc for System.nanoTime() states, " No guarantees are made
+         * about how frequently values change", so this is not very reliable.
+         * 
+         * However, on can uncomment the code and observe that elapsed times
+         * are generally less than 10 millionth of a second. I suppose if we
+         * declared a large enough epsilon, we could make it work, but it is
+         * easier to convince yourself from the CryptoHelper.arrayCompare() code
+         * itself that it always goes through all the bits of the byte array
+         * if it compares any bits at all.
+         */
+        
+//        long start, stop, diff;
+
+//        start = System.nanoTime();
+        assertTrue(CryptoHelper.arrayCompare(null, null));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertTrue(CryptoHelper.arrayCompare(ba1, ba1));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertFalse(CryptoHelper.arrayCompare(ba1, ba2));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertFalse(CryptoHelper.arrayCompare(ba1, ba3));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+
+//        start = System.nanoTime();
+        assertFalse(CryptoHelper.arrayCompare(ba1, null));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+        
+        ba2 = ba1;
+//        start = System.nanoTime();
+        assertTrue(CryptoHelper.arrayCompare(ba1, ba2));
+//        stop = System.nanoTime();
+//        diff = stop - start;
+//        System.out.println("diff: " + diff + " nanosec");
+    }
+
+    @Test
+    public final void testIsValidKDFVersion() {
+    	assertTrue( CryptoHelper.isValidKDFVersion(20110203, false, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(20130830, false, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(33330303, false, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(99991231, false, false));
+
+    	assertFalse( CryptoHelper.isValidKDFVersion(0, false, false));
+    	assertFalse( CryptoHelper.isValidKDFVersion(99991232, false, false));
+    	assertFalse( CryptoHelper.isValidKDFVersion(20110202, false, false));
+
+    	assertTrue( CryptoHelper.isValidKDFVersion(20110203, true, false));
+    	assertTrue( CryptoHelper.isValidKDFVersion(KeyDerivationFunction.kdfVersion, true, false));
+    	assertFalse( CryptoHelper.isValidKDFVersion(KeyDerivationFunction.kdfVersion + 1, true, false));
+
+    	try {
+        	CryptoHelper.isValidKDFVersion(77777777, true, true);
+        	fail("Failed to CryptoHelper.isValidKDFVersion() failed to throw IllegalArgumentException.");
+    	}
+    	catch (Exception e) {
+    		assertTrue( e instanceof IllegalArgumentException);
+    	}
+    }
+    
+    private void fillByteArray(byte[] ba, byte b) {
+        for (int i = 0; i < ba.length; i++) {
+            ba[i] = b;
+        }
+    }
+
+    private boolean checkByteArray(byte[] ba, byte b) {
+        for (int i = 0; i < ba.length; i++) {
+            if (ba[i] != b) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Run all the test cases in this suite. This is to allow running from
+     * {@code org.owasp.esapi.AllTests} which uses a JUnit 3 test runner.
+     */
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(CryptoHelperTest.class);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java
new file mode 100644
index 0000000..f819865
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java
@@ -0,0 +1,401 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.util.Date;
+import java.util.Map;
+
+import javax.crypto.SecretKey;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.ValidationException;
+
+public class CryptoTokenTest {
+    
+    private SecretKey skey1 = null;
+    private SecretKey skey2 = null;
+
+    @Before
+    public void setUp() throws Exception {
+        skey1 = CryptoHelper.generateSecretKey("AES", 128);
+        skey2 = CryptoHelper.generateSecretKey("AES", 128);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public final void testCryptoToken() {
+        // Test with default CTOR
+        CryptoToken ctok = new CryptoToken();
+        CTORtest( ctok, null );
+    }
+
+    @Test
+    public final void testCryptoTokenSecretKey() {
+     // Test with default CTOR
+        CryptoToken ctok = new CryptoToken(skey1);
+        CTORtest( ctok, skey1 );
+    }
+
+    private void CTORtest(CryptoToken ctok, SecretKey sk) {
+        String token = null;
+        try {
+            if ( sk == null ) {
+                token = ctok.getToken();    // Use default key, Encryptor.MasterKey
+            } else {
+                token = ctok.getToken(sk);
+            }
+        } catch (EncryptionException e) {
+            fail("Caught unexpected exception on getToken() call: " + e);
+        }
+        assertNotNull(token);
+        assertEquals( ctok.getUserAccountName(), CryptoToken.ANONYMOUS_USER);
+        assertFalse( ctok.isExpired() );
+        long expTime1 = ctok.getExpiration();
+        
+        CryptoToken ctok2 = null;
+        try {
+            if ( sk == null ) {
+                ctok2 = new CryptoToken( token );    // Use default key, Encryptor.MasterKey
+            } else {
+                ctok2 = new CryptoToken( sk, token );
+            }
+        } catch (EncryptionException e) {
+            e.printStackTrace(System.err);
+            fail("Caught unexpected exception on CryptoToken CTOR: " + e);
+        }
+        long expTime2 = ctok2.getExpiration();
+        assertTrue("Expected expiration for ctok2 (" + new Date(expTime2) +
+                   ") to be later than of ctok (" + new Date(expTime1) + ").",
+                   ( expTime2 >= expTime1 ) );
+    }
+
+    @Test
+    public final void testCryptoTokenSecretKeyString() {
+        CryptoToken ctok1 = new CryptoToken(skey1);
+        try {
+            ctok1.setUserAccountName("kevin.w.wall at gmail.com");
+        } catch (ValidationException e) {
+            fail("Failed to set user account name because of ValidationException: " + e);
+        }
+        try {
+            ctok1.setAttribute("role-name", "admin");
+            ctok1.setAttribute("company", "Qwest");
+        } catch (ValidationException e) {
+            fail("Failed to set 'role-name' or 'company' attribute because of ValidationException: " + e);
+        }
+        String token1 = null;
+        String token2 = null;
+        boolean passedFirst = false;
+        try {
+            token1 = ctok1.getToken();
+            passedFirst = true;
+            token2 = ctok1.getToken(skey2);
+            assertFalse("Tokens unexpectedly equal!", token1.equals(token2) );
+        } catch (EncryptionException e) {
+            fail("Failed to retrieve " + (passedFirst ? "1st" : "2nd" ) + " encrypted token");
+        }
+        CryptoToken ctok2 = null;
+        try {
+            ctok2 = new CryptoToken(skey1, token1);
+            token2 = ctok2.getToken();
+            ctok2.setAttribute("company", "CenturyLink");
+        } catch (EncryptionException e) {
+            fail("Failed to decrypt token1 or re-encrypt token; exception: " + e);
+        } catch (ValidationException e) {
+            fail("Failed with ValidationException on resetting 'company' attribute: " + e);
+        }
+        String userName = ctok2.getUserAccountName();
+        String roleAttr = ctok2.getAttribute("role-name");
+        String company  = ctok2.getAttribute("company");
+        assertEquals( userName, "kevin.w.wall at gmail.com");
+        assertEquals( roleAttr, "admin");
+        assertEquals( company, "CenturyLink");
+    }
+
+    @Test
+    public final void testExpiration() {
+        CryptoToken ctok = new CryptoToken();
+        ctok.setExpiration(2);  // 2 seconds
+        CryptoToken ctok2 = null;
+        try {
+            ctok2 = new CryptoToken( ctok.getToken() );
+        } catch (EncryptionException e1) {
+            fail("Failed to decrypt token");
+        }
+        assertFalse( ctok.isExpired() );
+        assertFalse( ctok2.isExpired() );
+        nap(2);
+
+        assertTrue( ctok.isExpired() );
+        assertTrue( ctok2.isExpired() );
+        
+        try {
+            ctok2.updateToken(2);
+        } catch (EncryptionException e) {
+            fail("EncryptionException for token ctok2 by adding additional 2 sec; exception: " + e);
+        } catch (ValidationException e) {
+            // This would be caused if the token would already be expired even AFTER adding
+            // an additional 2 seconds. We don't expect this, but it could happen if the OS
+            // causes this process to stall for a bit while running higher priority processes.
+            // We don't expect this here though. (Have a test for that below.)
+            fail("Failed to update token ctok2 by adding additional 2 sec; exception: " + e);
+        }
+        assertFalse( ctok2.isExpired() );
+        nap(3);
+        try {
+            ctok2.updateToken(1);
+            fail("Expected ValidationException!");
+        } catch (EncryptionException e) {
+            fail("EncryptionException for token ctok2 by adding additional 2 sec; exception: " + e);
+        } catch (ValidationException e) {
+            // Probably a bad idea to test this in the following manner as
+            // message could change whenever.
+            // assertEquals( e.getMessage(), "Token timed out.");
+            ;   // Ignore -- in this case, we expect it!
+        }
+    }
+
+    @Test
+    public final void testSetUserAccountName() {
+        CryptoToken ctok = new CryptoToken();
+        try {
+            ctok.setUserAccountName("kevin.w.wall at gmail.com");
+            ctok.setUserAccountName("kevin");
+            ctok.setUserAccountName("name-with-hyphen");
+            ctok.setUserAccountName("x");
+            ctok.setUserAccountName("X");
+        } catch (ValidationException e) {
+            fail("Failed to set user account name because of ValidationException: " + e);
+        }
+        try {
+            ctok.setUserAccountName("");    // Can't be empty
+            fail("Failed to throw expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        }
+        try {
+            ctok.setUserAccountName(null);    // Can't be null
+                // Should get one of these, depending on whether or not assertions are enabled.
+            fail("Failed to throw expected AssertionError or NullPointerException");
+        } catch (ValidationException e) {
+            fail("Wrong type of exception thrown (ValidationException): " + e);
+        } catch (NullPointerException e) {
+            ;   // Success
+        } catch (AssertionError e) {
+            ;   // Success
+        }
+        try {
+            ctok.setUserAccountName("1773g4l");    // Can't start w/ numeric
+            fail("Failed to throw expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        }
+        try {
+            ctok.setUserAccountName("invalid/char");    // '/' is not valid.
+            fail("Failed to throw expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        }
+        
+    }
+
+    @Test
+    public final void testSetExpirationDate() {
+        CryptoToken ctok = new CryptoToken();
+        try {
+            ctok.setExpiration(null);
+            fail("Expected IllegalArgumentException on ctok.setExpiration(null).");
+        } catch (IllegalArgumentException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        try {
+            Date now = new Date();
+            nap(1);
+            ctok.setExpiration(now);
+            fail("Expected IllegalArgumentException on ctok.setExpiration(Date) w/ Date in past.");
+        } catch (IllegalArgumentException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        try {
+            ctok.setExpiration(-1);
+            fail("Expected IllegalArgumentException on ctok.setExpiration(int) w/ negative interval.");
+        } catch (IllegalArgumentException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        try {
+            Date maxDate = new Date( Long.MAX_VALUE - 1 );
+            ctok.setExpiration( maxDate );
+            ctok.updateToken(1);
+            fail("Expected ArithmeticException on ctok.setExpiration(int).");
+        } catch (ArithmeticException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }   
+    }
+
+
+    @Test
+    public final void testSetAndGetAttribute() {
+        CryptoToken ctok = new CryptoToken();
+
+        // Test case where attr name is empty string. Expect ValidationException
+        try {
+            ctok.setAttribute("", "someValue");
+            fail("Expected ValidationException on ctok.setAttribute().");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+        
+        // Test case where attr name does not match regex "[A-Za-z0-9_.-]+".
+        // Expect ValidationException.
+        try {
+            ctok.setAttribute("/my/attr/", "someValue");
+            fail("Expected ValidationException on ctok.setAttribute() w/ invalid name.");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+        
+        // Test case where attr VALUE is not. Expect ValidationException.
+        try {
+            ctok.setAttribute("myAttr", null);
+            fail("Expected ValidationException on ctok.setAttribute() w/ null value.");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            fail("Caught unexpected exception: " + e);
+        }
+
+        // Test cases that should work. Specifically we want to test cases
+        // where attribute values contains each of the values that will
+        // be quoted, namely:   '\', '=', and ';'
+        try {
+            String complexValue = "kwwall;1291183520293;abc=x=yx;xyz=;efg=a;a;;bbb=quotes\\tuff";
+            
+            ctok.setAttribute("..--__", ""); // Ugly, but legal attr name; empty is legal value.
+            ctok.setAttribute("attr1", "\\");
+            ctok.setAttribute("attr2", ";");
+            ctok.setAttribute("attr3", "=");
+            ctok.setAttribute("complexAttr", complexValue);
+            String tokenVal = ctok.getToken();
+            assertNotNull("tokenVal should not be null", tokenVal);
+            
+            CryptoToken ctok2 = new CryptoToken(tokenVal);
+            String weirdAttr = ctok2.getAttribute("..--__");
+            assertTrue("Expecting empty string for value of weird attr, but got: " + weirdAttr,
+                       weirdAttr.equals(""));
+
+            String attr1 = ctok2.getAttribute("attr1");
+            assertTrue("attr1 has unexpected value of " + attr1, attr1.equals("\\") );
+
+            String attr2 = ctok2.getAttribute("attr2");
+            assertTrue("attr2 has unexpected value of " + attr2, attr2.equals(";") );
+
+            String attr3 = ctok2.getAttribute("attr3");
+            assertTrue("attr3 has unexpected value of " + attr3, attr3.equals("=") );
+
+            String complexAttr = ctok2.getAttribute("complexAttr");
+            assertNotNull(complexAttr);
+            assertTrue("complexAttr has unexpected value of " + complexAttr, complexAttr.equals(complexValue) );
+
+        } catch (ValidationException e) {
+            fail("Caught unexpected ValidationException: " + e);
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            fail("Caught unexpected exception: " + e);
+        }
+    }
+
+    // Test the following two methods in CryptoToken:
+    // public void addAttributes(final Map<String, String> attrs) throws ValidationException
+    // public Map<String, String> getAttributes()
+    @Test
+    public final void testAddandGetAttributes() {
+        CryptoToken ctok = new CryptoToken();       
+        Map<String, String> origAttrs = null;
+
+        try {
+            ctok.setAttribute("attr1", "value1");
+            ctok.setAttribute("attr2", "value2");
+            origAttrs = ctok.getAttributes();
+            origAttrs.put("attr2", "NewValue2");
+            String val = ctok.getAttribute("attr2");
+            assertTrue("Attribute map not cloned; crypto token attr changed!",
+                       val.equals("value2") );  // Confirm original attr2 did not change
+            
+            origAttrs.put("attr3", "value3");
+            origAttrs.put("attr4", "value4");
+            ctok.addAttributes(origAttrs);
+        } catch (ValidationException e) {
+            fail("Caught unexpected ValidationException: " + e);
+        }
+        try {
+            String token = ctok.getToken();
+            ctok = new CryptoToken(token);
+        } catch (EncryptionException e) {
+            fail("Caught unexpected EncryptionException: " + e);
+        }
+        
+        Map<String, String> extractedAttrs = ctok.getAttributes();
+        assertTrue("Expected extracted attrs to be equal to original attrs",
+                   origAttrs.equals(extractedAttrs));
+                
+        origAttrs.put("/illegalAttrName/", "someValue");
+        try {
+            ctok.addAttributes(origAttrs);
+            fail("Expected ValidationException");
+        } catch (ValidationException e) {
+            ;   // Success
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            fail("Caught unexpected exception: " + e);
+        }
+        
+        origAttrs.clear();
+        CryptoToken ctok2 = null;
+        try {
+            ctok.clearAttributes();     // Clear any attributes
+            ctok2 = new CryptoToken( ctok.getToken() );
+        } catch (EncryptionException e) {
+            fail("Unexpected EncryptionException");
+        }
+        
+        try {
+            ctok2.addAttributes(origAttrs);     // Add (empty) attribute map
+        } catch (ValidationException e) {
+            fail("Unexpected ValidationException");
+        }
+        extractedAttrs = ctok2.getAttributes();
+        assertTrue("Expected extracted attributes to be empty", extractedAttrs.isEmpty() );
+    }
+
+    // Sleep n seconds.
+    private static void nap(int n) {
+        try {
+            System.out.println("Sleeping " + n + " seconds...");
+            Thread.sleep( n * 1000 );
+        } catch (InterruptedException e) {
+            ;   // Ignore
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/ESAPICryptoMACByPassTest.java b/src/test/java/org/owasp/esapi/crypto/ESAPICryptoMACByPassTest.java
new file mode 100644
index 0000000..e54ad33
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/ESAPICryptoMACByPassTest.java
@@ -0,0 +1,231 @@
+/*
+ * OWASP Enterprise Security API (ESAPI) - Google issue # 306.
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2013 - The OWASP Foundation
+ * ESAPI is published by OWASP under the new BSD license. You should read
+ * and accept the LICENSE before you use, modify, and/or redistribute this
+ * software.
+ * 
+ * Full credit for this JUnit to illustrate what is now Google Issue # 306
+ * goes to Philippe Arteau <philippe.arteau at gmail.com>. Originally
+ * published 2013/08/21 to ESAPI-DEV mailing list. Shows that both
+ * ESAPI 2.0 and 2.0.1 is vulnerable. Minor tweaks by Kevin W. Wall.
+ *
+ * Original class name: SignatureByPassTest.
+ * 
+ * NOTE: If this test fails, your version of ESAPI is vulnerable (or you have
+ * 		 it configured not to require a MAC which you should NOT do).
+ */
+
+package org.owasp.esapi.crypto;
+
+import org.apache.commons.codec.binary.Hex;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.util.ByteConversionUtil;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Date;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class ESAPICryptoMACByPassTest {
+
+    @Before
+    public void setUp() throws NoSuchAlgorithmException, InvalidKeySpecException {
+    	; // Do any prerequisite setup here.
+    }
+
+    @Test
+    public void testMacBypass() throws EncryptionException, NoSuchFieldException, IllegalAccessException {
+
+        byte[] bkey = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xA,0x0B,0x0C,0x0D,0x0E,0x0F}; //Truly random key. ;-)
+        SecretKey sk = new SecretKeySpec(bkey,"AES");
+
+        //Encryption with MAC
+        String originalMessage = "Cryptography!?!?";
+        System.out.printf("Encrypting the message '%s'\n", originalMessage);
+        	// Until there is a better way to do this. But this is much easier
+        	// than having to set up a custom ESAPI.properties file just for
+        	// this test.
+        	//
+        	// NOTE: Philippe Arteau's original exploit used "AES/OFB/NoPadding"
+            //       so that one could see that the effect of the decryption would
+        	//		 be "Craptography!?!?". However, if you wish to do that, then
+        	//		 you must also change the ESAPI.properties file used by ESAPI
+        	//		 JUnit tests sp that "OFB" is accepted as an allowed cipher
+        	//		 mode. The easiest way to do that (if you are still not convinced)
+        	//		 is to add "OFB" mode to Encryptor.cipher_modes.additional_allowed;
+        	//		 e.g.,  Encryptor.cipher_modes.additional_allowed=CBC,OFB
+        	//
+        String origCipherXform =
+        	ESAPI.securityConfiguration().setCipherTransformation("AES/CBC/NoPadding");
+        CipherText ct = ESAPI.encryptor().encrypt(sk,new PlainText(originalMessage));
+
+        //Serialize the ciphertext in order to send it over the wire..
+        byte[] serializedCt = ct.asPortableSerializedByteArray();
+
+        //Malicious modification
+        byte[] modifiedCt = tamperCipherText(serializedCt);
+
+        //Decrypt
+        CipherText modifierCtObj = CipherText.fromPortableSerializedBytes(modifiedCt);
+        try {
+        	ESAPI.securityConfiguration().setCipherTransformation(origCipherXform);
+        		// This decryption should fail by throwing an EncryptionException
+        		// if ESAPI crypto is NOT vulnerable and you never get to the
+        		// subsequent lines in the try block.
+            PlainText pt = ESAPI.encryptor().decrypt(sk,modifierCtObj);
+            System.out.printf("Decrypting to '%s' (probably will look like garbage!)\n", new String(pt.asBytes()));
+            System.out.println("This ESAPI version vulnerable to MAC by-pass described in Google issue # 306! Upgrade to latest version.");
+            fail("This ESAPI version is vulnerable to MAC by-pass described in Google issue # 306! Upgrade to latest version.");
+        } catch(EncryptionException eex) {
+        	String errMsg = eex.getMessage();
+        		// See private String DECRYPTION_FAILED in JavaEncryptor.
+        	String expectedError = "Decryption failed; see logs for details.";
+        	assertTrue( errMsg.equals(expectedError) );
+        	System.out.println("testMacByPass(): Attempted decryption after MAC tampering failed.");
+            System.out.println("Fix of issue # 306 successful. Crypto MAC by-pass test failed; exception was: [" + eex + "]");
+        }
+    }
+
+    /**
+     * The modification of the ciphertext is done in a separate method to show that only the generate CipherText object is use.
+     * @param serializeCt
+     */
+    private byte[] tamperCipherText(byte[] serializeCt) throws EncryptionException, NoSuchFieldException, IllegalAccessException {
+        CipherText ct = CipherText.fromPortableSerializedBytes(serializeCt);
+
+        //Ciphertext metadata
+        //System.out.println(ct.toString());
+
+        byte[] cipherTextMod = ct.getRawCipherText();
+
+        System.out.printf("Original ciphertext\t'%s'\n",String.valueOf(Hex.encodeHex(cipherTextMod)));
+
+        cipherTextMod[2] ^= 'y' ^ 'a'; //Alter the 3rd character
+
+        System.out.printf("Modify ciphertext\t'%s'\n",String.valueOf(Hex.encodeHex(cipherTextMod)));
+
+        //MAC ... what MAC ?
+        Field f2 = ct.getClass().getDeclaredField("separate_mac_");
+        f2.setAccessible(true);
+        f2.set(ct,null); //mac byte array set to null
+
+        //Changing CT
+        Field f3 = ct.getClass().getDeclaredField("raw_ciphertext_");
+        f3.setAccessible(true);
+        f3.set(ct,cipherTextMod);
+
+        //return ct.asPortableSerializedByteArray(); //Will complain about missing mac
+        //return new CipherTextSerializer(ct).asSerializedByteArray(); //NPE on mac.length
+        return serialize(ct); //Modify version of CipherTextSerializer.asSerializedByteArray()
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    // The following code is a modified version of CipherTextSerializer
+    /////////////////////////////////////////////////////////////////////
+
+    private byte[] serialize(CipherText cipherText_) {
+        int kdfInfo = cipherText_.getKDFInfo();
+
+        long timestamp = cipherText_.getEncryptionTimestamp();
+        String cipherXform = cipherText_.getCipherTransformation();
+
+        short keySize = (short) cipherText_.getKeySize();
+
+        short blockSize = (short)cipherText_.getBlockSize();
+        byte[] iv = cipherText_.getIV();
+
+        short ivLen = (short)iv.length;
+        byte[] rawCiphertext = cipherText_.getRawCipherText();
+        int ciphertextLen = rawCiphertext.length;
+
+        byte[] mac = cipherText_.getSeparateMAC();
+
+        short macLen = 0;//(short)mac.length; //<-------------- The only modification to the serialization
+
+        return computeSerialization(kdfInfo, timestamp, cipherXform, keySize, blockSize, ivLen, iv, ciphertextLen, rawCiphertext, macLen, mac);
+    }
+
+    private byte[] computeSerialization(int kdfInfo, long timestamp, String cipherXform, short keySize, short blockSize, short ivLen, byte[] iv, int ciphertextLen, byte[] rawCiphertext, short macLen, byte[] mac)
+    {
+        debug("computeSerialization: kdfInfo = " + kdfInfo);
+        debug("computeSerialization: timestamp = " + new Date(timestamp));
+        debug("computeSerialization: cipherXform = " + cipherXform);
+        debug("computeSerialization: keySize = " + keySize);
+        debug("computeSerialization: blockSize = " + blockSize);
+        debug("computeSerialization: ivLen = " + ivLen);
+        debug("computeSerialization: ciphertextLen = " + ciphertextLen);
+        debug("computeSerialization: macLen = " + macLen);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeInt(baos, kdfInfo);
+        writeLong(baos, timestamp);
+        String[] parts = cipherXform.split("/");
+        assert (parts.length == 3) : "Malformed cipher transformation";
+        writeString(baos, cipherXform);
+        writeShort(baos, keySize);
+        writeShort(baos, blockSize);
+        writeShort(baos, ivLen);
+        if (ivLen > 0) baos.write(iv, 0, iv.length);
+        writeInt(baos, ciphertextLen);
+        baos.write(rawCiphertext, 0, rawCiphertext.length);
+        writeShort(baos, macLen);
+        if (macLen > 0) baos.write(mac, 0, mac.length);
+        return baos.toByteArray();
+    }
+
+    private static void debug(String msg) {
+        // System.err.println(msg);
+    }
+
+    private void writeShort(ByteArrayOutputStream baos, short s) {
+        byte[] shortAsByteArray = ByteConversionUtil.fromShort(s);
+        assert (shortAsByteArray.length == 2);
+        baos.write(shortAsByteArray, 0, 2);
+    }
+
+    private void writeInt(ByteArrayOutputStream baos, int i) {
+        byte[] intAsByteArray = ByteConversionUtil.fromInt(i);
+        baos.write(intAsByteArray, 0, 4);
+    }
+
+    private void writeLong(ByteArrayOutputStream baos, long l) {
+        byte[] longAsByteArray = ByteConversionUtil.fromLong(l);
+        assert (longAsByteArray.length == 8);
+        baos.write(longAsByteArray, 0, 8);
+    }
+
+    private void writeString(ByteArrayOutputStream baos, String str)
+    {
+        try
+        {
+            assert ((str != null) && (str.length() > 0));
+            byte[] bytes = str.getBytes("UTF8");
+            assert (bytes.length < 32767) : "writeString: String exceeds max length";
+            writeShort(baos, (short)bytes.length);
+            baos.write(bytes, 0, bytes.length);
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            System.err.println("writeString: " + e.getMessage());
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/crypto/PlainTextTest.java b/src/test/java/org/owasp/esapi/crypto/PlainTextTest.java
new file mode 100644
index 0000000..b7e1af0
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/PlainTextTest.java
@@ -0,0 +1,133 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.Test;
+import org.owasp.esapi.crypto.PlainText;
+
+public class PlainTextTest {
+	
+	private String unicodeStr = "A\u00ea\u00f1\u00fcC";	// I.e., "AêñüC"
+	private String altString  = "AêñüC";				// Same as above.
+	
+	/* NOTE: This test will not work on Windows unless executed under
+	 * Eclipse and the file is stored / treated as a UTF-8 encoded file
+	 * rather than the Windows native OS encoding of Windows-1252 (aka,
+	 * CP-1252). Therefore this test case has a check to not run the test
+	 * unless
+	 *     unicodeStr.equals(altString)
+	 * is true. If not the test is skipped and a message is printed to stderr.
+	 * Jim Manico made an attempt to address this (see private email to
+	 * kevin.w.wall at gmail.com on 11/26/2009, subject "Re: [OWASP-ESAPI] Unit
+	 * Tests Status") to correct this problem by setting some SVN attribute
+	 * to standardize all source files to UTF-8, but not all Subversion clients
+	 * are either honoring this or perhaps Windows just overrides this. Either
+	 * way, this test (which used to be an assertTrue() expression) was
+	 * introduced to account for this.
+	 */
+	@Test
+	public final void testUnicodeString() {
+	    // These 2 strings are *meant* to be equal. If they are not, please
+	    // do *NOT* change the test. It's a Windows thing. Sorry. Change your
+	    // OS instead. ;-)
+	    if ( ! unicodeStr.equals(altString) ) {
+	        System.err.println("Skipping JUnit test case " +
+	                           "PlainTextTest.testUnicodeString() on OS " +
+	                           System.getProperty("os.name") );
+	        return;
+	    }
+		try {
+			byte[] utf8Bytes = unicodeStr.getBytes("UTF-8");
+			PlainText pt1 = new PlainText(unicodeStr);
+			PlainText pt2 = new PlainText(altString);
+			
+			assertTrue( pt1.equals(pt1) );   // Equals self
+			assertFalse( pt1.equals(null) );
+			assertTrue( pt1.equals(pt2) );
+			assertFalse( pt1.equals( unicodeStr ) );
+			assertTrue( pt1.length() == utf8Bytes.length );
+			assertTrue( Arrays.equals(utf8Bytes, pt1.asBytes()) );			
+			assertTrue( pt1.hashCode() == unicodeStr.hashCode() );
+			
+		} catch (UnsupportedEncodingException e) {
+			fail("No UTF-8 byte encoding: " + e);
+			e.printStackTrace(System.err);
+		}
+	}
+	
+	@Test
+	public final void testNullCase() {
+	    int counter = 0;
+	    try {
+            byte[] bytes = null;
+            PlainText pt = new PlainText(bytes);
+            assertTrue( pt != null );   // Should never get to here.
+            fail("testNullCase(): Expected NullPointerException or AssertionError");
+        } catch (NullPointerException e) {
+            // Will get this case if assertions are not enabled for PlainText.
+            // System.err.println("Caught NullPointerException; exception was: " + e);
+            // e.printStackTrace(System.err);
+            counter++;
+        } catch (AssertionError e) {
+            // Will get this case if assertions *are* enabled for PlainText.
+            // System.err.println("Caught AssertionError; exception was: " + e);
+            // e.printStackTrace(System.err);
+            counter++;
+        } finally {
+            assertTrue( counter > 0 );
+        }
+	}
+
+	@Test
+	public final void testEmptyString() {
+		PlainText mt  = new PlainText("");
+		assertTrue( mt.length() == 0 );
+		byte[] ba = mt.asBytes();
+		assertTrue( ba != null && ba.length == 0 );
+	}
+
+	@Test
+	public final void testOverwrite() {
+		try {
+			byte[] origBytes = unicodeStr.getBytes("UTF-8");
+			PlainText pt = new PlainText(origBytes);
+			assertTrue( pt.toString().equals(unicodeStr) );
+			assertTrue( Arrays.equals(origBytes, pt.asBytes()) );			
+			assertTrue( pt.hashCode() == unicodeStr.hashCode() );
+			
+			int origLen = origBytes.length;
+
+			pt.overwrite();
+	        byte[] overwrittenBytes = pt.asBytes();
+			assertTrue( overwrittenBytes != null );
+			assertFalse( Arrays.equals( origBytes, overwrittenBytes ) );
+
+			// Ensure that ALL the bytes overwritten with '*'.
+			int afterLen = overwrittenBytes.length;
+			assertTrue( origLen == afterLen );
+			int sum = 0;
+			for( int i = 0; i < afterLen; i++ ) {
+			    if ( overwrittenBytes[i] == '*' ) {
+			        sum++;
+			    }
+			}
+			assertTrue( afterLen == sum );
+		} catch (UnsupportedEncodingException e) {
+			fail("No UTF-8 byte encoding: " + e);
+			e.printStackTrace(System.err);
+		}
+	}
+
+	/**
+	 * Run all the test cases in this suite.
+	 * This is to allow running from {@code org.owasp.esapi.AllTests} which
+	 * uses a JUnit 3 test runner.
+	 */
+	public static junit.framework.Test suite() {
+		return new JUnit4TestAdapter(PlainTextTest.class);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java b/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java
new file mode 100644
index 0000000..cba2da0
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java
@@ -0,0 +1,139 @@
+package org.owasp.esapi.crypto;
+
+import static org.junit.Assert.*;
+
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * Test for class {@code SecurityProviderLoader}. Note that these tests
+ * use Bouncy Castle's JCE provider so a version their jar must be added
+ * to your class path. If you wish to add it via Maven, you can do so by
+ * adding this to your <b><i>pom.xml</i></b>:
+ * <pre>
+ * <dependency>
+ *      <groupId>org.bouncycastle</groupId>
+ *      <artifactId>bcprov-jdk15</artifactId>
+ *      <version>1.44</version>
+ * </dependency>
+ * </pre>
+ * It has been tested with Bouncy Castle 1.44, but any later version should
+ * do as well.
+ * @author kevin.w.wall at gmail.com
+ */
+public class SecurityProviderLoaderTest {
+
+    private static boolean HAS_BOUNCY_CASTLE = false;
+    
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        try {
+            Class<?> providerClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
+            Provider cryptoProvider = (Provider)providerClass.newInstance();
+            assertTrue( cryptoProvider != null );
+            HAS_BOUNCY_CASTLE = true;
+        } catch(Exception ex) {
+            // Note: FindBugs reports a false positive here...
+            //    REC_CATCH_EXCEPTION: Exception is caught when Exception is not thrown
+            // but exceptions really can be thrown.
+            HAS_BOUNCY_CASTLE = false;
+        }
+    }
+
+    @Test
+    public final void testInsertProviderAt() {
+        if ( ! HAS_BOUNCY_CASTLE ) {
+            System.out.println("SecurityProviderLoaderTest.testInsertProviderAt(): " +
+                               "Skipping test -- must have Bouncy Castle JCE provider in classpath.");
+            return;
+        }
+
+        try {
+            SecurityProviderLoader.insertProviderAt("BC", 1);
+            assertTrue(true);
+        } catch (NoSuchProviderException e) {
+            fail("Caught NoSuchProviderException trying to load Bouncy Castle; exception was: " + e);
+        }
+    }
+
+    @Test
+    public final void testLoadESAPIPreferredJCEProvider() {
+        // Note: OK if empty string or unset, in fact default is empty string.
+        String preferredProvider = ESAPI.securityConfiguration().getPreferredJCEProvider();
+        try {
+            SecurityProviderLoader.loadESAPIPreferredJCEProvider();
+            assertTrue(true);
+        } catch (NoSuchProviderException e) {
+            fail("Caught NoSuchProviderException trying to preferred JCE provider " +
+                 preferredProvider + "; exception was: " + e);
+        }
+    }
+    
+    @Test(expected=NoSuchProviderException.class)
+    public final void testNoSuchProviderException() throws NoSuchProviderException {
+        SecurityProviderLoader.insertProviderAt("DrBobsSecretSnakeOilElixirCryptoJCE", 5);
+    }
+
+    @Test(expected=NoSuchProviderException.class)
+    public final void testBogusProviderWithFQCN() throws NoSuchProviderException {
+        SecurityProviderLoader.insertProviderAt("com.snakeoil.DrBobsSecretSnakeOilElixirCryptoJCE", 5);
+    }
+    
+    @Test
+    public final void testWithBouncyCastle() {
+        if ( ! HAS_BOUNCY_CASTLE ) {
+            System.out.println("SecurityProviderLoaderTest.testInsertProviderAt(): " +
+                               "Skipping test -- must have Bouncy Castle JCE provider in classpath.");
+            return;
+        }
+
+        try {
+            SecurityProviderLoader.insertProviderAt("BC", 1);
+            assertTrue(true);
+        } catch (NoSuchProviderException e) {
+            fail("Caught NoSuchProviderException trying to load Bouncy Castle; exception was: " + e);
+        }
+        
+        // First encrypt w/ preferred cipher transformation (AES/CBC/PKCS5Padding).
+        try {
+            PlainText clearMsg = new PlainText("This is top secret! We are all out of towels!");
+            String origMsg = clearMsg.toString(); // Must keep 'cuz by default, clearMsg is overwritten.
+            CipherText ct = ESAPI.encryptor().encrypt(clearMsg);
+            assertEquals( "*********************************************", clearMsg.toString() );
+            PlainText plain = ESAPI.encryptor().decrypt(ct);
+            assertEquals( origMsg, plain.toString() );
+        } catch (EncryptionException e) {
+            fail("Encryption w/ Bouncy Castle failed with EncryptionException for preferred " +
+                 "cipher transformation; exception was: " + e);
+        }
+        
+        // Next, try a "combined mode" cipher mode available in Bouncy Castle.
+        String origCipherXform = null;
+        try {
+            origCipherXform = ESAPI.securityConfiguration().setCipherTransformation("AES/GCM/NoPadding");
+            PlainText clearMsg = new PlainText("This is top secret! We are all out of towels!");
+            String origMsg = clearMsg.toString(); // Must keep 'cuz by default, clearMsg is overwritten.
+            CipherText ct = ESAPI.encryptor().encrypt(clearMsg);
+            PlainText plain = ESAPI.encryptor().decrypt(ct);
+            assertEquals( origMsg, plain.toString() );
+            // Verify that no MAC is calculated for GCM cipher mode. There is no method to
+            // validate this, so we look at the String representation of this CipherText
+            // object and pick it out of there.
+            String str = ct.toString();
+            assertTrue( str.matches(".*, MAC is absent;.*") );
+        } catch (EncryptionException e) {
+            fail("Encryption w/ Bouncy Castle failed with EncryptionException for preferred " +
+                 "cipher transformation; exception was: " + e);
+        } finally {
+            ESAPI.securityConfiguration().setCipherTransformation(origCipherXform);
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/errors/.svn/all-wcprops b/src/test/java/org/owasp/esapi/errors/.svn/all-wcprops
new file mode 100644
index 0000000..92e8a42
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/errors/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/errors
+END
+EnterpriseSecurityExceptionTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/errors/EnterpriseSecurityExceptionTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/errors/.svn/entries b/src/test/java/org/owasp/esapi/errors/.svn/entries
new file mode 100644
index 0000000..1c02ddb
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/errors/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/errors
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2008-12-21T19:08:07.098552Z
+410
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+EnterpriseSecurityExceptionTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.273953Z
+f5401c87a8449ee2c6d76a0805d82aa9
+2008-12-21T19:08:07.098552Z
+410
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6224
+

diff --git a/src/test/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityExceptionTest.java.svn-base b/src/test/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityExceptionTest.java.svn-base
new file mode 100644
index 0000000..f981a0c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/errors/.svn/text-base/EnterpriseSecurityExceptionTest.java.svn-base
@@ -0,0 +1,157 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationAccountsException;
+import org.owasp.esapi.errors.AuthenticationCredentialsException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.AuthenticationHostException;
+import org.owasp.esapi.errors.AuthenticationLoginException;
+import org.owasp.esapi.errors.AvailabilityException;
+import org.owasp.esapi.errors.CertificateException;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.ExecutorException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationAvailabilityException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.errors.ValidationUploadException;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * The Class AccessReferenceMapTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EnterpriseSecurityExceptionTest extends TestCase {
+    
+    /**
+	 * Instantiates a new access reference map test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public EnterpriseSecurityExceptionTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(EnterpriseSecurityExceptionTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 */
+    public void testExceptions() {
+        System.out.println("exceptions");
+        EnterpriseSecurityException e = null;
+        e = new EnterpriseSecurityException();
+        e = new EnterpriseSecurityException("m1","m2");
+        e = new EnterpriseSecurityException("m1","m2",new Throwable());
+        assertEquals( e.getUserMessage(), "m1" );
+        assertEquals( e.getLogMessage(), "m2" );
+        e = new AccessControlException();
+        e = new AccessControlException("m1","m2");
+        e = new AccessControlException("m1","m2",new Throwable());
+        e = new AuthenticationException();
+        e = new AuthenticationException("m1","m2");
+        e = new AuthenticationException("m1","m2",new Throwable());
+        e = new AvailabilityException();
+        e = new AvailabilityException("m1","m2");
+        e = new AvailabilityException("m1","m2",new Throwable());
+        e = new CertificateException();
+        e = new CertificateException("m1","m2");
+        e = new CertificateException("m1","m2",new Throwable());
+        e = new EncodingException();
+        e = new EncodingException("m1","m2");
+        e = new EncodingException("m1","m2",new Throwable());
+        e = new EncryptionException();
+        e = new EncryptionException("m1","m2");
+        e = new EncryptionException("m1","m2",new Throwable());
+        e = new ExecutorException();
+        e = new ExecutorException("m1","m2");
+        e = new ExecutorException("m1","m2",new Throwable());
+        e = new ValidationException();
+        e = new ValidationException("m1","m2");
+        e = new ValidationException("m1","m2",new Throwable());
+        e = new ValidationException("m1","m2","context");
+        e = new ValidationException("m1","m2",new Throwable(),"context");
+         
+        e = new IntegrityException();
+        e = new IntegrityException("m1","m2");
+        e = new IntegrityException("m1","m2",new Throwable());
+        e = new AuthenticationHostException();
+        e = new AuthenticationHostException("m1","m2");
+        e = new AuthenticationHostException("m1","m2",new Throwable());
+
+        e = new AuthenticationAccountsException();
+        e = new AuthenticationAccountsException("m1","m2");
+        e = new AuthenticationAccountsException("m1","m2",new Throwable());
+        e = new AuthenticationCredentialsException();
+        e = new AuthenticationCredentialsException("m1","m2");
+        e = new AuthenticationCredentialsException("m1","m2",new Throwable());
+        e = new AuthenticationLoginException();
+        e = new AuthenticationLoginException("m1","m2");
+        e = new AuthenticationLoginException("m1","m2",new Throwable());
+        e = new ValidationAvailabilityException();
+        e = new ValidationAvailabilityException("m1","m2");
+        e = new ValidationAvailabilityException("m1","m2",new Throwable());
+        e = new ValidationUploadException();
+        e = new ValidationUploadException("m1","m2");
+        e = new ValidationUploadException("m1","m2",new Throwable());
+
+        ValidationException ve = new ValidationException();
+        ve.setContext("test");
+        assertEquals( "test", ve.getContext() );
+
+        IntrusionException ex = new IntrusionException( "test", "test details");
+        ex = new IntrusionException("m1","m2");
+        ex = new IntrusionException("m1","m2", new Throwable());
+        assertEquals( ex.getUserMessage(), "m1" );
+        assertEquals( ex.getLogMessage(), "m2" );
+    }
+    
+}
diff --git a/src/test/java/org/owasp/esapi/errors/EnterpriseSecurityExceptionTest.java b/src/test/java/org/owasp/esapi/errors/EnterpriseSecurityExceptionTest.java
new file mode 100644
index 0000000..f981a0c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/errors/EnterpriseSecurityExceptionTest.java
@@ -0,0 +1,157 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.errors;
+
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationAccountsException;
+import org.owasp.esapi.errors.AuthenticationCredentialsException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.AuthenticationHostException;
+import org.owasp.esapi.errors.AuthenticationLoginException;
+import org.owasp.esapi.errors.AvailabilityException;
+import org.owasp.esapi.errors.CertificateException;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.ExecutorException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationAvailabilityException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.errors.ValidationUploadException;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * The Class AccessReferenceMapTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EnterpriseSecurityExceptionTest extends TestCase {
+    
+    /**
+	 * Instantiates a new access reference map test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public EnterpriseSecurityExceptionTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(EnterpriseSecurityExceptionTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 */
+    public void testExceptions() {
+        System.out.println("exceptions");
+        EnterpriseSecurityException e = null;
+        e = new EnterpriseSecurityException();
+        e = new EnterpriseSecurityException("m1","m2");
+        e = new EnterpriseSecurityException("m1","m2",new Throwable());
+        assertEquals( e.getUserMessage(), "m1" );
+        assertEquals( e.getLogMessage(), "m2" );
+        e = new AccessControlException();
+        e = new AccessControlException("m1","m2");
+        e = new AccessControlException("m1","m2",new Throwable());
+        e = new AuthenticationException();
+        e = new AuthenticationException("m1","m2");
+        e = new AuthenticationException("m1","m2",new Throwable());
+        e = new AvailabilityException();
+        e = new AvailabilityException("m1","m2");
+        e = new AvailabilityException("m1","m2",new Throwable());
+        e = new CertificateException();
+        e = new CertificateException("m1","m2");
+        e = new CertificateException("m1","m2",new Throwable());
+        e = new EncodingException();
+        e = new EncodingException("m1","m2");
+        e = new EncodingException("m1","m2",new Throwable());
+        e = new EncryptionException();
+        e = new EncryptionException("m1","m2");
+        e = new EncryptionException("m1","m2",new Throwable());
+        e = new ExecutorException();
+        e = new ExecutorException("m1","m2");
+        e = new ExecutorException("m1","m2",new Throwable());
+        e = new ValidationException();
+        e = new ValidationException("m1","m2");
+        e = new ValidationException("m1","m2",new Throwable());
+        e = new ValidationException("m1","m2","context");
+        e = new ValidationException("m1","m2",new Throwable(),"context");
+         
+        e = new IntegrityException();
+        e = new IntegrityException("m1","m2");
+        e = new IntegrityException("m1","m2",new Throwable());
+        e = new AuthenticationHostException();
+        e = new AuthenticationHostException("m1","m2");
+        e = new AuthenticationHostException("m1","m2",new Throwable());
+
+        e = new AuthenticationAccountsException();
+        e = new AuthenticationAccountsException("m1","m2");
+        e = new AuthenticationAccountsException("m1","m2",new Throwable());
+        e = new AuthenticationCredentialsException();
+        e = new AuthenticationCredentialsException("m1","m2");
+        e = new AuthenticationCredentialsException("m1","m2",new Throwable());
+        e = new AuthenticationLoginException();
+        e = new AuthenticationLoginException("m1","m2");
+        e = new AuthenticationLoginException("m1","m2",new Throwable());
+        e = new ValidationAvailabilityException();
+        e = new ValidationAvailabilityException("m1","m2");
+        e = new ValidationAvailabilityException("m1","m2",new Throwable());
+        e = new ValidationUploadException();
+        e = new ValidationUploadException("m1","m2");
+        e = new ValidationUploadException("m1","m2",new Throwable());
+
+        ValidationException ve = new ValidationException();
+        ve.setContext("test");
+        assertEquals( "test", ve.getContext() );
+
+        IntrusionException ex = new IntrusionException( "test", "test details");
+        ex = new IntrusionException("m1","m2");
+        ex = new IntrusionException("m1","m2", new Throwable());
+        assertEquals( ex.getUserMessage(), "m1" );
+        assertEquals( ex.getLogMessage(), "m2" );
+    }
+    
+}
diff --git a/src/test/java/org/owasp/esapi/filters/.svn/all-wcprops b/src/test/java/org/owasp/esapi/filters/.svn/all-wcprops
new file mode 100644
index 0000000..2470f5c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/filters/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/filters
+END
+ClickjackFilterTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/filters/ClickjackFilterTest.java
+END
+SafeRequestTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/filters/.svn/entries b/src/test/java/org/owasp/esapi/filters/.svn/entries
new file mode 100644
index 0000000..7098565
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/filters/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/filters
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-05-16T21:25:27.196865Z
+1417
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+ClickjackFilterTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.269952Z
+356cf8034851382f3f0f1333ceb36851
+2009-05-10T04:33:18.539487Z
+510
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3023
+

+SafeRequestTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.269952Z
+bd046c55140f790049badbf76b9db451
+2010-05-16T21:25:27.196865Z
+1417
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6386
+

diff --git a/src/test/java/org/owasp/esapi/filters/.svn/text-base/ClickjackFilterTest.java.svn-base b/src/test/java/org/owasp/esapi/filters/.svn/text-base/ClickjackFilterTest.java.svn-base
new file mode 100644
index 0000000..a14b801
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/filters/.svn/text-base/ClickjackFilterTest.java.svn-base
@@ -0,0 +1,106 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.FilterConfig;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockFilterConfig;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class ClickjackFilterTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ClickjackFilterTest extends TestCase {
+    
+    /**
+	 * @param testName
+	 *            the test name
+	 */
+    public ClickjackFilterTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(ClickjackFilterTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+     * @throws Exception
+     */
+    public void testFilter() throws Exception {
+        System.out.println("ClickjackFilter");
+
+        Map map = new HashMap();
+    	FilterConfig mfc = new MockFilterConfig( map );
+    	ClickjackFilter filter = new ClickjackFilter();        
+    	filter.init( mfc );
+   	    MockHttpServletRequest request = new MockHttpServletRequest();
+		
+		// the mock filter chain writes the requested URI to the response body
+		MockFilterChain chain = new MockFilterChain();
+
+        URL url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "\nTest request: " + url );
+        request = new MockHttpServletRequest( url );
+    	MockHttpServletResponse response = new MockHttpServletResponse();
+    	try {
+        	filter.doFilter(request, response, chain);
+        } catch( Exception e ) {
+        	e.printStackTrace();
+        	fail();
+        }
+        String header = response.getHeader( "X-FRAME-OPTIONS");
+        System.out.println(">>>" + header );
+        assertEquals( "DENY", header );
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/filters/.svn/text-base/SafeRequestTest.java.svn-base b/src/test/java/org/owasp/esapi/filters/.svn/text-base/SafeRequestTest.java.svn-base
new file mode 100644
index 0000000..2864d6b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/filters/.svn/text-base/SafeRequestTest.java.svn-base
@@ -0,0 +1,203 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+
+/**
+ * The Class SafeRequestTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class SafeRequestTest extends TestCase {
+
+	/**
+	 * Instantiates a new access controller test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 * @throws Exception
+	 */
+	public SafeRequestTest(String testName) throws Exception {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws Exception
+	 */
+	protected void setUp() throws Exception {
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws Exception
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SafeRequestTest.class);
+		return suite;
+	}
+
+	/**
+	 *
+	 */
+	public void testGetRequestParameters() {
+		System.out.println( "getRequestParameters");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter( "one","1" );
+		request.addParameter( "two","2" );
+		request.addParameter( "one","3" );
+		request.addParameter( "one","4" );
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest( request );
+		String[] params = safeRequest.getParameterValues("one");
+		String out = "";
+		for (int i = 0; i < params.length; i++ ) out += params[i];
+		assertEquals( "134", out );
+	}
+
+	public void testGetQueryStringNull()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString(null);
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertNull(wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringNonNull()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=b");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=b",wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringNUL()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=\u0000");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("",wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringPercent()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%62");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=b",wrappedReq.getQueryString());
+	}
+	
+	public void testGetQueryStringPercentNUL()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%00");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("",wrappedReq.getQueryString());
+	}
+
+	/* these tests need to be enabled&changed based on the decisions
+	 * made regarding issue 125. Currently they fail.
+	public void testGetQueryStringPercentEquals()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%3d");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=%3d",wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringPercentAmpersand()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%26b");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=%26b",wrappedReq.getQueryString());
+	}
+	*/
+
+	// Test to ensure null-value contract defined by ServletRequest.getParameterNames(String) is met.
+	public void testGetParameterValuesReturnsNullWhenParameterDoesNotExistInRequest() {
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.clearParameters();
+ 
+		final String paramName = "nonExistentParameter";
+		assertNull(request.getParameterValues(paramName));
+
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+		assertNull("Expecting null value to be returned for non-existent parameter.", safeRequest.getParameterValues(paramName));
+	}
+
+	public void testGetParameterValuesReturnsCorrectValueWhenParameterExistsInRequest() {
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.clearParameters();
+
+		final String paramName = "existentParameter";
+		final String paramValue = "foobar";
+		request.addParameter(paramName, paramValue);
+		assertTrue(request.getParameterValues(paramName)[0].equals(paramValue));
+
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+		final String actualParamValue = safeRequest.getParameterValues(paramName)[0];
+		assertEquals(paramValue, actualParamValue);
+	}
+
+	public void testGetParameterValuesReturnsCorrectValuesWhenParameterExistsMultipleTimesInRequest() {
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.clearParameters();
+
+		final String paramName = "existentParameter";
+		final String paramValue_0 = "foobar";
+		final String paramValue_1 = "barfoo";
+		request.addParameter(paramName, paramValue_0);
+		request.addParameter(paramName, paramValue_1);
+		assertTrue(request.getParameterValues(paramName)[0].equals(paramValue_0));
+		assertTrue(request.getParameterValues(paramName)[1].equals(paramValue_1));
+
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+		final String[] actualParamValues = safeRequest.getParameterValues(paramName);
+		assertEquals(paramValue_0, actualParamValues[0]);
+		assertEquals(paramValue_1, actualParamValues[1]);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/filters/ClickjackFilterTest.java b/src/test/java/org/owasp/esapi/filters/ClickjackFilterTest.java
new file mode 100644
index 0000000..a14b801
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/filters/ClickjackFilterTest.java
@@ -0,0 +1,106 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.FilterConfig;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockFilterConfig;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class ClickjackFilterTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ClickjackFilterTest extends TestCase {
+    
+    /**
+	 * @param testName
+	 *            the test name
+	 */
+    public ClickjackFilterTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(ClickjackFilterTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+     * @throws Exception
+     */
+    public void testFilter() throws Exception {
+        System.out.println("ClickjackFilter");
+
+        Map map = new HashMap();
+    	FilterConfig mfc = new MockFilterConfig( map );
+    	ClickjackFilter filter = new ClickjackFilter();        
+    	filter.init( mfc );
+   	    MockHttpServletRequest request = new MockHttpServletRequest();
+		
+		// the mock filter chain writes the requested URI to the response body
+		MockFilterChain chain = new MockFilterChain();
+
+        URL url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "\nTest request: " + url );
+        request = new MockHttpServletRequest( url );
+    	MockHttpServletResponse response = new MockHttpServletResponse();
+    	try {
+        	filter.doFilter(request, response, chain);
+        } catch( Exception e ) {
+        	e.printStackTrace();
+        	fail();
+        }
+        String header = response.getHeader( "X-FRAME-OPTIONS");
+        System.out.println(">>>" + header );
+        assertEquals( "DENY", header );
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java
new file mode 100644
index 0000000..2864d6b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java
@@ -0,0 +1,203 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.filters;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+
+/**
+ * The Class SafeRequestTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class SafeRequestTest extends TestCase {
+
+	/**
+	 * Instantiates a new access controller test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 * @throws Exception
+	 */
+	public SafeRequestTest(String testName) throws Exception {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws Exception
+	 */
+	protected void setUp() throws Exception {
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @throws Exception
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SafeRequestTest.class);
+		return suite;
+	}
+
+	/**
+	 *
+	 */
+	public void testGetRequestParameters() {
+		System.out.println( "getRequestParameters");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter( "one","1" );
+		request.addParameter( "two","2" );
+		request.addParameter( "one","3" );
+		request.addParameter( "one","4" );
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest( request );
+		String[] params = safeRequest.getParameterValues("one");
+		String out = "";
+		for (int i = 0; i < params.length; i++ ) out += params[i];
+		assertEquals( "134", out );
+	}
+
+	public void testGetQueryStringNull()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString(null);
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertNull(wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringNonNull()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=b");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=b",wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringNUL()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=\u0000");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("",wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringPercent()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%62");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=b",wrappedReq.getQueryString());
+	}
+	
+	public void testGetQueryStringPercentNUL()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%00");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("",wrappedReq.getQueryString());
+	}
+
+	/* these tests need to be enabled&changed based on the decisions
+	 * made regarding issue 125. Currently they fail.
+	public void testGetQueryStringPercentEquals()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%3d");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=%3d",wrappedReq.getQueryString());
+	}
+
+	public void testGetQueryStringPercentAmpersand()
+	{
+		MockHttpServletRequest req = new MockHttpServletRequest();
+		SecurityWrapperRequest wrappedReq;
+
+		req.setQueryString("a=%26b");
+		wrappedReq = new SecurityWrapperRequest(req);
+		assertEquals("a=%26b",wrappedReq.getQueryString());
+	}
+	*/
+
+	// Test to ensure null-value contract defined by ServletRequest.getParameterNames(String) is met.
+	public void testGetParameterValuesReturnsNullWhenParameterDoesNotExistInRequest() {
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.clearParameters();
+ 
+		final String paramName = "nonExistentParameter";
+		assertNull(request.getParameterValues(paramName));
+
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+		assertNull("Expecting null value to be returned for non-existent parameter.", safeRequest.getParameterValues(paramName));
+	}
+
+	public void testGetParameterValuesReturnsCorrectValueWhenParameterExistsInRequest() {
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.clearParameters();
+
+		final String paramName = "existentParameter";
+		final String paramValue = "foobar";
+		request.addParameter(paramName, paramValue);
+		assertTrue(request.getParameterValues(paramName)[0].equals(paramValue));
+
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+		final String actualParamValue = safeRequest.getParameterValues(paramName)[0];
+		assertEquals(paramValue, actualParamValue);
+	}
+
+	public void testGetParameterValuesReturnsCorrectValuesWhenParameterExistsMultipleTimesInRequest() {
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.clearParameters();
+
+		final String paramName = "existentParameter";
+		final String paramValue_0 = "foobar";
+		final String paramValue_1 = "barfoo";
+		request.addParameter(paramName, paramValue_0);
+		request.addParameter(paramName, paramValue_1);
+		assertTrue(request.getParameterValues(paramName)[0].equals(paramValue_0));
+		assertTrue(request.getParameterValues(paramName)[1].equals(paramValue_1));
+
+		SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+		final String[] actualParamValues = safeRequest.getParameterValues(paramName);
+		assertEquals(paramValue_0, actualParamValues[0]);
+		assertEquals(paramValue_1, actualParamValues[1]);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/http/.svn/all-wcprops b/src/test/java/org/owasp/esapi/http/.svn/all-wcprops
new file mode 100644
index 0000000..d0a8de5
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/all-wcprops
@@ -0,0 +1,59 @@
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http
+END
+MockHttpSession.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockHttpSession.java
+END
+MockHttpServletResponse.java
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java
+END
+package.html
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/package.html
+END
+MockFilterChain.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockFilterChain.java
+END
+MockServletContext.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockServletContext.java
+END
+MockFilterConfig.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockFilterConfig.java
+END
+MockHttpServletRequest.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java
+END
+MockRequestDispatcher.java
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockRequestDispatcher.java
+END
+MockServletInputStream.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http/MockServletInputStream.java
+END
diff --git a/src/test/java/org/owasp/esapi/http/.svn/entries b/src/test/java/org/owasp/esapi/http/.svn/entries
new file mode 100644
index 0000000..3c45fcb
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/entries
@@ -0,0 +1,334 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/http
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-05-16T01:50:26.609030Z
+1416
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+MockRequestDispatcher.java
+file
+
+
+
+
+2014-02-18T16:19:52.173951Z
+ad040192598c5ae0182c866a58508c9b
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1598
+

+MockServletInputStream.java
+file
+
+
+
+
+2014-02-18T16:19:52.177951Z
+3595e365cf8878ab554ddac8f7079e1d
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1278
+

+MockHttpSession.java
+file
+
+
+
+
+2014-02-18T16:19:52.177951Z
+80e6799ef18ab26d9bc77ddce8a10d0c
+2009-11-14T04:14:22.993242Z
+831
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5049
+

+MockHttpServletResponse.java
+file
+
+
+
+
+2014-02-18T16:19:52.177951Z
+936651ce04506e87b717eb054acf529d
+2009-11-14T04:14:22.993242Z
+831
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7980
+

+package.html
+file
+
+
+
+
+2014-02-18T16:19:52.177951Z
+948478e8bf83c430188f4cc61084926b
+2008-12-05T07:15:24.217879Z
+387
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+407
+

+MockFilterChain.java
+file
+
+
+
+
+2014-02-18T16:19:52.173951Z
+4814379fcd00dc0179381e9d967ae189
+2009-10-27T04:19:35.471220Z
+733
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1701
+

+MockServletContext.java
+file
+
+
+
+
+2014-02-18T16:19:52.173951Z
+ef96b30c7109d4f749a5324b550221b6
+2009-05-01T13:53:25.899723Z
+485
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+21684
+

+MockFilterConfig.java
+file
+
+
+
+
+2014-02-18T16:19:52.173951Z
+5b28fb866f741f949d524793e5592a26
+2009-05-01T13:53:25.899723Z
+485
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2027
+

+MockHttpServletRequest.java
+file
+
+
+
+
+2014-02-18T16:19:52.173951Z
+643e8ae75e1f30f50860709e258dc3bd
+2010-05-16T01:50:26.609030Z
+1416
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+14045
+

diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockFilterChain.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockFilterChain.java.svn-base
new file mode 100644
index 0000000..7bd05ca
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockFilterChain.java.svn-base
@@ -0,0 +1,46 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class MockFilterChain implements FilterChain {
+    /**
+     *
+     * @param request
+     * @param response
+     * @throws java.io.IOException
+     * @throws javax.servlet.ServletException
+     */
+    public void doFilter( ServletRequest request, ServletResponse response ) throws IOException, ServletException {
+    	System.out.println( "CHAIN received " + request.getClass().getName() + " and is issuing " + response.getClass().getName() );
+       	response.getOutputStream().println( "   This is the body of a response for " +  ((HttpServletRequest)request).getRequestURI() );
+           	((HttpServletResponse)response).addCookie( new Cookie( "name", "value" ) );
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockFilterConfig.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockFilterConfig.java.svn-base
new file mode 100644
index 0000000..6d5a3bf
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockFilterConfig.java.svn-base
@@ -0,0 +1,66 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+
+/**
+ * A filter configuration object used by a servlet container
+ * to pass information to a filter during initialization.
+ */
+public class MockFilterConfig implements FilterConfig {
+	private Map map;
+
+	public MockFilterConfig( Map map ) {
+		this.map = map;
+	}
+	
+    public String getFilterName() {
+    	return "mock";
+    }
+
+    /**
+     * Returns a reference to the {@link ServletContext} in which the caller
+     * is executing.
+     */
+    public ServletContext getServletContext() {
+    	return new MockServletContext();
+    }
+
+    /**
+     * Returns a <code>String</code> containing the value of the
+     * named initialization parameter, or <code>null</code> if
+     * the parameter does not exist.
+     */
+    public String getInitParameter(String name) {
+    	return (String)map.get( name );
+    }
+
+    /**
+     * Returns the names of the filter's initialization parameters
+     * as an <code>Enumeration</code> of <code>String</code> objects,
+     * or an empty <code>Enumeration</code> if the filter has
+     * no initialization parameters.
+     */
+    public Enumeration getInitParameterNames() {
+    	return Collections.enumeration( map.keySet() );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpServletRequest.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpServletRequest.java.svn-base
new file mode 100644
index 0000000..87cb92f
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpServletRequest.java.svn-base
@@ -0,0 +1,782 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.security.Principal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * The Class MockHttpServletRequest.
+ * 
+ * @author jwilliams
+ */
+public class MockHttpServletRequest implements HttpServletRequest
+{
+	private static final String HDR_CONTENT_TYPE = "Content-Type";
+	private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+	/** The requestDispatcher */
+	private RequestDispatcher requestDispatcher = new MockRequestDispatcher();
+
+	/** The session. */
+	private MockHttpSession session = null;
+
+	/** The cookies. */
+	private ArrayList cookies = new ArrayList();
+
+	/** The parameters. */
+	private Map<String,String[]> parameters = new HashMap<String,String[]>();
+
+	/** The headers. */
+	private Map<String,List<String>> headers = new HashMap<String,List<String>>();
+
+	private byte[] body;
+
+	private String scheme = "https";
+
+	private String remoteHost = "64.14.103.52";
+
+	private String serverHost = "64.14.103.52";
+
+	private String uri = "/test";
+
+	private String url = "https://www.example.com" + uri;
+
+	private String queryString = "pid=1&qid=test";
+
+	private String method = "POST";
+
+	private Map<String,Object> attrs = new HashMap<String,Object>();
+
+	/**
+	 *
+	 */
+	public MockHttpServletRequest() {
+	}
+
+	/**
+	 *
+	 * @param uri
+	 * @param body
+	 */
+	public MockHttpServletRequest(String uri, byte[] body) {
+		this.body = body;
+		this.uri = uri;
+	}
+
+	public MockHttpServletRequest( URL url ) {
+		scheme = url.getProtocol();
+		serverHost = url.getHost();
+		uri = url.getPath();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getAuthType() {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getContextPath() {
+		return null;
+	}
+
+	/**
+	 * Adds the parameter.
+	 * 
+	 * @param name the name
+	 * @param value the value
+	 */
+	public void addParameter(String name, String value) {
+		String[] old = parameters.get(name);
+		if ( old == null ) {
+			old = new String[0];
+		}
+		String[] updated = new String[old.length + 1];
+		for ( int i = 0; i < old.length; i++ ) updated[i] = old[i];
+		updated[old.length] = value;
+		parameters.put(name, updated);
+	}
+
+	/**
+	 * removeParameter removes the parameter name from the parameters map if it exists
+	 *  
+	 * @param name
+	 * 			parameter name to be removed
+	 */
+	public void removeParameter( String name ) {
+		parameters.remove( name );
+	}
+
+	/**
+	 * Adds the header.
+	 * 
+	 * @param name the name
+	 * @param value the value
+	 */
+	public void addHeader(String name, String value)
+	{
+		List<String> values;
+
+		if((values = headers.get(name))==null)
+		{
+			values = new ArrayList<String>();
+			headers.put(name, values);
+		}
+		values.add(value);
+	}
+
+	/**
+	 * Set a header replacing any previous value(s).
+	 * @param name the header name
+	 * @param value the header value
+	 */
+	public void setHeader(String name, String value)
+	{
+		List<String> values = new ArrayList<String>();
+
+		values.add(value);
+		headers.put(name,values);
+	}
+
+	/**
+	 * Sets the cookies.
+	 * 
+	 * @param list the new cookies
+	 */
+	public void setCookies(ArrayList list) {
+		cookies = list;
+	}
+
+	/**
+	 *
+	 * @param name
+	 * @param value
+	 */
+	public void setCookie(String name, String value ) {
+		Cookie c = new Cookie( name, value );
+		cookies.add( c );
+	}
+
+	public boolean clearCookie(String name) {
+		return cookies.remove(name);
+	}
+
+	/**
+	 * @return 
+	 *
+	 */
+	public void clearCookies() {
+		cookies.clear();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Cookie[] getCookies() {
+		if ( cookies.isEmpty() ) return null;
+		return (Cookie[]) cookies.toArray(new Cookie[0]);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @return
+	 */
+	public long getDateHeader(String name) {
+		try {
+			Date date = SimpleDateFormat.getDateTimeInstance().parse( getParameter( name ) );
+			return date.getTime(); // TODO needs to be HTTP format
+		} catch( ParseException e ) {
+			return 0;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @return
+	 */
+	public String getHeader(String name) {
+		List<String> values;
+
+		if((values = headers.get(name))==null)
+			return null;
+		if(values.size() == 0)
+			return null;
+		return values.get(0);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return Enumeration of header names as strings
+	 */
+	public Enumeration getHeaderNames()
+	{
+		return Collections.enumeration(headers.keySet());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public Enumeration getHeaders(String name) {
+		Vector v = new Vector();
+		v.add( getHeader( name ) );
+		return v.elements();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @return
+	 */
+	public int getIntHeader(String name) {
+
+		return 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getMethod() {
+		return method;
+	}
+
+	/**
+	 *
+	 * @param value
+	 */
+	public void setMethod( String value ) {
+		method = value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getPathInfo() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getPathTranslated() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getQueryString() {
+		return queryString;
+	}
+
+	/**
+	 * Set the query string to return.
+	 * @param str The query string to return.
+	 */
+	public void setQueryString(String str)
+	{
+		this.queryString = str;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRemoteUser() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRequestURI() {
+		return uri;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public StringBuffer getRequestURL() {
+		return new StringBuffer( getScheme() + "://" + this.getServerName() + getRequestURI() + "?" + getQueryString() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRequestedSessionId() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getServletPath() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public HttpSession getSession() {
+		if (session != null) {
+			return getSession(false);
+		}
+		return getSession(true);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param create 
+	 * @return
+	 */
+	public HttpSession getSession(boolean create) {
+		if (session == null && create) {
+			session = new MockHttpSession();
+		} else if (session != null && session.getInvalidated()) {
+			session = new MockHttpSession();
+		}
+		return session;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Principal getUserPrincipal() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isRequestedSessionIdFromCookie() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isRequestedSessionIdFromURL() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public boolean isRequestedSessionIdFromUrl() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isRequestedSessionIdValid() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param role 
+	 * @return
+	 */
+	public boolean isUserInRole(String role) {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public Object getAttribute(String name) {
+		return attrs.get(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Enumeration getAttributeNames() {
+		return Collections.enumeration(attrs.keySet());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getCharacterEncoding() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getContentLength() {
+		return body.length;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getContentType() {
+		return getHeader(HDR_CONTENT_TYPE);
+	}
+
+	/**
+	 *
+	 * @param value
+	 */
+	public void setContentType( String value ) {
+		setHeader(HDR_CONTENT_TYPE, value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 * @throws IOException
+	 */
+	public ServletInputStream getInputStream() throws IOException {
+		return new MockServletInputStream(body);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getLocalAddr() {
+		return "10.1.43.6";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getLocalName() {
+		return "www.domain.com";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getLocalPort() {
+		return 80;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Locale getLocale() {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Enumeration getLocales() {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public String getParameter(String name) {
+		String[] values = parameters.get(name);
+		if ( values == null ) return null;
+		return values[0];
+	}
+
+	public void clearParameter(String name) {
+		parameters.remove( name );
+	}
+
+	public void clearParameters() {
+		parameters.clear();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Map getParameterMap() {
+		return parameters;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Enumeration getParameterNames() {
+		return Collections.enumeration(parameters.keySet());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public String[] getParameterValues(String name) {
+		return parameters.get(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getProtocol() {
+		return "HTTP/1.1";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return 
+	 * @throws IOException
+	 */
+	public BufferedReader getReader() throws IOException {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param path
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public String getRealPath(String path) {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRemoteAddr() {
+		return remoteHost;
+	}
+
+	public void setRemoteAddr(String remoteHost) {
+		this.remoteHost = remoteHost;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRemoteHost() {
+		return remoteHost;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getRemotePort() {
+
+		return 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param path
+	 * @return
+	 */
+	public RequestDispatcher getRequestDispatcher(String path) {
+		return requestDispatcher;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getScheme() {
+		return scheme;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getServerName() {
+		return serverHost;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getServerPort() {
+
+		return 80;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isSecure() {
+		return scheme.equals( "https" );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 */
+	public void removeAttribute(String name) {
+		attrs.remove(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @param o
+	 */
+	public void setAttribute(String name, Object o) {
+		attrs.put(name,o);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param env
+	 * @throws UnsupportedEncodingException
+	 */
+	public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+
+	}
+
+	/**
+	 *
+	 * @param uri
+	 * @throws java.io.UnsupportedEncodingException
+	 */
+	public void setRequestURI(String uri) throws UnsupportedEncodingException {
+		this.uri = uri;
+	}
+
+	/**
+	 *
+	 * @param url
+	 * @throws java.io.UnsupportedEncodingException
+	 */
+	public void setRequestURL(String url) throws UnsupportedEncodingException {
+		// get the scheme
+		int p = url.indexOf( ":" );
+		this.scheme = url.substring( 0, p );
+
+		// get the queryString
+		int q = url.indexOf( "?" );
+		if ( q != -1 )
+		{
+			queryString = url.substring( q+1 );
+			url = url.substring( 0, q );
+		}
+		else
+			queryString = null;
+		this.url = url;
+	}
+
+	public void setScheme( String scheme ) {
+		this.scheme = scheme;
+	}
+
+	public void dump()
+	{
+		String[] names;
+
+		System.out.println();
+		System.out.println( "  " + this.getMethod() + " " + this.getRequestURL() );
+
+		names = headers.keySet().toArray(EMPTY_STRING_ARRAY);
+		Arrays.sort(names);	// make debugging a bit easier...
+		for (String name : names)
+			for(String value : headers.get(name))
+				System.out.println( "  " + name + ": " + value);
+		names = parameters.keySet().toArray(EMPTY_STRING_ARRAY);
+		Arrays.sort(names);
+		for (String name : names) {
+			for(String value : parameters.get(name))
+				System.out.println( "  " + name + "=" + value);
+		}
+		System.out.println( "\n" );
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpServletResponse.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpServletResponse.java.svn-base
new file mode 100644
index 0000000..43b4a7e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpServletResponse.java.svn-base
@@ -0,0 +1,463 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+
+/**
+ * The Class MockHttpServletResponse.
+ * 
+ * @author jwilliams
+ */
+public class MockHttpServletResponse implements HttpServletResponse {
+
+	/** The cookies. */
+	List cookies = new ArrayList();
+
+	/** The header names. */
+	List headerNames = new ArrayList();
+
+	/** The header values. */
+	List headerValues = new ArrayList();
+
+	/** The status. */
+	int status = 200;
+
+	StringBuffer body = new StringBuffer();
+
+	String contentType = "text/html; charset=ISO-8895-1";
+
+	public String getBody() {
+		return body.toString();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param cookie
+	 */
+	public void addCookie(Cookie cookie) {
+		cookies.add(cookie);
+	}
+
+	/**
+	 * Gets the cookies.
+	 * 
+	 * @return the cookies
+	 */
+	public List getCookies() {
+		return cookies;
+	}
+
+	/**
+	 * 
+	 * @param name
+	 * @return
+	 */
+	public Cookie getCookie(String name) {
+		Iterator i = cookies.iterator();
+		while (i.hasNext()) {
+			Cookie c = (Cookie) i.next();
+			if (c.getName().equals(name)) {
+				return c;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param date
+	 */
+	public void addDateHeader(String name, long date) {
+		headerNames.add(name);
+		headerValues.add("" + date);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void addHeader(String name, String value) {
+		headerNames.add(name);
+		headerValues.add(value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void addIntHeader(String name, int value) {
+		headerNames.add(name);
+		headerValues.add("" + value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @return
+	 */
+	public boolean containsHeader(String name) {
+		return headerNames.contains(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	/**
+	 * Gets the header.
+	 * 
+	 * @param name
+	 *            the name
+	 * 
+	 * @return the header
+	 */
+	public String getHeader(String name) {
+		int index = headerNames.indexOf(name);
+		if (index != -1) {
+			return (String) headerValues.get(index);
+		}
+		return null;
+	}
+
+	/**
+	 * Gets the header names.
+	 * 
+	 * @return the header names
+	 */
+	public List getHeaderNames() {
+		return headerNames;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 */
+	public String encodeRedirectURL(String url) {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public String encodeRedirectUrl(String url) {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 */
+	public String encodeURL(String url) {
+		String enc = url;
+		try { enc = ESAPI.encoder().encodeForURL(url);
+		} catch( Exception e ) {}
+		return enc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public String encodeUrl(String url) {
+		return encodeURL( url );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 * @throws IOException
+	 */
+	public void sendError(int sc) throws IOException {
+		status = sc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 * @param msg
+	 * @throws IOException
+	 */
+	public void sendError(int sc, String msg) throws IOException {
+		status = sc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param location
+	 * @throws IOException
+	 */
+	public void sendRedirect(String location) throws IOException {
+		status = HttpServletResponse.SC_MOVED_PERMANENTLY;
+		body = new StringBuffer( "Redirect to " + location );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param date
+	 */
+	public void setDateHeader(String name, long date) {
+		headerNames.add(name);
+		headerValues.add("" + date);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void setHeader(String name, String value) {
+		headerNames.add(name);
+		headerValues.add(value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void setIntHeader(String name, int value) {
+		headerNames.add(name);
+		headerValues.add("" + value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 */
+	public void setStatus(int sc) {
+		status = sc;
+	}
+
+	/**
+	 * Gets the status.
+	 * 
+	 * @return the status
+	 */
+	public int getStatus() {
+		return status;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 * @param sm
+	 * @deprecated
+	 */
+	@Deprecated
+	public void setStatus(int sc, String sm) {
+		status = sc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @throws IOException
+	 */
+	public void flushBuffer() throws IOException {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public int getBufferSize() {
+		return body.length();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public String getCharacterEncoding() {
+		return "UTF-8";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public String getContentType() {
+		return contentType;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public Locale getLocale() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 * @throws IOException
+	 */
+	public ServletOutputStream getOutputStream() throws IOException {
+		return new ServletOutputStream() {
+			public void write(int b) throws IOException {
+				body.append((char)b);
+			}
+		};
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 * @throws IOException
+	 */
+	public PrintWriter getWriter() throws IOException {
+		return new PrintWriter( getOutputStream(), true );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public boolean isCommitted() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void reset() {
+		body = new StringBuffer();
+		cookies = new ArrayList();
+		headerNames = new ArrayList();
+		headerValues = new ArrayList();
+		status = 200;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void resetBuffer() {
+		body = new StringBuffer();
+	}
+
+	public void setBody( String value ) {
+		body = new StringBuffer( value );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param size
+	 */
+	public void setBufferSize(int size) {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param charset
+	 */
+	public void setCharacterEncoding(String charset) {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param len
+	 */
+	public void setContentLength(int len) {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param type
+	 */
+	public void setContentType(String type) {
+		contentType = type;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param loc
+	 */
+	public void setLocale(Locale loc) {
+
+	}
+
+	/*
+	 * Dump the response in a semi-readable format close to a real HTTP response on the wire
+	 */
+	public void dump() {
+        System.out.println();
+		System.out.println( "  " + this.getStatus() + " " );
+        for ( Object name : getHeaderNames() ) System.out.println( "  " + name + "=" + getHeader( (String)name ) );
+        System.out.println( "  BODY: " + this.getBody() );
+        System.out.println();
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpSession.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpSession.java.svn-base
new file mode 100644
index 0000000..baaccf2
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockHttpSession.java.svn-base
@@ -0,0 +1,267 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+
+/**
+ * The Class MockHttpSession.
+ * 
+ * @author jwilliams
+ */
+public class MockHttpSession implements HttpSession {
+
+	/** The invalidated. */
+	boolean invalidated = false;
+	
+	/** The creation time. */
+	private long creationTime=new Date().getTime();
+	
+	/** The accessed time. */
+	private long accessedTime=new Date().getTime();
+	
+	/** The count. */
+	private static int count = 1;
+	
+	/** The sessionid. */
+	private int sessionid=count++;
+	
+	/** The attributes. */
+	private Map attributes = new HashMap();
+	
+	/**
+	 * Instantiates a new test http session.
+	 */
+	public MockHttpSession() {
+		// to replace synthetic accessor method
+	}
+	
+	/**
+	 * Instantiates a new test http session.
+	 * 
+	 * @param creationTime
+	 *            the creation time
+	 * @param accessedTime
+	 *            the accessed time
+	 */
+	public MockHttpSession( long creationTime, long accessedTime ) {
+		this.creationTime = creationTime;
+		this.accessedTime = accessedTime;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @return
+     */
+	public Object getAttribute(String string) {
+		return attributes.get( string );
+	}
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @return
+     */
+	public Enumeration getAttributeNames() {
+		Vector v = new Vector( attributes.keySet() );
+		return v.elements();
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public long getCreationTime() {
+		return creationTime;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public String getId() {
+		return ""+sessionid;
+	}
+
+	/**
+	 * Gets the invalidated.
+	 * 
+	 * @return the invalidated
+	 */
+	public boolean getInvalidated() {
+		return invalidated;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public long getLastAccessedTime() {
+		return accessedTime;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public int getMaxInactiveInterval() {
+		return 0;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public ServletContext getServletContext() {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return null
+     * @deprecated
+     */
+        @Deprecated
+	// need the full class here as for whatever stupid reason you can't
+	// seem to @SuppressWarnings{'deprecation'} on the import... *sigh*
+	public javax.servlet.http.HttpSessionContext getSessionContext() {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @return
+     * @deprecated
+     */
+     	@Deprecated
+	public Object getValue(String string) {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     * @deprecated
+     */
+     	@Deprecated
+	public String[] getValueNames() {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public void invalidate() {
+		invalidated = true;
+	}
+    
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public boolean isNew() {
+		return true;
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @param object
+     * @deprecated
+     */
+     	@Deprecated
+	public void putValue(String string, Object object) {
+		setAttribute( string, object );
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     */
+	public void removeAttribute(String string) {
+		attributes.remove( string );
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @deprecated
+     */
+     	@Deprecated
+	public void removeValue(String string) {
+		removeAttribute( string );
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @param object
+     */
+	public void setAttribute(String string, Object object) {
+		attributes.put(string, object);
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param i
+     */
+	public void setMaxInactiveInterval(int i) {
+		// stub
+	}
+	
+    /**
+     *
+     * @param time
+     */
+    public void setAccessedTime( long time ) {
+		this.accessedTime = time;
+	}
+
+	
+    /**
+     *
+     * @param time
+     */
+    public void setCreationTime( long time ) {
+		this.creationTime = time;
+	}
+
+}
+
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockRequestDispatcher.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockRequestDispatcher.java.svn-base
new file mode 100644
index 0000000..3cec7a1
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockRequestDispatcher.java.svn-base
@@ -0,0 +1,54 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class MockRequestDispatcher implements RequestDispatcher {
+
+    /**
+     *
+     * @param request
+     * @param response
+     * @throws javax.servlet.ServletException
+     * @throws java.io.IOException
+     */
+    public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+    	System.out.println( "Forwarding" );
+    }
+    
+    /**
+     *
+     * @param request
+     * @param response
+     * @throws javax.servlet.ServletException
+     * @throws java.io.IOException
+     */
+    public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+    	System.out.println( "Including" );
+    }
+}
+
+
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockServletContext.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockServletContext.java.svn-base
new file mode 100644
index 0000000..8c2c4f2
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockServletContext.java.svn-base
@@ -0,0 +1,549 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/**
+ * Defines a set of methods that a servlet uses to communicate with its
+ * servlet container, for example, to get the MIME type of a file, dispatch
+ * requests, or write to a log file.
+ *
+ * <p>There is one context per "web application" per Java Virtual Machine.  (A
+ * "web application" is a collection of servlets and content installed under a
+ * specific subset of the server's URL namespace such as <code>/catalog</code>
+ * and possibly installed via a <code>.war</code> file.)
+ *
+ * <p>In the case of a web
+ * application marked "distributed" in its deployment descriptor, there will
+ * be one context instance for each virtual machine.  In this situation, the
+ * context cannot be used as a location to share global information (because
+ * the information won't be truly global).  Use an external resource like
+ * a database instead.
+ *
+ * <p>The <code>ServletContext</code> object is contained within
+ * the {@link ServletConfig} object, which the Web server provides the
+ * servlet when the servlet is initialized.
+ *
+ * @see Servlet#getServletConfig
+ * @see ServletConfig#getServletContext
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 04:56:06 -0500 (Tue, 14 Sep 2004) $
+ */
+public class MockServletContext implements ServletContext {
+    /**
+     * Returns a <code>ServletContext</code> object that
+     * corresponds to a specified URL on the server.
+     *
+     * <p>This method allows servlets to gain
+     * access to the context for various parts of the server, and as
+     * needed obtain {@link RequestDispatcher} objects from the context.
+     * The given path must be begin with "/", is interpreted relative
+     * to the server's document root and is matched against the context roots of
+     * other web applications hosted on this container.
+     *
+     * <p>In a security conscious environment, the servlet container may
+     * return <code>null</code> for a given URL.
+     *
+     * @param uripath a <code>String</code> specifying the context path of
+     * another web application in the container.
+     * @return the <code>ServletContext</code> object that
+     * corresponds to the named URL, or null if either none exists or the
+     * container wishes to restrict this access.
+     *
+     * @see RequestDispatcher
+     */
+    public ServletContext getContext(String uripath) {
+    	return null;
+    }
+
+    /**
+     * Returns the major version of the Java Servlet API that this
+     * servlet container supports. All implementations that comply
+     * with Version 2.4 must have this method
+     * return the integer 2.
+     *
+     * @return 2
+     */
+    public int getMajorVersion() {
+    	return 2;
+    }
+
+    /**
+     * Returns the minor version of the Servlet API that this
+     * servlet container supports. All implementations that comply
+     * with Version 2.4 must have this method
+     * return the integer 4.
+     */
+    public int getMinorVersion() {
+    	return 4;
+    }
+
+    /**
+     * Returns the MIME type of the specified file, or <code>null</code> if
+     * the MIME type is not known. The MIME type is determined
+     * by the configuration of the servlet container, and may be specified
+     * in a web application deployment descriptor. Common MIME
+     * types are <code>"text/html"</code> and <code>"image/gif"</code>.
+     *
+     * @param file a <code>String</code> specifying the name
+     * of a file
+     *
+     * @return a <code>String</code> specifying the file's MIME type
+     */
+    public String getMimeType(String file) {
+    	return "text/html";
+    }
+
+    /**
+     * Returns a directory-like listing of all the paths to resources within
+     * the web application whose longest sub-path matches the supplied path
+     * argument. Paths indicating subdirectory paths end with a '/'. The
+     * returned paths are all relative to the root of the web application and
+     * have a leading '/'. For example, for a web application containing<br>
+     * <br>
+     * /welcome.html<br>
+     * /catalog/index.html<br>
+     * /catalog/products.html<br>
+     * /catalog/offers/books.html<br>
+     * /catalog/offers/music.html<br>
+     * /customer/login.jsp<br>
+     * /WEB-INF/web.xml<br>
+     * /WEB-INF/classes/com.acme.OrderServlet.class,<br><br>
+     *
+     * getResourcePaths("/") returns {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}<br>
+     * getResourcePaths("/catalog/") returns {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}.<br>
+     *
+     * @param path the partial path used to match the resources,
+     *  which must start with a /
+     * @return a Set containing the directory listing, or null if there are no
+     * resources in the web application whose path begins with the supplied path.
+     *
+     * @since Servlet 2.3
+     */
+    public Set getResourcePaths(String path) {
+    	return null;
+    }
+
+    /**
+     * Returns a URL to the resource that is mapped to a specified
+     * path. The path must begin with a "/" and is interpreted
+     * as relative to the current context root.
+     *
+     * <p>This method allows the servlet container to make a resource
+     * available to servlets from any source. Resources
+     * can be located on a local or remote
+     * file system, in a database, or in a <code>.war</code> file.
+     *
+     * <p>The servlet container must implement the URL handlers
+     * and <code>URLConnection</code> objects that are necessary
+     * to access the resource.
+     *
+     * <p>This method returns <code>null</code>
+     * if no resource is mapped to the pathname.
+     *
+     * <p>Some containers may allow writing to the URL returned by
+     * this method using the methods of the URL class.
+     *
+     * <p>The resource content is returned directly, so be aware that
+     * requesting a <code>.jsp</code> page returns the JSP source code.
+     * Use a <code>RequestDispatcher</code> instead to include results of
+     * an execution.
+     *
+     * <p>This method has a different purpose than
+     * <code>java.lang.Class.getResource</code>,
+     * which looks up resources based on a class loader. This
+     * method does not use class loaders.
+     *
+     * @param path a <code>String</code> specifying the path to the resource
+     *
+     * @return the resource located at the named path, or <code>null</code>
+     * if there is no resource at that path
+     *
+     * @exception MalformedURLException if the pathname is not given in
+     * the correct form
+     */
+    public URL getResource(String path) throws MalformedURLException {
+    	return null;
+    }
+
+    /**
+     * Returns the resource located at the named path as
+     * an <code>InputStream</code> object.
+     *
+     * <p>The data in the <code>InputStream</code> can be
+     * of any type or length. The path must be specified according
+     * to the rules given in <code>getResource</code>.
+     * This method returns <code>null</code> if no resource exists at
+     * the specified path.
+     *
+     * <p>Meta-information such as content length and content type
+     * that is available via <code>getResource</code>
+     * method is lost when using this method.
+     *
+     * <p>The servlet container must implement the URL handlers
+     * and <code>URLConnection</code> objects necessary to access
+     * the resource.
+     *
+     * <p>This method is different from
+     * <code>java.lang.Class.getResourceAsStream</code>,
+     * which uses a class loader. This method allows servlet containers
+     * to make a resource available
+     * to a servlet from any location, without using a class loader.
+     *
+     * @param path a <code>String</code> specifying the path
+     * to the resource
+     *
+     * @return the <code>InputStream</code> returned to the
+     * servlet, or <code>null</code> if no resource exists at the
+     * specified path
+     */
+    public InputStream getResourceAsStream(String path) {
+    	return null;
+    }
+
+    /**
+     * Returns a {@link RequestDispatcher} object that acts
+     * as a wrapper for the resource located at the given path.
+     * A <code>RequestDispatcher</code> object can be used to forward
+     * a request to the resource or to include the resource in a response.
+     * The resource can be dynamic or static.
+     *
+     * <p>The pathname must begin with a "/" and is interpreted as relative
+     * to the current context root.  Use <code>getContext</code> to obtain
+     * a <code>RequestDispatcher</code> for resources in foreign contexts.
+     * This method returns <code>null</code> if the <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code>.
+     *
+     * @param path a <code>String</code> specifying the pathname
+     * to the resource
+     *
+     * @return a <code>RequestDispatcher</code> object that acts as a wrapper
+     * for the resource at the specified path, or <code>null</code> if the
+     * <code>ServletContext</code> cannot return a <code>RequestDispatcher</code>
+     *
+     * @see RequestDispatcher
+     * @see ServletContext#getContext
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+    	return null;
+    }
+
+    /**
+     * Returns a {@link RequestDispatcher} object that acts
+     * as a wrapper for the named servlet.
+     *
+     * <p>Servlets (and JSP pages also) may be given names via server
+     * administration or via a web application deployment descriptor.
+     * A servlet instance can determine its name using
+     * {@link ServletConfig#getServletName}.
+     *
+     * <p>This method returns <code>null</code> if the
+     * <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code> for any reason.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of a servlet to wrap
+     *
+     * @return a <code>RequestDispatcher</code> object
+     * that acts as a wrapper for the named servlet,
+     * or <code>null</code> if the <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code>
+     *
+     * @see RequestDispatcher
+     * @see ServletContext#getContext
+     * @see ServletConfig#getServletName
+     */
+    public RequestDispatcher getNamedDispatcher(String name) {
+    	return null;
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     *
+     * <p>This method was originally defined to retrieve a servlet
+     * from a <code>ServletContext</code>. In this version, this method
+     * always returns <code>null</code> and remains only to preserve
+     * binary compatibility. This method will be permanently removed
+     * in a future version of the Java Servlet API.
+     *
+     * <p>In lieu of this method, servlets can share information using the
+     * <code>ServletContext</code> class and can perform shared business logic
+     * by invoking methods on common non-servlet classes.
+     */
+    public Servlet getServlet(String name) throws ServletException {
+    	return null;
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.0, with no replacement.
+     *
+     * <p>This method was originally defined to return an <code>Enumeration</code>
+     * of all the servlets known to this servlet context. In this
+     * version, this method always returns an empty enumeration and
+     * remains only to preserve binary compatibility. This method
+     * will be permanently removed in a future version of the Java
+     * Servlet API.
+     */
+    public Enumeration getServlets() {
+    	return null;
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no replacement.
+     *
+     * <p>This method was originally defined to return an
+     * <code>Enumeration</code>
+     * of all the servlet names known to this context. In this version,
+     * this method always returns an empty <code>Enumeration</code> and
+     * remains only to preserve binary compatibility. This method will
+     * be permanently removed in a future version of the Java Servlet API.
+     */
+    public Enumeration getServletNames() {
+    	return null;
+    }
+
+    /**
+     * Writes the specified message to a servlet log file, usually
+     * an event log. The name and type of the servlet log file is
+     * specific to the servlet container.
+     *
+     * @param msg a <code>String</code> specifying the
+     * message to be written to the log file
+     */
+    public void log(String msg) {
+    	ESAPI.getLogger( "MockServletContext" ).warning( Logger.EVENT_FAILURE, msg );
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, use
+     * {@link #log(String message, Throwable throwable)}
+     * instead.
+     *
+     * <p>This method was originally defined to write an
+     * exception's stack trace and an explanatory error message
+     * to the servlet log file.
+     */
+    public void log(Exception exception, String msg) {
+    	ESAPI.getLogger( "MockServletContext" ).warning( Logger.EVENT_FAILURE, msg, exception );
+    }
+
+    /**
+     * Writes an explanatory message and a stack trace
+     * for a given <code>Throwable</code> exception
+     * to the servlet log file. The name and type of the servlet log
+     * file is specific to the servlet container, usually an event log.
+     *
+     * @param message a <code>String</code> that
+     * describes the error or exception
+     *
+     * @param throwable the <code>Throwable</code> error
+     * or exception
+     */
+    public void log(String message, Throwable throwable) {
+    	ESAPI.getLogger( "MockServletContext" ).warning( Logger.EVENT_FAILURE, message, throwable );
+    }
+
+    /**
+     * Returns a <code>String</code> containing the real path
+     * for a given virtual path. For example, the path "/index.html"
+     * returns the absolute file path on the server's filesystem would be
+     * served by a request for "http://host/contextPath/index.html",
+     * where contextPath is the context path of this ServletContext..
+     *
+     * <p>The real path returned will be in a form
+     * appropriate to the computer and operating system on
+     * which the servlet container is running, including the
+     * proper path separators. This method returns <code>null</code>
+     * if the servlet container cannot translate the virtual path
+     * to a real path for any reason (such as when the content is
+     * being made available from a <code>.war</code> archive).
+     *
+     * @param path a <code>String</code> specifying a virtual path
+     *
+     * @return a <code>String</code> specifying the real path,
+     * or null if the translation cannot be performed
+     */
+    public String getRealPath(String path) {
+    	return ESAPI.securityConfiguration().getResourceFile( path ).getAbsolutePath();
+    }
+
+    /**
+     * Returns the name and version of the servlet container on which
+     * the servlet is running.
+     *
+     * <p>The form of the returned string is
+     * <i>servername</i>/<i>versionnumber</i>.
+     * For example, the JavaServer Web Development Kit may return the string
+     * <code>JavaServer Web Dev Kit/1.0</code>.
+     *
+     * <p>The servlet container may return other optional information
+     * after the primary string in parentheses, for example,
+     * <code>JavaServer Web Dev Kit/1.0 (JDK 1.1.6; Windows NT 4.0 x86)</code>.
+     *
+     * @return a <code>String</code> containing at least the
+     * servlet container name and version number
+     */
+    public String getServerInfo() {
+    	return null;
+    }
+
+    /**
+     * Returns a <code>String</code> containing the value of the named
+     * context-wide initialization parameter, or <code>null</code> if the
+     * parameter does not exist.
+     *
+     * <p>This method can make available configuration information useful
+     * to an entire "web application".  For example, it can provide a
+     * webmaster's email address or the name of a system that holds
+     * critical data.
+     *
+     * @param name a <code>String</code> containing the name of the
+     * parameter whose value is requested
+     *
+     * @return a <code>String</code> containing at least the
+     * servlet container name and version number
+     *
+     * @see ServletConfig#getInitParameter
+     */
+    public String getInitParameter(String name) {
+    	return null;
+    }
+
+    /**
+     * Returns the names of the context's initialization parameters as an
+     * <code>Enumeration</code> of <code>String</code> objects, or an
+     * empty <code>Enumeration</code> if the context has no initialization
+     * parameters.
+     *
+     * @return an <code>Enumeration</code> of <code>String</code>
+     * objects containing the names of the context's initialization parameters
+     *
+     * @see ServletConfig#getInitParameter
+     */
+    public Enumeration getInitParameterNames() {
+    	return null;
+    }
+
+
+    /**
+     * Returns the servlet container attribute with the given name,
+     * or <code>null</code> if there is no attribute by that name.
+     * An attribute allows a servlet container to give the
+     * servlet additional information not
+     * already provided by this interface. See your
+     * server documentation for information about its attributes.
+     * A list of supported attributes can be retrieved using
+     * <code>getAttributeNames</code>.
+     *
+     * <p>The attribute is returned as a <code>java.lang.Object</code>
+     * or some subclass.
+     * Attribute names should follow the same convention as package
+     * names. The Java Servlet API specification reserves names
+     * matching <code>java.*</code>, <code>javax.*</code>,
+     * and <code>sun.*</code>.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of the attribute
+     *
+     * @return an <code>Object</code> containing the value
+     * of the attribute, or <code>null</code> if no attribute
+     * exists matching the given name
+     *
+     * @see ServletContext#getAttributeNames
+     */
+    public Object getAttribute(String name) {
+    	return null;
+    }
+
+    /**
+     * Returns an <code>Enumeration</code> containing the
+     * attribute names available
+     * within this servlet context. Use the
+     * {@link #getAttribute} method with an attribute name
+     * to get the value of an attribute.
+     *
+     * @return an <code>Enumeration</code> of attribute names
+     *
+     * @see #getAttribute
+     */
+    public Enumeration getAttributeNames() {
+    	return null;
+    }
+
+    /**
+     * Binds an object to a given attribute name in this servlet context. If
+     * the name specified is already used for an attribute, this
+     * method will replace the attribute with the new to the new attribute.
+     * <p>If listeners are configured on the <code>ServletContext</code> the
+     * container notifies them accordingly.
+     * <p>
+     * If a null value is passed, the effect is the same as calling
+     * <code>removeAttribute()</code>.
+     *
+     * <p>Attribute names should follow the same convention as package
+     * names. The Java Servlet API specification reserves names
+     * matching <code>java.*</code>, <code>javax.*</code>, and
+     * <code>sun.*</code>.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of the attribute
+     *
+     * @param object an <code>Object</code> representing the
+     * attribute to be bound
+     */
+    public void setAttribute(String name, Object object) {
+    }
+
+    /**
+     * Removes the attribute with the given name from
+     * the servlet context. After removal, subsequent calls to
+     * {@link #getAttribute} to retrieve the attribute's value
+     * will return <code>null</code>.
+     *
+     * <p>If listeners are configured on the <code>ServletContext</code> the
+     * container notifies them accordingly.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of the attribute to be removed
+     */
+    public void removeAttribute(String name) {
+    }
+
+    /**
+     * Returns the name of this web application correponding to this ServletContext as specified in the deployment
+     * descriptor for this web application by the display-name element.
+     *
+     * @return The name of the web application or null if no name has been declared in the deployment descriptor.
+     * @since Servlet 2.3
+     */
+    public String getServletContextName() {
+    	return null;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/MockServletInputStream.java.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockServletInputStream.java.svn-base
new file mode 100644
index 0000000..1e7c903
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/MockServletInputStream.java.svn-base
@@ -0,0 +1,51 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import javax.servlet.ServletInputStream;
+import java.io.IOException;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class MockServletInputStream extends ServletInputStream {
+
+    private byte[] body;
+
+    private int next;
+
+    /**
+     * constructor
+     * @param body
+     */
+    public MockServletInputStream(byte[] body) {
+        this.body = body;
+    }
+
+    /**
+     * read
+     * @return 
+     * @throws IOException
+     */
+    public int read() throws IOException {
+        if (next < body.length) {
+            return body[next++];
+        } else {
+            return -1;
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/http/.svn/text-base/package.html.svn-base b/src/test/java/org/owasp/esapi/http/.svn/text-base/package.html.svn-base
new file mode 100644
index 0000000..76f720f
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/.svn/text-base/package.html.svn-base
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+A few simple mock classes to help test the ESAPI reference
+implementation. These classes are not fully functional and only
+implement the functions required to test the library. These
+implementations are not fully accurate and may not behave like the real
+Java EE classes.
+
+</body>
+</html>
diff --git a/src/test/java/org/owasp/esapi/http/MockFilterChain.java b/src/test/java/org/owasp/esapi/http/MockFilterChain.java
new file mode 100644
index 0000000..7bd05ca
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockFilterChain.java
@@ -0,0 +1,46 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class MockFilterChain implements FilterChain {
+    /**
+     *
+     * @param request
+     * @param response
+     * @throws java.io.IOException
+     * @throws javax.servlet.ServletException
+     */
+    public void doFilter( ServletRequest request, ServletResponse response ) throws IOException, ServletException {
+    	System.out.println( "CHAIN received " + request.getClass().getName() + " and is issuing " + response.getClass().getName() );
+       	response.getOutputStream().println( "   This is the body of a response for " +  ((HttpServletRequest)request).getRequestURI() );
+           	((HttpServletResponse)response).addCookie( new Cookie( "name", "value" ) );
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/http/MockFilterConfig.java b/src/test/java/org/owasp/esapi/http/MockFilterConfig.java
new file mode 100644
index 0000000..6d5a3bf
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockFilterConfig.java
@@ -0,0 +1,66 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+
+/**
+ * A filter configuration object used by a servlet container
+ * to pass information to a filter during initialization.
+ */
+public class MockFilterConfig implements FilterConfig {
+	private Map map;
+
+	public MockFilterConfig( Map map ) {
+		this.map = map;
+	}
+	
+    public String getFilterName() {
+    	return "mock";
+    }
+
+    /**
+     * Returns a reference to the {@link ServletContext} in which the caller
+     * is executing.
+     */
+    public ServletContext getServletContext() {
+    	return new MockServletContext();
+    }
+
+    /**
+     * Returns a <code>String</code> containing the value of the
+     * named initialization parameter, or <code>null</code> if
+     * the parameter does not exist.
+     */
+    public String getInitParameter(String name) {
+    	return (String)map.get( name );
+    }
+
+    /**
+     * Returns the names of the filter's initialization parameters
+     * as an <code>Enumeration</code> of <code>String</code> objects,
+     * or an empty <code>Enumeration</code> if the filter has
+     * no initialization parameters.
+     */
+    public Enumeration getInitParameterNames() {
+    	return Collections.enumeration( map.keySet() );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java b/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java
new file mode 100644
index 0000000..87cb92f
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java
@@ -0,0 +1,782 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.security.Principal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * The Class MockHttpServletRequest.
+ * 
+ * @author jwilliams
+ */
+public class MockHttpServletRequest implements HttpServletRequest
+{
+	private static final String HDR_CONTENT_TYPE = "Content-Type";
+	private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+	/** The requestDispatcher */
+	private RequestDispatcher requestDispatcher = new MockRequestDispatcher();
+
+	/** The session. */
+	private MockHttpSession session = null;
+
+	/** The cookies. */
+	private ArrayList cookies = new ArrayList();
+
+	/** The parameters. */
+	private Map<String,String[]> parameters = new HashMap<String,String[]>();
+
+	/** The headers. */
+	private Map<String,List<String>> headers = new HashMap<String,List<String>>();
+
+	private byte[] body;
+
+	private String scheme = "https";
+
+	private String remoteHost = "64.14.103.52";
+
+	private String serverHost = "64.14.103.52";
+
+	private String uri = "/test";
+
+	private String url = "https://www.example.com" + uri;
+
+	private String queryString = "pid=1&qid=test";
+
+	private String method = "POST";
+
+	private Map<String,Object> attrs = new HashMap<String,Object>();
+
+	/**
+	 *
+	 */
+	public MockHttpServletRequest() {
+	}
+
+	/**
+	 *
+	 * @param uri
+	 * @param body
+	 */
+	public MockHttpServletRequest(String uri, byte[] body) {
+		this.body = body;
+		this.uri = uri;
+	}
+
+	public MockHttpServletRequest( URL url ) {
+		scheme = url.getProtocol();
+		serverHost = url.getHost();
+		uri = url.getPath();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getAuthType() {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getContextPath() {
+		return null;
+	}
+
+	/**
+	 * Adds the parameter.
+	 * 
+	 * @param name the name
+	 * @param value the value
+	 */
+	public void addParameter(String name, String value) {
+		String[] old = parameters.get(name);
+		if ( old == null ) {
+			old = new String[0];
+		}
+		String[] updated = new String[old.length + 1];
+		for ( int i = 0; i < old.length; i++ ) updated[i] = old[i];
+		updated[old.length] = value;
+		parameters.put(name, updated);
+	}
+
+	/**
+	 * removeParameter removes the parameter name from the parameters map if it exists
+	 *  
+	 * @param name
+	 * 			parameter name to be removed
+	 */
+	public void removeParameter( String name ) {
+		parameters.remove( name );
+	}
+
+	/**
+	 * Adds the header.
+	 * 
+	 * @param name the name
+	 * @param value the value
+	 */
+	public void addHeader(String name, String value)
+	{
+		List<String> values;
+
+		if((values = headers.get(name))==null)
+		{
+			values = new ArrayList<String>();
+			headers.put(name, values);
+		}
+		values.add(value);
+	}
+
+	/**
+	 * Set a header replacing any previous value(s).
+	 * @param name the header name
+	 * @param value the header value
+	 */
+	public void setHeader(String name, String value)
+	{
+		List<String> values = new ArrayList<String>();
+
+		values.add(value);
+		headers.put(name,values);
+	}
+
+	/**
+	 * Sets the cookies.
+	 * 
+	 * @param list the new cookies
+	 */
+	public void setCookies(ArrayList list) {
+		cookies = list;
+	}
+
+	/**
+	 *
+	 * @param name
+	 * @param value
+	 */
+	public void setCookie(String name, String value ) {
+		Cookie c = new Cookie( name, value );
+		cookies.add( c );
+	}
+
+	public boolean clearCookie(String name) {
+		return cookies.remove(name);
+	}
+
+	/**
+	 * @return 
+	 *
+	 */
+	public void clearCookies() {
+		cookies.clear();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Cookie[] getCookies() {
+		if ( cookies.isEmpty() ) return null;
+		return (Cookie[]) cookies.toArray(new Cookie[0]);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @return
+	 */
+	public long getDateHeader(String name) {
+		try {
+			Date date = SimpleDateFormat.getDateTimeInstance().parse( getParameter( name ) );
+			return date.getTime(); // TODO needs to be HTTP format
+		} catch( ParseException e ) {
+			return 0;
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @return
+	 */
+	public String getHeader(String name) {
+		List<String> values;
+
+		if((values = headers.get(name))==null)
+			return null;
+		if(values.size() == 0)
+			return null;
+		return values.get(0);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return Enumeration of header names as strings
+	 */
+	public Enumeration getHeaderNames()
+	{
+		return Collections.enumeration(headers.keySet());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public Enumeration getHeaders(String name) {
+		Vector v = new Vector();
+		v.add( getHeader( name ) );
+		return v.elements();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @return
+	 */
+	public int getIntHeader(String name) {
+
+		return 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getMethod() {
+		return method;
+	}
+
+	/**
+	 *
+	 * @param value
+	 */
+	public void setMethod( String value ) {
+		method = value;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getPathInfo() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getPathTranslated() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getQueryString() {
+		return queryString;
+	}
+
+	/**
+	 * Set the query string to return.
+	 * @param str The query string to return.
+	 */
+	public void setQueryString(String str)
+	{
+		this.queryString = str;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRemoteUser() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRequestURI() {
+		return uri;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public StringBuffer getRequestURL() {
+		return new StringBuffer( getScheme() + "://" + this.getServerName() + getRequestURI() + "?" + getQueryString() );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRequestedSessionId() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getServletPath() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public HttpSession getSession() {
+		if (session != null) {
+			return getSession(false);
+		}
+		return getSession(true);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param create 
+	 * @return
+	 */
+	public HttpSession getSession(boolean create) {
+		if (session == null && create) {
+			session = new MockHttpSession();
+		} else if (session != null && session.getInvalidated()) {
+			session = new MockHttpSession();
+		}
+		return session;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Principal getUserPrincipal() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isRequestedSessionIdFromCookie() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isRequestedSessionIdFromURL() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public boolean isRequestedSessionIdFromUrl() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isRequestedSessionIdValid() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param role 
+	 * @return
+	 */
+	public boolean isUserInRole(String role) {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public Object getAttribute(String name) {
+		return attrs.get(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Enumeration getAttributeNames() {
+		return Collections.enumeration(attrs.keySet());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getCharacterEncoding() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getContentLength() {
+		return body.length;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getContentType() {
+		return getHeader(HDR_CONTENT_TYPE);
+	}
+
+	/**
+	 *
+	 * @param value
+	 */
+	public void setContentType( String value ) {
+		setHeader(HDR_CONTENT_TYPE, value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 * @throws IOException
+	 */
+	public ServletInputStream getInputStream() throws IOException {
+		return new MockServletInputStream(body);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getLocalAddr() {
+		return "10.1.43.6";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getLocalName() {
+		return "www.domain.com";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getLocalPort() {
+		return 80;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Locale getLocale() {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Enumeration getLocales() {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public String getParameter(String name) {
+		String[] values = parameters.get(name);
+		if ( values == null ) return null;
+		return values[0];
+	}
+
+	public void clearParameter(String name) {
+		parameters.remove( name );
+	}
+
+	public void clearParameters() {
+		parameters.clear();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Map getParameterMap() {
+		return parameters;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public Enumeration getParameterNames() {
+		return Collections.enumeration(parameters.keySet());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 * @return
+	 */
+	public String[] getParameterValues(String name) {
+		return parameters.get(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getProtocol() {
+		return "HTTP/1.1";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return 
+	 * @throws IOException
+	 */
+	public BufferedReader getReader() throws IOException {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param path
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public String getRealPath(String path) {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRemoteAddr() {
+		return remoteHost;
+	}
+
+	public void setRemoteAddr(String remoteHost) {
+		this.remoteHost = remoteHost;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getRemoteHost() {
+		return remoteHost;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getRemotePort() {
+
+		return 0;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param path
+	 * @return
+	 */
+	public RequestDispatcher getRequestDispatcher(String path) {
+		return requestDispatcher;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getScheme() {
+		return scheme;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public String getServerName() {
+		return serverHost;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public int getServerPort() {
+
+		return 80;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @return
+	 */
+	public boolean isSecure() {
+		return scheme.equals( "https" );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name
+	 */
+	public void removeAttribute(String name) {
+		attrs.remove(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param name 
+	 * @param o
+	 */
+	public void setAttribute(String name, Object o) {
+		attrs.put(name,o);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @param env
+	 * @throws UnsupportedEncodingException
+	 */
+	public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+
+	}
+
+	/**
+	 *
+	 * @param uri
+	 * @throws java.io.UnsupportedEncodingException
+	 */
+	public void setRequestURI(String uri) throws UnsupportedEncodingException {
+		this.uri = uri;
+	}
+
+	/**
+	 *
+	 * @param url
+	 * @throws java.io.UnsupportedEncodingException
+	 */
+	public void setRequestURL(String url) throws UnsupportedEncodingException {
+		// get the scheme
+		int p = url.indexOf( ":" );
+		this.scheme = url.substring( 0, p );
+
+		// get the queryString
+		int q = url.indexOf( "?" );
+		if ( q != -1 )
+		{
+			queryString = url.substring( q+1 );
+			url = url.substring( 0, q );
+		}
+		else
+			queryString = null;
+		this.url = url;
+	}
+
+	public void setScheme( String scheme ) {
+		this.scheme = scheme;
+	}
+
+	public void dump()
+	{
+		String[] names;
+
+		System.out.println();
+		System.out.println( "  " + this.getMethod() + " " + this.getRequestURL() );
+
+		names = headers.keySet().toArray(EMPTY_STRING_ARRAY);
+		Arrays.sort(names);	// make debugging a bit easier...
+		for (String name : names)
+			for(String value : headers.get(name))
+				System.out.println( "  " + name + ": " + value);
+		names = parameters.keySet().toArray(EMPTY_STRING_ARRAY);
+		Arrays.sort(names);
+		for (String name : names) {
+			for(String value : parameters.get(name))
+				System.out.println( "  " + name + "=" + value);
+		}
+		System.out.println( "\n" );
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java b/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java
new file mode 100644
index 0000000..43b4a7e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java
@@ -0,0 +1,463 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.ESAPI;
+
+/**
+ * The Class MockHttpServletResponse.
+ * 
+ * @author jwilliams
+ */
+public class MockHttpServletResponse implements HttpServletResponse {
+
+	/** The cookies. */
+	List cookies = new ArrayList();
+
+	/** The header names. */
+	List headerNames = new ArrayList();
+
+	/** The header values. */
+	List headerValues = new ArrayList();
+
+	/** The status. */
+	int status = 200;
+
+	StringBuffer body = new StringBuffer();
+
+	String contentType = "text/html; charset=ISO-8895-1";
+
+	public String getBody() {
+		return body.toString();
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param cookie
+	 */
+	public void addCookie(Cookie cookie) {
+		cookies.add(cookie);
+	}
+
+	/**
+	 * Gets the cookies.
+	 * 
+	 * @return the cookies
+	 */
+	public List getCookies() {
+		return cookies;
+	}
+
+	/**
+	 * 
+	 * @param name
+	 * @return
+	 */
+	public Cookie getCookie(String name) {
+		Iterator i = cookies.iterator();
+		while (i.hasNext()) {
+			Cookie c = (Cookie) i.next();
+			if (c.getName().equals(name)) {
+				return c;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param date
+	 */
+	public void addDateHeader(String name, long date) {
+		headerNames.add(name);
+		headerValues.add("" + date);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void addHeader(String name, String value) {
+		headerNames.add(name);
+		headerValues.add(value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void addIntHeader(String name, int value) {
+		headerNames.add(name);
+		headerValues.add("" + value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @return
+	 */
+	public boolean containsHeader(String name) {
+		return headerNames.contains(name);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	/**
+	 * Gets the header.
+	 * 
+	 * @param name
+	 *            the name
+	 * 
+	 * @return the header
+	 */
+	public String getHeader(String name) {
+		int index = headerNames.indexOf(name);
+		if (index != -1) {
+			return (String) headerValues.get(index);
+		}
+		return null;
+	}
+
+	/**
+	 * Gets the header names.
+	 * 
+	 * @return the header names
+	 */
+	public List getHeaderNames() {
+		return headerNames;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 */
+	public String encodeRedirectURL(String url) {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public String encodeRedirectUrl(String url) {
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 */
+	public String encodeURL(String url) {
+		String enc = url;
+		try { enc = ESAPI.encoder().encodeForURL(url);
+		} catch( Exception e ) {}
+		return enc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param url
+	 * @return
+	 * @deprecated
+	 */
+	@Deprecated
+	public String encodeUrl(String url) {
+		return encodeURL( url );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 * @throws IOException
+	 */
+	public void sendError(int sc) throws IOException {
+		status = sc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 * @param msg
+	 * @throws IOException
+	 */
+	public void sendError(int sc, String msg) throws IOException {
+		status = sc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param location
+	 * @throws IOException
+	 */
+	public void sendRedirect(String location) throws IOException {
+		status = HttpServletResponse.SC_MOVED_PERMANENTLY;
+		body = new StringBuffer( "Redirect to " + location );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param date
+	 */
+	public void setDateHeader(String name, long date) {
+		headerNames.add(name);
+		headerValues.add("" + date);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void setHeader(String name, String value) {
+		headerNames.add(name);
+		headerValues.add(value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void setIntHeader(String name, int value) {
+		headerNames.add(name);
+		headerValues.add("" + value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 */
+	public void setStatus(int sc) {
+		status = sc;
+	}
+
+	/**
+	 * Gets the status.
+	 * 
+	 * @return the status
+	 */
+	public int getStatus() {
+		return status;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param sc
+	 * @param sm
+	 * @deprecated
+	 */
+	@Deprecated
+	public void setStatus(int sc, String sm) {
+		status = sc;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @throws IOException
+	 */
+	public void flushBuffer() throws IOException {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public int getBufferSize() {
+		return body.length();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public String getCharacterEncoding() {
+		return "UTF-8";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public String getContentType() {
+		return contentType;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public Locale getLocale() {
+
+		return null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 * @throws IOException
+	 */
+	public ServletOutputStream getOutputStream() throws IOException {
+		return new ServletOutputStream() {
+			public void write(int b) throws IOException {
+				body.append((char)b);
+			}
+		};
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 * @throws IOException
+	 */
+	public PrintWriter getWriter() throws IOException {
+		return new PrintWriter( getOutputStream(), true );
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @return
+	 */
+	public boolean isCommitted() {
+
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void reset() {
+		body = new StringBuffer();
+		cookies = new ArrayList();
+		headerNames = new ArrayList();
+		headerValues = new ArrayList();
+		status = 200;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void resetBuffer() {
+		body = new StringBuffer();
+	}
+
+	public void setBody( String value ) {
+		body = new StringBuffer( value );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param size
+	 */
+	public void setBufferSize(int size) {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param charset
+	 */
+	public void setCharacterEncoding(String charset) {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param len
+	 */
+	public void setContentLength(int len) {
+
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param type
+	 */
+	public void setContentType(String type) {
+		contentType = type;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @param loc
+	 */
+	public void setLocale(Locale loc) {
+
+	}
+
+	/*
+	 * Dump the response in a semi-readable format close to a real HTTP response on the wire
+	 */
+	public void dump() {
+        System.out.println();
+		System.out.println( "  " + this.getStatus() + " " );
+        for ( Object name : getHeaderNames() ) System.out.println( "  " + name + "=" + getHeader( (String)name ) );
+        System.out.println( "  BODY: " + this.getBody() );
+        System.out.println();
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/http/MockHttpSession.java b/src/test/java/org/owasp/esapi/http/MockHttpSession.java
new file mode 100644
index 0000000..baaccf2
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockHttpSession.java
@@ -0,0 +1,267 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+
+/**
+ * The Class MockHttpSession.
+ * 
+ * @author jwilliams
+ */
+public class MockHttpSession implements HttpSession {
+
+	/** The invalidated. */
+	boolean invalidated = false;
+	
+	/** The creation time. */
+	private long creationTime=new Date().getTime();
+	
+	/** The accessed time. */
+	private long accessedTime=new Date().getTime();
+	
+	/** The count. */
+	private static int count = 1;
+	
+	/** The sessionid. */
+	private int sessionid=count++;
+	
+	/** The attributes. */
+	private Map attributes = new HashMap();
+	
+	/**
+	 * Instantiates a new test http session.
+	 */
+	public MockHttpSession() {
+		// to replace synthetic accessor method
+	}
+	
+	/**
+	 * Instantiates a new test http session.
+	 * 
+	 * @param creationTime
+	 *            the creation time
+	 * @param accessedTime
+	 *            the accessed time
+	 */
+	public MockHttpSession( long creationTime, long accessedTime ) {
+		this.creationTime = creationTime;
+		this.accessedTime = accessedTime;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @return
+     */
+	public Object getAttribute(String string) {
+		return attributes.get( string );
+	}
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @return
+     */
+	public Enumeration getAttributeNames() {
+		Vector v = new Vector( attributes.keySet() );
+		return v.elements();
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public long getCreationTime() {
+		return creationTime;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public String getId() {
+		return ""+sessionid;
+	}
+
+	/**
+	 * Gets the invalidated.
+	 * 
+	 * @return the invalidated
+	 */
+	public boolean getInvalidated() {
+		return invalidated;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public long getLastAccessedTime() {
+		return accessedTime;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public int getMaxInactiveInterval() {
+		return 0;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public ServletContext getServletContext() {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return null
+     * @deprecated
+     */
+        @Deprecated
+	// need the full class here as for whatever stupid reason you can't
+	// seem to @SuppressWarnings{'deprecation'} on the import... *sigh*
+	public javax.servlet.http.HttpSessionContext getSessionContext() {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @return
+     * @deprecated
+     */
+     	@Deprecated
+	public Object getValue(String string) {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     * @deprecated
+     */
+     	@Deprecated
+	public String[] getValueNames() {
+		return null;
+	}
+
+    /**
+     * {@inheritDoc}
+	 */
+	public void invalidate() {
+		invalidated = true;
+	}
+    
+    /**
+     * {@inheritDoc}
+     *
+     * @return
+     */
+	public boolean isNew() {
+		return true;
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @param object
+     * @deprecated
+     */
+     	@Deprecated
+	public void putValue(String string, Object object) {
+		setAttribute( string, object );
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     */
+	public void removeAttribute(String string) {
+		attributes.remove( string );
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @deprecated
+     */
+     	@Deprecated
+	public void removeValue(String string) {
+		removeAttribute( string );
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string
+     * @param object
+     */
+	public void setAttribute(String string, Object object) {
+		attributes.put(string, object);
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param i
+     */
+	public void setMaxInactiveInterval(int i) {
+		// stub
+	}
+	
+    /**
+     *
+     * @param time
+     */
+    public void setAccessedTime( long time ) {
+		this.accessedTime = time;
+	}
+
+	
+    /**
+     *
+     * @param time
+     */
+    public void setCreationTime( long time ) {
+		this.creationTime = time;
+	}
+
+}
+
diff --git a/src/test/java/org/owasp/esapi/http/MockRequestDispatcher.java b/src/test/java/org/owasp/esapi/http/MockRequestDispatcher.java
new file mode 100644
index 0000000..3cec7a1
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockRequestDispatcher.java
@@ -0,0 +1,54 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class MockRequestDispatcher implements RequestDispatcher {
+
+    /**
+     *
+     * @param request
+     * @param response
+     * @throws javax.servlet.ServletException
+     * @throws java.io.IOException
+     */
+    public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+    	System.out.println( "Forwarding" );
+    }
+    
+    /**
+     *
+     * @param request
+     * @param response
+     * @throws javax.servlet.ServletException
+     * @throws java.io.IOException
+     */
+    public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+    	System.out.println( "Including" );
+    }
+}
+
+
diff --git a/src/test/java/org/owasp/esapi/http/MockServletContext.java b/src/test/java/org/owasp/esapi/http/MockServletContext.java
new file mode 100644
index 0000000..8c2c4f2
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockServletContext.java
@@ -0,0 +1,549 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+
+/**
+ * Defines a set of methods that a servlet uses to communicate with its
+ * servlet container, for example, to get the MIME type of a file, dispatch
+ * requests, or write to a log file.
+ *
+ * <p>There is one context per "web application" per Java Virtual Machine.  (A
+ * "web application" is a collection of servlets and content installed under a
+ * specific subset of the server's URL namespace such as <code>/catalog</code>
+ * and possibly installed via a <code>.war</code> file.)
+ *
+ * <p>In the case of a web
+ * application marked "distributed" in its deployment descriptor, there will
+ * be one context instance for each virtual machine.  In this situation, the
+ * context cannot be used as a location to share global information (because
+ * the information won't be truly global).  Use an external resource like
+ * a database instead.
+ *
+ * <p>The <code>ServletContext</code> object is contained within
+ * the {@link ServletConfig} object, which the Web server provides the
+ * servlet when the servlet is initialized.
+ *
+ * @see Servlet#getServletConfig
+ * @see ServletConfig#getServletContext
+ *
+ * @version $Rev: 46019 $ $Date: 2004-09-14 04:56:06 -0500 (Tue, 14 Sep 2004) $
+ */
+public class MockServletContext implements ServletContext {
+    /**
+     * Returns a <code>ServletContext</code> object that
+     * corresponds to a specified URL on the server.
+     *
+     * <p>This method allows servlets to gain
+     * access to the context for various parts of the server, and as
+     * needed obtain {@link RequestDispatcher} objects from the context.
+     * The given path must be begin with "/", is interpreted relative
+     * to the server's document root and is matched against the context roots of
+     * other web applications hosted on this container.
+     *
+     * <p>In a security conscious environment, the servlet container may
+     * return <code>null</code> for a given URL.
+     *
+     * @param uripath a <code>String</code> specifying the context path of
+     * another web application in the container.
+     * @return the <code>ServletContext</code> object that
+     * corresponds to the named URL, or null if either none exists or the
+     * container wishes to restrict this access.
+     *
+     * @see RequestDispatcher
+     */
+    public ServletContext getContext(String uripath) {
+    	return null;
+    }
+
+    /**
+     * Returns the major version of the Java Servlet API that this
+     * servlet container supports. All implementations that comply
+     * with Version 2.4 must have this method
+     * return the integer 2.
+     *
+     * @return 2
+     */
+    public int getMajorVersion() {
+    	return 2;
+    }
+
+    /**
+     * Returns the minor version of the Servlet API that this
+     * servlet container supports. All implementations that comply
+     * with Version 2.4 must have this method
+     * return the integer 4.
+     */
+    public int getMinorVersion() {
+    	return 4;
+    }
+
+    /**
+     * Returns the MIME type of the specified file, or <code>null</code> if
+     * the MIME type is not known. The MIME type is determined
+     * by the configuration of the servlet container, and may be specified
+     * in a web application deployment descriptor. Common MIME
+     * types are <code>"text/html"</code> and <code>"image/gif"</code>.
+     *
+     * @param file a <code>String</code> specifying the name
+     * of a file
+     *
+     * @return a <code>String</code> specifying the file's MIME type
+     */
+    public String getMimeType(String file) {
+    	return "text/html";
+    }
+
+    /**
+     * Returns a directory-like listing of all the paths to resources within
+     * the web application whose longest sub-path matches the supplied path
+     * argument. Paths indicating subdirectory paths end with a '/'. The
+     * returned paths are all relative to the root of the web application and
+     * have a leading '/'. For example, for a web application containing<br>
+     * <br>
+     * /welcome.html<br>
+     * /catalog/index.html<br>
+     * /catalog/products.html<br>
+     * /catalog/offers/books.html<br>
+     * /catalog/offers/music.html<br>
+     * /customer/login.jsp<br>
+     * /WEB-INF/web.xml<br>
+     * /WEB-INF/classes/com.acme.OrderServlet.class,<br><br>
+     *
+     * getResourcePaths("/") returns {"/welcome.html", "/catalog/", "/customer/", "/WEB-INF/"}<br>
+     * getResourcePaths("/catalog/") returns {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}.<br>
+     *
+     * @param path the partial path used to match the resources,
+     *  which must start with a /
+     * @return a Set containing the directory listing, or null if there are no
+     * resources in the web application whose path begins with the supplied path.
+     *
+     * @since Servlet 2.3
+     */
+    public Set getResourcePaths(String path) {
+    	return null;
+    }
+
+    /**
+     * Returns a URL to the resource that is mapped to a specified
+     * path. The path must begin with a "/" and is interpreted
+     * as relative to the current context root.
+     *
+     * <p>This method allows the servlet container to make a resource
+     * available to servlets from any source. Resources
+     * can be located on a local or remote
+     * file system, in a database, or in a <code>.war</code> file.
+     *
+     * <p>The servlet container must implement the URL handlers
+     * and <code>URLConnection</code> objects that are necessary
+     * to access the resource.
+     *
+     * <p>This method returns <code>null</code>
+     * if no resource is mapped to the pathname.
+     *
+     * <p>Some containers may allow writing to the URL returned by
+     * this method using the methods of the URL class.
+     *
+     * <p>The resource content is returned directly, so be aware that
+     * requesting a <code>.jsp</code> page returns the JSP source code.
+     * Use a <code>RequestDispatcher</code> instead to include results of
+     * an execution.
+     *
+     * <p>This method has a different purpose than
+     * <code>java.lang.Class.getResource</code>,
+     * which looks up resources based on a class loader. This
+     * method does not use class loaders.
+     *
+     * @param path a <code>String</code> specifying the path to the resource
+     *
+     * @return the resource located at the named path, or <code>null</code>
+     * if there is no resource at that path
+     *
+     * @exception MalformedURLException if the pathname is not given in
+     * the correct form
+     */
+    public URL getResource(String path) throws MalformedURLException {
+    	return null;
+    }
+
+    /**
+     * Returns the resource located at the named path as
+     * an <code>InputStream</code> object.
+     *
+     * <p>The data in the <code>InputStream</code> can be
+     * of any type or length. The path must be specified according
+     * to the rules given in <code>getResource</code>.
+     * This method returns <code>null</code> if no resource exists at
+     * the specified path.
+     *
+     * <p>Meta-information such as content length and content type
+     * that is available via <code>getResource</code>
+     * method is lost when using this method.
+     *
+     * <p>The servlet container must implement the URL handlers
+     * and <code>URLConnection</code> objects necessary to access
+     * the resource.
+     *
+     * <p>This method is different from
+     * <code>java.lang.Class.getResourceAsStream</code>,
+     * which uses a class loader. This method allows servlet containers
+     * to make a resource available
+     * to a servlet from any location, without using a class loader.
+     *
+     * @param path a <code>String</code> specifying the path
+     * to the resource
+     *
+     * @return the <code>InputStream</code> returned to the
+     * servlet, or <code>null</code> if no resource exists at the
+     * specified path
+     */
+    public InputStream getResourceAsStream(String path) {
+    	return null;
+    }
+
+    /**
+     * Returns a {@link RequestDispatcher} object that acts
+     * as a wrapper for the resource located at the given path.
+     * A <code>RequestDispatcher</code> object can be used to forward
+     * a request to the resource or to include the resource in a response.
+     * The resource can be dynamic or static.
+     *
+     * <p>The pathname must begin with a "/" and is interpreted as relative
+     * to the current context root.  Use <code>getContext</code> to obtain
+     * a <code>RequestDispatcher</code> for resources in foreign contexts.
+     * This method returns <code>null</code> if the <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code>.
+     *
+     * @param path a <code>String</code> specifying the pathname
+     * to the resource
+     *
+     * @return a <code>RequestDispatcher</code> object that acts as a wrapper
+     * for the resource at the specified path, or <code>null</code> if the
+     * <code>ServletContext</code> cannot return a <code>RequestDispatcher</code>
+     *
+     * @see RequestDispatcher
+     * @see ServletContext#getContext
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+    	return null;
+    }
+
+    /**
+     * Returns a {@link RequestDispatcher} object that acts
+     * as a wrapper for the named servlet.
+     *
+     * <p>Servlets (and JSP pages also) may be given names via server
+     * administration or via a web application deployment descriptor.
+     * A servlet instance can determine its name using
+     * {@link ServletConfig#getServletName}.
+     *
+     * <p>This method returns <code>null</code> if the
+     * <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code> for any reason.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of a servlet to wrap
+     *
+     * @return a <code>RequestDispatcher</code> object
+     * that acts as a wrapper for the named servlet,
+     * or <code>null</code> if the <code>ServletContext</code>
+     * cannot return a <code>RequestDispatcher</code>
+     *
+     * @see RequestDispatcher
+     * @see ServletContext#getContext
+     * @see ServletConfig#getServletName
+     */
+    public RequestDispatcher getNamedDispatcher(String name) {
+    	return null;
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+     *
+     * <p>This method was originally defined to retrieve a servlet
+     * from a <code>ServletContext</code>. In this version, this method
+     * always returns <code>null</code> and remains only to preserve
+     * binary compatibility. This method will be permanently removed
+     * in a future version of the Java Servlet API.
+     *
+     * <p>In lieu of this method, servlets can share information using the
+     * <code>ServletContext</code> class and can perform shared business logic
+     * by invoking methods on common non-servlet classes.
+     */
+    public Servlet getServlet(String name) throws ServletException {
+    	return null;
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.0, with no replacement.
+     *
+     * <p>This method was originally defined to return an <code>Enumeration</code>
+     * of all the servlets known to this servlet context. In this
+     * version, this method always returns an empty enumeration and
+     * remains only to preserve binary compatibility. This method
+     * will be permanently removed in a future version of the Java
+     * Servlet API.
+     */
+    public Enumeration getServlets() {
+    	return null;
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, with no replacement.
+     *
+     * <p>This method was originally defined to return an
+     * <code>Enumeration</code>
+     * of all the servlet names known to this context. In this version,
+     * this method always returns an empty <code>Enumeration</code> and
+     * remains only to preserve binary compatibility. This method will
+     * be permanently removed in a future version of the Java Servlet API.
+     */
+    public Enumeration getServletNames() {
+    	return null;
+    }
+
+    /**
+     * Writes the specified message to a servlet log file, usually
+     * an event log. The name and type of the servlet log file is
+     * specific to the servlet container.
+     *
+     * @param msg a <code>String</code> specifying the
+     * message to be written to the log file
+     */
+    public void log(String msg) {
+    	ESAPI.getLogger( "MockServletContext" ).warning( Logger.EVENT_FAILURE, msg );
+    }
+
+    /**
+     * @deprecated As of Java Servlet API 2.1, use
+     * {@link #log(String message, Throwable throwable)}
+     * instead.
+     *
+     * <p>This method was originally defined to write an
+     * exception's stack trace and an explanatory error message
+     * to the servlet log file.
+     */
+    public void log(Exception exception, String msg) {
+    	ESAPI.getLogger( "MockServletContext" ).warning( Logger.EVENT_FAILURE, msg, exception );
+    }
+
+    /**
+     * Writes an explanatory message and a stack trace
+     * for a given <code>Throwable</code> exception
+     * to the servlet log file. The name and type of the servlet log
+     * file is specific to the servlet container, usually an event log.
+     *
+     * @param message a <code>String</code> that
+     * describes the error or exception
+     *
+     * @param throwable the <code>Throwable</code> error
+     * or exception
+     */
+    public void log(String message, Throwable throwable) {
+    	ESAPI.getLogger( "MockServletContext" ).warning( Logger.EVENT_FAILURE, message, throwable );
+    }
+
+    /**
+     * Returns a <code>String</code> containing the real path
+     * for a given virtual path. For example, the path "/index.html"
+     * returns the absolute file path on the server's filesystem would be
+     * served by a request for "http://host/contextPath/index.html",
+     * where contextPath is the context path of this ServletContext..
+     *
+     * <p>The real path returned will be in a form
+     * appropriate to the computer and operating system on
+     * which the servlet container is running, including the
+     * proper path separators. This method returns <code>null</code>
+     * if the servlet container cannot translate the virtual path
+     * to a real path for any reason (such as when the content is
+     * being made available from a <code>.war</code> archive).
+     *
+     * @param path a <code>String</code> specifying a virtual path
+     *
+     * @return a <code>String</code> specifying the real path,
+     * or null if the translation cannot be performed
+     */
+    public String getRealPath(String path) {
+    	return ESAPI.securityConfiguration().getResourceFile( path ).getAbsolutePath();
+    }
+
+    /**
+     * Returns the name and version of the servlet container on which
+     * the servlet is running.
+     *
+     * <p>The form of the returned string is
+     * <i>servername</i>/<i>versionnumber</i>.
+     * For example, the JavaServer Web Development Kit may return the string
+     * <code>JavaServer Web Dev Kit/1.0</code>.
+     *
+     * <p>The servlet container may return other optional information
+     * after the primary string in parentheses, for example,
+     * <code>JavaServer Web Dev Kit/1.0 (JDK 1.1.6; Windows NT 4.0 x86)</code>.
+     *
+     * @return a <code>String</code> containing at least the
+     * servlet container name and version number
+     */
+    public String getServerInfo() {
+    	return null;
+    }
+
+    /**
+     * Returns a <code>String</code> containing the value of the named
+     * context-wide initialization parameter, or <code>null</code> if the
+     * parameter does not exist.
+     *
+     * <p>This method can make available configuration information useful
+     * to an entire "web application".  For example, it can provide a
+     * webmaster's email address or the name of a system that holds
+     * critical data.
+     *
+     * @param name a <code>String</code> containing the name of the
+     * parameter whose value is requested
+     *
+     * @return a <code>String</code> containing at least the
+     * servlet container name and version number
+     *
+     * @see ServletConfig#getInitParameter
+     */
+    public String getInitParameter(String name) {
+    	return null;
+    }
+
+    /**
+     * Returns the names of the context's initialization parameters as an
+     * <code>Enumeration</code> of <code>String</code> objects, or an
+     * empty <code>Enumeration</code> if the context has no initialization
+     * parameters.
+     *
+     * @return an <code>Enumeration</code> of <code>String</code>
+     * objects containing the names of the context's initialization parameters
+     *
+     * @see ServletConfig#getInitParameter
+     */
+    public Enumeration getInitParameterNames() {
+    	return null;
+    }
+
+
+    /**
+     * Returns the servlet container attribute with the given name,
+     * or <code>null</code> if there is no attribute by that name.
+     * An attribute allows a servlet container to give the
+     * servlet additional information not
+     * already provided by this interface. See your
+     * server documentation for information about its attributes.
+     * A list of supported attributes can be retrieved using
+     * <code>getAttributeNames</code>.
+     *
+     * <p>The attribute is returned as a <code>java.lang.Object</code>
+     * or some subclass.
+     * Attribute names should follow the same convention as package
+     * names. The Java Servlet API specification reserves names
+     * matching <code>java.*</code>, <code>javax.*</code>,
+     * and <code>sun.*</code>.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of the attribute
+     *
+     * @return an <code>Object</code> containing the value
+     * of the attribute, or <code>null</code> if no attribute
+     * exists matching the given name
+     *
+     * @see ServletContext#getAttributeNames
+     */
+    public Object getAttribute(String name) {
+    	return null;
+    }
+
+    /**
+     * Returns an <code>Enumeration</code> containing the
+     * attribute names available
+     * within this servlet context. Use the
+     * {@link #getAttribute} method with an attribute name
+     * to get the value of an attribute.
+     *
+     * @return an <code>Enumeration</code> of attribute names
+     *
+     * @see #getAttribute
+     */
+    public Enumeration getAttributeNames() {
+    	return null;
+    }
+
+    /**
+     * Binds an object to a given attribute name in this servlet context. If
+     * the name specified is already used for an attribute, this
+     * method will replace the attribute with the new to the new attribute.
+     * <p>If listeners are configured on the <code>ServletContext</code> the
+     * container notifies them accordingly.
+     * <p>
+     * If a null value is passed, the effect is the same as calling
+     * <code>removeAttribute()</code>.
+     *
+     * <p>Attribute names should follow the same convention as package
+     * names. The Java Servlet API specification reserves names
+     * matching <code>java.*</code>, <code>javax.*</code>, and
+     * <code>sun.*</code>.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of the attribute
+     *
+     * @param object an <code>Object</code> representing the
+     * attribute to be bound
+     */
+    public void setAttribute(String name, Object object) {
+    }
+
+    /**
+     * Removes the attribute with the given name from
+     * the servlet context. After removal, subsequent calls to
+     * {@link #getAttribute} to retrieve the attribute's value
+     * will return <code>null</code>.
+     *
+     * <p>If listeners are configured on the <code>ServletContext</code> the
+     * container notifies them accordingly.
+     *
+     * @param name a <code>String</code> specifying the name
+     * of the attribute to be removed
+     */
+    public void removeAttribute(String name) {
+    }
+
+    /**
+     * Returns the name of this web application correponding to this ServletContext as specified in the deployment
+     * descriptor for this web application by the display-name element.
+     *
+     * @return The name of the web application or null if no name has been declared in the deployment descriptor.
+     * @since Servlet 2.3
+     */
+    public String getServletContextName() {
+    	return null;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/http/MockServletInputStream.java b/src/test/java/org/owasp/esapi/http/MockServletInputStream.java
new file mode 100644
index 0000000..1e7c903
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/MockServletInputStream.java
@@ -0,0 +1,51 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.http;
+
+import javax.servlet.ServletInputStream;
+import java.io.IOException;
+
+/**
+ *
+ * @author jwilliams
+ */
+public class MockServletInputStream extends ServletInputStream {
+
+    private byte[] body;
+
+    private int next;
+
+    /**
+     * constructor
+     * @param body
+     */
+    public MockServletInputStream(byte[] body) {
+        this.body = body;
+    }
+
+    /**
+     * read
+     * @return 
+     * @throws IOException
+     */
+    public int read() throws IOException {
+        if (next < body.length) {
+            return body[next++];
+        } else {
+            return -1;
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/http/package.html b/src/test/java/org/owasp/esapi/http/package.html
new file mode 100644
index 0000000..76f720f
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/http/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+A few simple mock classes to help test the ESAPI reference
+implementation. These classes are not fully functional and only
+implement the functions required to test the library. These
+implementations are not fully accurate and may not behave like the real
+Java EE classes.
+
+</body>
+</html>
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/all-wcprops b/src/test/java/org/owasp/esapi/reference/.svn/all-wcprops
new file mode 100644
index 0000000..bbf9cb1
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/all-wcprops
@@ -0,0 +1,143 @@
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference
+END
+TestError.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/TestError.java
+END
+TestInfo.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/TestInfo.java
+END
+IntegerAccessReferenceMapTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 110
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/IntegerAccessReferenceMapTest.java
+END
+ExecutorTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/ExecutorTest.java
+END
+TestUnspecified.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/TestUnspecified.java
+END
+TestTrace.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/TestTrace.java
+END
+UnitTestSecurityConfiguration.java
+K 25
+svn:wc:ra_dav:version-url
+V 110
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/UnitTestSecurityConfiguration.java
+END
+DefaultSecurityConfigurationTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java
+END
+AccessControllerTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java
+END
+TestWarning.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/TestWarning.java
+END
+RandomizerTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/RandomizerTest.java
+END
+AuthenticatorTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java
+END
+HTTPUtilitiesTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java
+END
+SafeFileTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/SafeFileTest.java
+END
+AccessReferenceMapTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/AccessReferenceMapTest.java
+END
+UserTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/UserTest.java
+END
+Log4JLoggerTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java
+END
+EncoderTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/EncoderTest.java
+END
+JavaLoggerTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/JavaLoggerTest.java
+END
+IntrusionDetectorTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/IntrusionDetectorTest.java
+END
+ValidatorTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/ValidatorTest.java
+END
+TestDebug.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/TestDebug.java
+END
+TestFatal.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/TestFatal.java
+END
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/entries b/src/test/java/org/owasp/esapi/reference/.svn/entries
new file mode 100644
index 0000000..d870ad3
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/entries
@@ -0,0 +1,819 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:37:45.146375Z
+1890
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+TestFatal.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+b35953628fef72e4f7aa3b249035da9c
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+447
+

+TestError.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+1811f9350ba92d6c983144a3fd1096ae
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+447
+

+TestInfo.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+43526fa90d059e7263221f836c45cbee
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+446
+

+IntegerAccessReferenceMapTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+997f6fec4f142bd5238ffbba8ab1ba78
+2008-12-21T19:08:07.098552Z
+410
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7113
+

+ExecutorTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+a252670c42ade98215cbbaac438ed36a
+2010-09-15T23:43:49.847233Z
+1529
+patrick.allen.higgins
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8010
+

+TestUnspecified.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+4a1be764b048522897aa3bc5e7566b29
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+453
+

+TestTrace.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+721affa1543341c07059055a7d0f94c3
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+447
+

+UnitTestSecurityConfiguration.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+84bcb9f26ab3226d0edf0c543c3b095a
+2010-06-08T04:39:48.001264Z
+1437
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2151
+

+crypto
+dir
+

+DefaultSecurityConfigurationTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+d63fed1d4063d85736d92659ee460c9b
+2011-02-04T05:20:56.939145Z
+1693
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+17753
+

+AccessControllerTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+f04850455c39119d5d42980f6f941cb2
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13519
+

+TestWarning.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+8ba5080ae3691e7b544eb14dad66b3ff
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+449
+

+RandomizerTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+7f064ec93733257ac20a1411dbcef156
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4434
+

+AuthenticatorTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+d1351a92ff8e6ad4dffb022dd1f77093
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22270
+

+validation
+dir
+

+HTTPUtilitiesTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+60a1f3b6dac01e34b31739fec4de091a
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+17808
+

+SafeFileTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+74a8a01e96caf42265249365e9cb318d
+2010-01-17T22:06:50.493737Z
+949
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8426
+

+AccessReferenceMapTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+4c26192cfc0baf74736cf653f0bd3a23
+2008-12-21T19:08:07.098552Z
+410
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7080
+

+UserTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+c204ad19a3e32f81068d2b02075b0f0c
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+24731
+

+Log4JLoggerTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+049526cde9e1e2ffce40801169bcb245
+2011-02-04T05:22:28.277494Z
+1695
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+18874
+

+EncoderTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+e85ce2707e810720cd383bb0e6994354
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+35801
+

+JavaLoggerTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+7e4c8dd2b24f4a12404dccad72154e5e
+2011-02-04T05:22:13.106333Z
+1694
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10937
+

+accesscontrol
+dir
+

+IntrusionDetectorTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+360e54a4e2fe5a2c2ae65f40c47df6c3
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4565
+

+ValidatorTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+6e6ab1fec929c49bd6196776f740f1e5
+2011-07-25T05:56:06.650568Z
+1857
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+63163
+

+TestDebug.java
+file
+
+
+
+
+2014-02-18T16:19:52.481957Z
+bff84dc746f681c3fb3198e8063544af
+2010-10-20T23:53:00.447670Z
+1620
+augustd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+448
+

diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/AccessControllerTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/AccessControllerTest.java.svn-base
new file mode 100644
index 0000000..b3c2a6d
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/AccessControllerTest.java.svn-base
@@ -0,0 +1,358 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+
+
+/**
+ * The Class AccessControllerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AccessControllerTest extends TestCase {
+
+	/**
+	 * Instantiates a new access controller test.
+	 * 
+	 * @param testName
+     *            the test name
+     * @throws Exception
+	 */
+	public AccessControllerTest(String testName) throws Exception {
+		super(testName);
+		
+		Authenticator authenticator = ESAPI.authenticator();
+		String password = authenticator.generateStrongPassword();
+
+		// create a user with the "user" role for this test
+		User alice = authenticator.getUser("testuser1");
+		if ( alice == null ) {
+			alice = authenticator.createUser( "testuser1", password, password);
+		}
+		alice.addRole("user");		
+
+		// create a user with the "admin" role for this test
+		User bob = authenticator.getUser("testuser2");
+		if ( bob == null ) {
+			bob = authenticator.createUser( "testuser2", password, password);
+		}
+		bob.addRole("admin");
+		
+		// create a user with the "user" and "admin" roles for this test
+		User mitch = authenticator.getUser("testuser3");
+		if ( mitch == null ) {
+			mitch = authenticator.createUser( "testuser3", password, password);
+		}
+		mitch.addRole("admin");
+		mitch.addRole("user");
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AccessControllerTest.class);
+		return suite;
+	}
+
+    /**
+     *
+     */
+    public void testMatchRule() {
+		ESAPI.authenticator().setCurrentUser(null);
+		assertFalse(ESAPI.accessController().isAuthorizedForURL("/nobody"));
+	}
+	
+	/**
+	 * Test of isAuthorizedForURL method, of class
+	 * org.owasp.esapi.AccessController.
+     *
+     * @throws Exception
+     */
+	public void testIsAuthorizedForURL() throws Exception {
+		System.out.println("isAuthorizedForURL");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertFalse(instance.isAuthorizedForURL("/nobody"));
+		assertFalse(instance.isAuthorizedForURL("/test/admin"));
+		assertTrue(instance.isAuthorizedForURL("/test/user"));
+		assertTrue(instance.isAuthorizedForURL("/test/all"));
+		assertFalse(instance.isAuthorizedForURL("/test/none"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.gif"));
+		assertFalse(instance.isAuthorizedForURL("/test/none/test.exe"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.png"));
+		assertFalse(instance.isAuthorizedForURL("/test/moderator"));
+		assertTrue(instance.isAuthorizedForURL("/test/profile"));
+		assertFalse(instance.isAuthorizedForURL("/upload"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForURL("/nobody"));
+		assertTrue(instance.isAuthorizedForURL("/test/admin"));
+		assertFalse(instance.isAuthorizedForURL("/test/user"));
+		assertTrue(instance.isAuthorizedForURL("/test/all"));
+		assertFalse(instance.isAuthorizedForURL("/test/none"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.png"));
+		assertFalse(instance.isAuthorizedForURL("/test/moderator"));
+		assertTrue(instance.isAuthorizedForURL("/test/profile"));
+		assertFalse(instance.isAuthorizedForURL("/upload"));
+		
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertFalse(instance.isAuthorizedForURL("/nobody"));
+		assertTrue(instance.isAuthorizedForURL("/test/admin"));
+		assertTrue(instance.isAuthorizedForURL("/test/user"));
+		assertTrue(instance.isAuthorizedForURL("/test/all"));
+		assertFalse(instance.isAuthorizedForURL("/test/none"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.png"));
+		assertFalse(instance.isAuthorizedForURL("/test/moderator"));
+		assertTrue(instance.isAuthorizedForURL("/test/profile"));
+		assertFalse(instance.isAuthorizedForURL("/upload"));
+		
+		try {
+			instance.assertAuthorizedForURL("/test/admin");
+			instance.assertAuthorizedForURL( "/nobody" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of isAuthorizedForFunction method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForFunction() {
+		System.out.println("isAuthorizedForFunction");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForFunction("/FunctionA"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionAdeny"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionB"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionBdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionC"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionCdeny"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForFunction("/FunctionA"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionAdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionB"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionBdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionD"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionDdeny"));
+
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForFunction("/FunctionA"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionAdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionB"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionBdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionC"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionCdeny"));
+
+		try {
+			instance.assertAuthorizedForFunction("/FunctionA");
+			instance.assertAuthorizedForFunction( "/FunctionDdeny" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of isAuthorizedForData method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForData() {
+		System.out.println("isAuthorizedForData");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		Class adminR = null;
+		Class adminRW = null;
+		Class userW = null;
+		Class userRW = null;
+		Class anyR = null;
+		Class userAdminR = null;
+		Class userAdminRW = null;
+		Class undefined = null;
+		
+		try{
+			adminR = Class.forName("java.util.ArrayList");
+			adminRW = Class.forName("java.lang.Math");
+			userW = Class.forName("java.util.Date");
+			userRW = Class.forName("java.lang.String");
+			anyR = Class.forName("java.io.BufferedReader");
+			userAdminR = Class.forName("java.util.Random");
+			userAdminRW = Class.forName("java.awt.event.MouseWheelEvent");
+			undefined = Class.forName("java.io.FileWriter");
+			
+		}catch(ClassNotFoundException cnf){
+			System.out.println("CLASS NOT FOUND.");
+			cnf.printStackTrace();
+		}
+		//test User
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForData("read", userRW));
+		assertFalse(instance.isAuthorizedForData("read", undefined));
+		assertFalse(instance.isAuthorizedForData("write", undefined));
+		assertFalse(instance.isAuthorizedForData("read", userW));
+		assertFalse(instance.isAuthorizedForData("read", adminRW));
+		assertTrue(instance.isAuthorizedForData("write", userRW));
+		assertTrue(instance.isAuthorizedForData("write", userW));
+		assertFalse(instance.isAuthorizedForData("write", anyR));
+		assertTrue(instance.isAuthorizedForData("read", anyR));
+		assertTrue(instance.isAuthorizedForData("read", userAdminR));
+		assertTrue(instance.isAuthorizedForData("write", userAdminRW));
+		
+		//test Admin
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertTrue(instance.isAuthorizedForData("read", adminRW));
+		assertFalse(instance.isAuthorizedForData("read", undefined));
+		assertFalse(instance.isAuthorizedForData("write", undefined));
+		assertFalse(instance.isAuthorizedForData("read", userRW));
+		assertTrue(instance.isAuthorizedForData("write", adminRW));
+		assertFalse(instance.isAuthorizedForData("write", anyR));
+		assertTrue(instance.isAuthorizedForData("read", anyR));
+		assertTrue(instance.isAuthorizedForData("read", userAdminR));
+		assertTrue(instance.isAuthorizedForData("write", userAdminRW));
+		
+		//test User/Admin
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForData("read", userRW));
+		assertFalse(instance.isAuthorizedForData("read", undefined));
+		assertFalse(instance.isAuthorizedForData("write", undefined));
+		assertFalse(instance.isAuthorizedForData("read", userW));
+		assertTrue(instance.isAuthorizedForData("read", adminR));
+		assertTrue(instance.isAuthorizedForData("write", userRW));
+		assertTrue(instance.isAuthorizedForData("write", userW));
+		assertFalse(instance.isAuthorizedForData("write", anyR));
+		assertTrue(instance.isAuthorizedForData("read", anyR));
+		assertTrue(instance.isAuthorizedForData("read", userAdminR));
+		assertTrue(instance.isAuthorizedForData("write", userAdminRW));
+		try {
+			instance.assertAuthorizedForData("read", userRW);
+			instance.assertAuthorizedForData( "write", adminR );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+		
+	}
+
+	/**
+	 * Test of isAuthorizedForFile method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForFile() {
+		System.out.println("isAuthorizedForFile");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForFile("/Dir/File1"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/File2"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File3"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForFile("/Dir/File1"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File2"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File4"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForFile("/Dir/File1"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File2"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/File5"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/ridiculous"));
+
+		try {
+			instance.assertAuthorizedForFile("/Dir/File1");
+			instance.assertAuthorizedForFile( "/Dir/File6" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of isAuthorizedForService method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForService() {
+		System.out.println("isAuthorizedForService");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForService("/services/ServiceA"));
+		assertFalse(instance.isAuthorizedForService("/services/ServiceB"));
+		assertTrue(instance.isAuthorizedForService("/services/ServiceC"));
+		
+		assertFalse(instance.isAuthorizedForService("/test/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForService("/services/ServiceA"));
+		assertTrue(instance.isAuthorizedForService("/services/ServiceB"));
+		assertFalse(instance.isAuthorizedForService("/services/ServiceF"));
+		assertFalse(instance.isAuthorizedForService("/test/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForService("/services/ServiceA"));
+		assertTrue(instance.isAuthorizedForService("/services/ServiceB"));
+		assertFalse(instance.isAuthorizedForService("/services/ServiceE"));
+		assertFalse(instance.isAuthorizedForService("/test/ridiculous"));
+
+		try {
+			instance.assertAuthorizedForService("/services/ServiceD");
+			instance.assertAuthorizedForService( "/test/ridiculous" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/AccessReferenceMapTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/AccessReferenceMapTest.java.svn-base
new file mode 100644
index 0000000..ec98329
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/AccessReferenceMapTest.java.svn-base
@@ -0,0 +1,223 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The Class AccessReferenceMapTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AccessReferenceMapTest extends TestCase {
+    
+    /**
+	 * Instantiates a new access reference map test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public AccessReferenceMapTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(AccessReferenceMapTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AuthenticationException
+     *             the authentication exception
+     * @throws EncryptionException
+	 */
+    public void testUpdate() throws AuthenticationException, EncryptionException {
+        System.out.println("update");
+    	RandomAccessReferenceMap arm = new RandomAccessReferenceMap();
+    	Authenticator auth = ESAPI.authenticator();
+    	
+    	String pass = auth.generateStrongPassword();
+    	User u = auth.createUser( "armUpdate", pass, pass );
+    	
+    	// test to make sure update returns something
+		arm.update(auth.getUserNames());
+		String indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect == null ) fail();
+		
+		// test to make sure update removes items that are no longer in the list
+		auth.removeUser( u.getAccountName() );
+		arm.update(auth.getUserNames());
+		indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect != null ) fail();
+		
+		// test to make sure old indirect reference is maintained after an update
+		arm.update(auth.getUserNames());
+		String newIndirect = arm.getIndirectReference( u.getAccountName() );
+		assertEquals(indirect, newIndirect);
+    }
+    
+    
+    /**
+	 * Test of iterator method, of class org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testIterator() {
+        System.out.println("iterator");
+    	RandomAccessReferenceMap arm = new RandomAccessReferenceMap();
+        Authenticator auth = ESAPI.authenticator();
+        
+		arm.update(auth.getUserNames());
+
+		Iterator i = arm.iterator();
+		while ( i.hasNext() ) {
+			String userName = (String)i.next();
+			User u = auth.getUser( userName );
+			if ( u == null ) fail();
+		}
+    }
+    
+    /**
+	 * Test of getIndirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testGetIndirectReference() {
+        System.out.println("getIndirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String expResult = directReference;
+        String result = instance.getIndirectReference(directReference);
+        assertNotSame(expResult, result);        
+    }
+
+    /**
+	 * Test of getDirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AccessControlException
+	 *             the access control exception
+	 */
+    public void testGetDirectReference() throws AccessControlException {
+        System.out.println("getDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String ind = instance.getIndirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+        try {
+        	instance.getDirectReference("invalid");
+        	fail();
+        } catch( AccessControlException e ) {
+        	// success
+        }
+    }
+    
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testAddDirectReference() throws AccessControlException {
+        System.out.println("addDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String newDirect = instance.addDirectReference("newDirect");
+        assertNotNull( newDirect );
+        String ind = instance.addDirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+    	String newInd = instance.addDirectReference(directReference);
+    	assertEquals(ind, newInd);
+    }
+
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testRemoveDirectReference() throws AccessControlException {
+        System.out.println("removeDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String indirect = instance.getIndirectReference(directReference);
+        assertNotNull(indirect);
+        String deleted = instance.removeDirectReference(directReference);
+        assertEquals(indirect,deleted);
+    	deleted = instance.removeDirectReference("ridiculous");
+    	assertNull(deleted);
+    }
+    
+    
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/AuthenticatorTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/AuthenticatorTest.java.svn-base
new file mode 100644
index 0000000..31699cc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/AuthenticatorTest.java.svn-base
@@ -0,0 +1,617 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class AuthenticatorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticatorTest extends TestCase {
+
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AuthenticatorTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Instantiates a new authenticator test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public AuthenticatorTest(String testName) {
+		super(testName);
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	
+	/**
+	 * Test of createAccount method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+     *             the authentication exception
+     * @throws EncryptionException
+	 */
+	public void testCreateUser() throws AuthenticationException, EncryptionException {
+		System.out.println("createUser");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(accountName, password, password);
+		assertTrue(user.verifyPassword(password));
+        try {
+            instance.createUser(accountName, password, password); // duplicate user
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS), "password1", "password2"); // don't match
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS), "weak1", "weak1");  // weak password
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(null, "weak1", "weak1");  // null username
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS), null, null);  // null password
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+        	String uName = "ea234kEknr";	//sufficiently random password that also works as a username
+            instance.createUser(uName, uName, uName);  // using username as password
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+	}
+
+	/**
+	 * Test of generateStrongPassword method, of class
+	 * org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGenerateStrongPassword() throws AuthenticationException {
+		System.out.println("generateStrongPassword");		
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = "iiiiiiiiii";  // i is not allowed in passwords - this prevents failures from containing pieces of old password
+		String newPassword = null;
+		String username = "FictionalEsapiUser";
+		User user = new DefaultUser(username);
+		for (int i = 0; i < 100; i++) {
+            try {
+                newPassword = instance.generateStrongPassword();
+                instance.verifyPasswordStrength(oldPassword, newPassword, user);
+            } catch( AuthenticationException e ) {
+            	System.out.println( "  FAILED >> " + newPassword + " : " + e.getLogMessage());
+                fail();
+            }
+		}
+		try {
+			instance.verifyPasswordStrength("test56^$test", "abcdx56^$sl", user );
+		} catch( AuthenticationException e ) {
+			// expected
+		}
+	}
+
+
+	/**
+	 * Test of getCurrentUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+     *
+     * @throws Exception
+     */
+	public void testGetCurrentUser() throws Exception {
+		System.out.println("getCurrentUser");
+        Authenticator instance = ESAPI.authenticator();
+		String username1 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String username2 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user1 = instance.createUser(username1, "getCurrentUser", "getCurrentUser");
+		User user2 = instance.createUser(username2, "getCurrentUser", "getCurrentUser");		
+		user1.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		user1.loginWithPassword("getCurrentUser");
+		User currentUser = instance.getCurrentUser();
+		assertEquals( currentUser, user1 );
+		instance.setCurrentUser( user2 );
+		assertFalse( currentUser.getAccountName().equals( user2.getAccountName() ) );
+		
+		Runnable echo = new Runnable() {
+			private int count = 1;
+            private boolean result = false;
+			public void run() {
+		        Authenticator auth = ESAPI.authenticator();
+				User a = null;
+				try {
+					String password = auth.generateStrongPassword();
+					String accountName = "TestAccount" + count++;
+					a = auth.getUser(accountName);
+					if ( a != null ) {
+						auth.removeUser(accountName);
+					}
+					a = auth.createUser(accountName, password, password);
+					auth.setCurrentUser(a);
+				} catch (AuthenticationException e) {
+					e.printStackTrace();
+				}
+				User b = auth.getCurrentUser();
+				result &= a.equals(b);
+			}
+		};
+        ThreadGroup tg = new ThreadGroup("test");
+		for ( int i = 0; i<10; i++ ) {
+			new Thread( tg, echo ).start();
+		}
+        while (tg.activeCount() > 0 ) {
+            Thread.sleep(100);
+        }
+	}
+
+	/**
+	 * Test of getUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetUser() throws AuthenticationException {
+		System.out.println("getUser");
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		instance.createUser(accountName, password, password);
+		assertNotNull(instance.getUser( accountName ));
+		assertNull(instance.getUser( ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS) ));
+	}
+	
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AuthenticationException
+     */
+    public void testGetUserFromRememberToken() throws AuthenticationException {
+		System.out.println("getUserFromRememberToken");
+        Authenticator instance = ESAPI.authenticator();
+        instance.logout();  // in case anyone is logged in
+		String password = instance.generateStrongPassword();
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user = instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		
+		System.out.println("getUserFromRememberToken - expecting failure");
+		request.setCookie( HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME, "ridiculous" );
+		try {
+			instance.login( request, response );  // wrong cookie will fail
+		} catch( AuthenticationException e ) {
+			// expected
+		}
+
+		System.out.println("getUserFromRememberToken - expecting success");
+		request = new MockHttpServletRequest();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		ESAPI.authenticator().setCurrentUser(user);
+		String newToken = ESAPI.httpUtilities().setRememberToken(request, response, password, 10000, "test.com", request.getContextPath() );
+		request.setCookie( HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME, newToken );
+        user.logout();  // logout the current user so we can log them in with the remember cookie
+		User test2 = instance.login( request, response );
+		assertSame( user, test2 );
+	}
+	
+
+	
+	/**
+	 * Test get user from session.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetUserFromSession() throws AuthenticationException {
+		System.out.println("getUserFromSession");
+        FileBasedAuthenticator instance = (FileBasedAuthenticator)ESAPI.authenticator();
+        instance.logout();  // in case anyone is logged in
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", accountName);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP( request, response );
+		instance.login( request, response);
+		User test = instance.getUserFromSession();
+		assertEquals( user, test );
+	}
+
+	/**
+	 * Test get user names.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetUserNames() throws AuthenticationException {
+		System.out.println("getUserNames");
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		String[] testnames = new String[10];
+		for(int i=0;i<testnames.length;i++) {
+			testnames[i] = ESAPI.randomizer().getRandomString(8,EncoderConstants.CHAR_ALPHANUMERICS);
+		}
+		for(int i=0;i<testnames.length;i++) {
+			instance.createUser(testnames[i], password, password);
+		}
+		Set names = instance.getUserNames();
+		for(int i=0;i<testnames.length;i++) {
+			assertTrue(names.contains(testnames[i].toLowerCase()));
+		}
+	}
+	
+	/**
+	 * Test of hashPassword method, of class org.owasp.esapi.Authenticator.
+     *
+     * @throws EncryptionException
+     */
+	public void testHashPassword() throws EncryptionException {
+		System.out.println("hashPassword");
+		String username = "Jeff";
+		String password = "test";
+        Authenticator instance = ESAPI.authenticator();
+		String result1 = instance.hashPassword(password, username);
+		String result2 = instance.hashPassword(password, username);
+		assertTrue(result1.equals(result2));
+	}
+
+	/**
+	 * Test of login method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLogin() throws AuthenticationException {
+		System.out.println("login");
+        Authenticator instance = ESAPI.authenticator();
+        String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(username, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", username);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		User test = instance.login( request, response);
+		assertTrue( test.isLoggedIn() );
+	}
+	
+	/**
+	 * Test of removeAccount method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testRemoveUser() throws Exception {
+		System.out.println("removeUser");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		instance.createUser(accountName, password, password);
+		assertTrue( instance.exists(accountName));
+		instance.removeUser(accountName);
+		assertFalse( instance.exists(accountName));
+	}
+
+	/**
+	 * Test of saveUsers method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testSaveUsers() throws Exception {
+		System.out.println("saveUsers");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        FileBasedAuthenticator instance = (FileBasedAuthenticator)ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		instance.createUser(accountName, password, password);
+		instance.saveUsers();
+		assertNotNull( instance.getUser(accountName) );
+		instance.removeUser(accountName);
+		assertNull( instance.getUser(accountName) );
+	}
+
+	
+	/**
+	 * Test of setCurrentUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetCurrentUser() throws AuthenticationException {
+		System.out.println("setCurrentUser");
+        final Authenticator instance = ESAPI.authenticator();
+		String user1 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_UPPERS);
+		String user2 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_UPPERS);
+		User userOne = instance.createUser(user1, "getCurrentUser", "getCurrentUser");
+		userOne.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		userOne.loginWithPassword("getCurrentUser");
+		User currentUser = instance.getCurrentUser();
+		assertEquals( currentUser, userOne );
+		User userTwo = instance.createUser(user2, "getCurrentUser", "getCurrentUser");		
+		instance.setCurrentUser( userTwo );
+		assertFalse( currentUser.getAccountName().equals( userTwo.getAccountName() ) );
+		
+		Runnable echo = new Runnable() {
+			private int count = 1;
+			public void run() {
+				User u=null;
+				try {
+					String password = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+					u = instance.createUser("test" + count++, password, password);
+					instance.setCurrentUser(u);
+					ESAPI.getLogger("test").info( Logger.SECURITY_SUCCESS, "Got current user" );
+					// ESAPI.authenticator().removeUser( u.getAccountName() );
+				} catch (AuthenticationException e) {
+					e.printStackTrace();
+				}
+			}
+		};
+		for ( int i = 0; i<10; i++ ) {
+			new Thread( echo ).start();
+		}
+	}
+	
+
+	/**
+	 * Test of setCurrentUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetCurrentUserWithRequest() throws AuthenticationException {
+		System.out.println("setCurrentUser(req,resp)");
+        Authenticator instance = ESAPI.authenticator();
+        instance.logout();  // in case anyone is logged in
+		String password = instance.generateStrongPassword();
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		DefaultUser user = (DefaultUser) instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", accountName);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		instance.login( request, response );
+		assertEquals( user, instance.getCurrentUser() );
+		try {
+			user.disable();
+			instance.login( request, response );
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			user.enable();
+			user.lock();
+			instance.login( request, response );
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			user.unlock();
+			user.setExpirationTime( new Date() );
+			instance.login( request, response );
+		} catch( Exception e ) {
+			// expected
+		}
+	}
+	
+	
+	
+	/**
+	 * Test of validatePasswordStrength method, of class
+	 * org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testValidatePasswordStrength() throws AuthenticationException {
+		System.out.println("validatePasswordStrength");
+        Authenticator instance = ESAPI.authenticator();
+        
+        String username = "FictionalEsapiUser";
+		User user = new DefaultUser(username);
+
+		// should fail
+		try {
+			instance.verifyPasswordStrength("password", "jeff", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("diff123bang", "same123string", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "JEFF", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "1234", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "password", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "-1", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "password123", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "test123", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		//jtm - 11/16/2010 - fix for bug http://code.google.com/p/owasp-esapi-java/issues/detail?id=108
+		try {
+			instance.verifyPasswordStrength("password", "FictionalEsapiUser", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "FICTIONALESAPIUSER", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+
+		// should pass
+		instance.verifyPasswordStrength("password", "jeffJEFF12!", user);
+		instance.verifyPasswordStrength("password", "super calif ragil istic", user);
+		instance.verifyPasswordStrength("password", "TONYTONYTONYTONY", user);
+		instance.verifyPasswordStrength("password", instance.generateStrongPassword(), user);
+
+        // chrisisbeef - Issue 65 - http://code.google.com/p/owasp-esapi-java/issues/detail?id=65
+        instance.verifyPasswordStrength("password", "b!gbr0ther", user);
+	}
+
+	/**
+	 * Test of exists method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testExists() throws Exception {
+		System.out.println("exists");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		instance.createUser(accountName, password, password);
+		assertTrue(instance.exists(accountName));
+		instance.removeUser(accountName);
+		assertFalse(instance.exists(accountName));
+	}
+
+    /**
+     * Test of main method, of class org.owasp.esapi.Authenticator.
+     * @throws Exception
+     */
+    public void testMain() throws Exception {
+        System.out.println("Authenticator Main");
+        Authenticator instance = ESAPI.authenticator();
+        String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        String password = instance.generateStrongPassword();
+        String role = "test";
+        
+        // test wrong parameters - missing role parameter
+        String[] badargs = { accountName, password };
+        FileBasedAuthenticator.main( badargs );
+        // load users since the new user was added in another instance
+        ((FileBasedAuthenticator)instance).loadUsersImmediately();
+        User u1 = instance.getUser(accountName);
+        assertNull( u1 );
+
+        // test good parameters
+        String[] args = { accountName, password, role };
+        FileBasedAuthenticator.main(args);
+        // load users since the new user was added in another instance
+        ((FileBasedAuthenticator)instance).loadUsersImmediately();
+        DefaultUser u2 = (DefaultUser) instance.getUser(accountName);
+        assertNotNull( u2 );
+        assertTrue( u2.isInRole(role));
+        assertEquals( instance.hashPassword(password, accountName), ((FileBasedAuthenticator)instance).getHashedPassword(u2) );
+    }
+    
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/DefaultSecurityConfigurationTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/DefaultSecurityConfigurationTest.java.svn-base
new file mode 100644
index 0000000..4e1d1fe
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/DefaultSecurityConfigurationTest.java.svn-base
@@ -0,0 +1,390 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.ConfigurationException;
+
+public class DefaultSecurityConfigurationTest {
+
+	private DefaultSecurityConfiguration createWithProperty(String key, String val) {
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(key, val);
+		return new DefaultSecurityConfiguration(properties);
+	}
+	
+	@Test
+	public void testGetApplicationName() {
+		final String expected = "ESAPI_UnitTests";
+		DefaultSecurityConfiguration secConf = this.createWithProperty(DefaultSecurityConfiguration.APPLICATION_NAME, expected);
+		Assert.assertEquals(expected, secConf.getApplicationName());
+	}
+	
+	@Test
+	public void testGetLogImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_LOG_IMPLEMENTATION, secConf.getLogImplementation());
+		
+		final String expected = "TestLogger";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getLogImplementation());
+	}
+	
+	@Test
+	public void testAuthenticationImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_AUTHENTICATION_IMPLEMENTATION, secConf.getAuthenticationImplementation());
+		
+		final String expected = "TestAuthentication";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.AUTHENTICATION_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getAuthenticationImplementation());
+	}
+	
+	@Test
+	public void testEncoderImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCODER_IMPLEMENTATION, secConf.getEncoderImplementation());
+		
+		final String expected = "TestEncoder";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCODER_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getEncoderImplementation());
+	}
+	
+	@Test
+	public void testAccessControlImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ACCESS_CONTROL_IMPLEMENTATION, secConf.getAccessControlImplementation());
+		
+		final String expected = "TestAccessControl";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ACCESS_CONTROL_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getAccessControlImplementation());
+	}
+	
+	@Test
+	public void testEncryptionImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCRYPTION_IMPLEMENTATION, secConf.getEncryptionImplementation());
+		
+		final String expected = "TestEncryption";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCRYPTION_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getEncryptionImplementation());
+	}
+	
+	@Test
+	public void testIntrusionDetectionImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION, secConf.getIntrusionDetectionImplementation());
+		
+		final String expected = "TestIntrusionDetection";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.INTRUSION_DETECTION_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getIntrusionDetectionImplementation());
+	}
+	
+	@Test
+	public void testRandomizerImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_RANDOMIZER_IMPLEMENTATION, secConf.getRandomizerImplementation());
+		
+		final String expected = "TestRandomizer";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.RANDOMIZER_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getRandomizerImplementation());
+	}
+	
+	@Test
+	public void testExecutorImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_EXECUTOR_IMPLEMENTATION, secConf.getExecutorImplementation());
+		
+		final String expected = "TestExecutor";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.EXECUTOR_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getExecutorImplementation());
+	}
+	
+	@Test
+	public void testHTTPUtilitiesImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_HTTP_UTILITIES_IMPLEMENTATION, secConf.getHTTPUtilitiesImplementation());
+		
+		final String expected = "TestHTTPUtilities";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.HTTP_UTILITIES_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getHTTPUtilitiesImplementation());
+	}
+	
+	@Test
+	public void testValidationImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_VALIDATOR_IMPLEMENTATION, secConf.getValidationImplementation());
+		
+		final String expected = "TestValidation";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.VALIDATOR_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getValidationImplementation());
+	}
+	
+	@Test
+	public void testGetEncryptionKeyLength() {
+		// test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(128, secConf.getEncryptionKeyLength());
+		
+		final int expected = 256;
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.KEY_LENGTH, String.valueOf(expected));
+		Assert.assertEquals(expected, secConf.getEncryptionKeyLength());
+	}
+	
+	@Test
+	public void testGetKDFPseudoRandomFunction() {
+		// test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("HmacSHA256", secConf.getKDFPseudoRandomFunction());
+		
+		final String expected = "HmacSHA1";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.KDF_PRF_ALG, expected);
+		Assert.assertEquals(expected, secConf.getKDFPseudoRandomFunction());
+	}
+	
+	@Test
+	public void testGetMasterSalt() {
+		try {
+			DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+			secConf.getMasterSalt();
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+		
+		final String salt = "53081";
+		final String property = ESAPI.encoder().encodeForBase64(salt.getBytes(), false);
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.MASTER_SALT, property);
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals(salt, new String(secConf.getMasterSalt()));
+	}
+	
+	@Test
+	public void testGetAllowedExecutables() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		java.util.List<String> allowedExecutables = secConf.getAllowedExecutables();
+		
+		//is this really what should be returned? what about an empty list?
+		Assert.assertEquals(1, allowedExecutables.size());
+		Assert.assertEquals("", allowedExecutables.get(0));
+		
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.APPROVED_EXECUTABLES, String.valueOf("/bin/bzip2,/bin/diff, /bin/cvs"));
+		secConf = new DefaultSecurityConfiguration(properties);
+		allowedExecutables = secConf.getAllowedExecutables();
+		Assert.assertEquals(3, allowedExecutables.size());
+		Assert.assertEquals("/bin/bzip2", allowedExecutables.get(0));
+		Assert.assertEquals("/bin/diff", allowedExecutables.get(1));
+		
+		//this seems less than optimal, maybe each value should have a trim() done to it
+		//at least we know that this behavior exists, the property should'nt have spaces between values
+		Assert.assertEquals(" /bin/cvs", allowedExecutables.get(2));
+	}
+	
+	@Test
+	public void testGetAllowedFileExtensions() {
+		
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		java.util.List<String> allowedFileExtensions = secConf.getAllowedFileExtensions();
+		Assert.assertFalse(allowedFileExtensions.isEmpty());
+		
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.APPROVED_UPLOAD_EXTENSIONS, String.valueOf(".txt,.xml,.html,.png"));
+		secConf = new DefaultSecurityConfiguration(properties);
+		allowedFileExtensions = secConf.getAllowedFileExtensions();
+		Assert.assertEquals(4, allowedFileExtensions.size());
+		Assert.assertEquals(".html", allowedFileExtensions.get(2));
+	}
+	
+	@Test
+	public void testGetAllowedFileUploadSize() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		//assert that the default is of some reasonable size
+		Assert.assertTrue(secConf.getAllowedFileUploadSize() > (1024 * 100));
+		
+		final int expected = (1024 * 1000);
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.MAX_UPLOAD_FILE_BYTES, String.valueOf(expected));
+		Assert.assertEquals(expected, secConf.getAllowedFileUploadSize());
+	}
+	
+	@Test
+	public void testGetParameterNames() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("password", secConf.getPasswordParameterName());
+		Assert.assertEquals("username", secConf.getUsernameParameterName());
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.PASSWORD_PARAMETER_NAME, "j_password");
+		properties.setProperty(DefaultSecurityConfiguration.USERNAME_PARAMETER_NAME, "j_username");
+		secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals("j_password", secConf.getPasswordParameterName());
+		Assert.assertEquals("j_username", secConf.getUsernameParameterName());
+	}
+	
+	@Test
+	public void testGetEncryptionAlgorithm() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("AES", secConf.getEncryptionAlgorithm());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCRYPTION_ALGORITHM, "3DES");
+		Assert.assertEquals("3DES", secConf.getEncryptionAlgorithm());
+	}
+	
+	@Test
+	public void testGetCipherXProperties() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("AES/CBC/PKCS5Padding", secConf.getCipherTransformation());
+		//Assert.assertEquals("AES/CBC/PKCS5Padding", secConf.getC);
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.CIPHER_TRANSFORMATION_IMPLEMENTATION, "Blowfish/CFB/ISO10126Padding");
+		secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation());
+		
+		secConf.setCipherTransformation("DESede/PCBC/PKCS5Padding");
+		Assert.assertEquals("DESede/PCBC/PKCS5Padding", secConf.getCipherTransformation());
+		
+		secConf.setCipherTransformation(null);//sets it back to default
+		Assert.assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation());
+	}
+	
+	@Test
+	public void testIV() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("random", secConf.getIVType());
+		try {
+			secConf.getFixedIV();
+			Assert.fail();
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "fixed");
+		properties.setProperty(DefaultSecurityConfiguration.FIXED_IV, "ivValue");
+		secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals("fixed", secConf.getIVType());
+		Assert.assertEquals("ivValue", secConf.getFixedIV());
+		
+		properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "illegal");
+		secConf = new DefaultSecurityConfiguration(properties);
+		try {
+			secConf.getIVType();
+			Assert.fail();
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+		try {
+			secConf.getFixedIV();
+			Assert.fail();
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+	}
+	
+	@Test
+	public void testGetAllowMultipleEncoding() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertFalse(secConf.getAllowMultipleEncoding());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "yes");
+		Assert.assertTrue(secConf.getAllowMultipleEncoding());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "true");
+		Assert.assertTrue(secConf.getAllowMultipleEncoding());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "no");
+		Assert.assertFalse(secConf.getAllowMultipleEncoding());
+	}
+	
+	@Test
+	public void testGetDefaultCanonicalizationCodecs() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertFalse(secConf.getDefaultCanonicalizationCodecs().isEmpty());
+		
+		String property = "org.owasp.esapi.codecs.TestCodec1,org.owasp.esapi.codecs.TestCodec2";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.CANONICALIZATION_CODECS, property);
+		Assert.assertTrue(secConf.getDefaultCanonicalizationCodecs().contains("org.owasp.esapi.codecs.TestCodec1"));
+	}
+	
+	@Test
+	public void testGetDisableIntrusionDetection() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertFalse(secConf.getDisableIntrusionDetection());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "TRUE");
+		Assert.assertTrue(secConf.getDisableIntrusionDetection());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "true");
+		Assert.assertTrue(secConf.getDisableIntrusionDetection());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "false");
+		Assert.assertFalse(secConf.getDisableIntrusionDetection());
+	}
+	
+	@Test
+	public void testGetLogLevel() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(Logger.WARNING, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "trace");
+		Assert.assertEquals(Logger.TRACE, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "Off");
+		Assert.assertEquals(Logger.OFF, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "all");
+		Assert.assertEquals(Logger.ALL, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "DEBUG");
+		Assert.assertEquals(Logger.DEBUG, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "info");
+		Assert.assertEquals(Logger.INFO, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "ERROR");
+		Assert.assertEquals(Logger.ERROR, secConf.getLogLevel());
+	}
+	
+	@Test
+	public void testGetLogFileName() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("ESAPI_logging_file", secConf.getLogFileName());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_FILE_NAME, "log.txt");
+		Assert.assertEquals("log.txt", secConf.getLogFileName());
+	}
+	
+	@Test
+	public void testGetMaxLogFileSize() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_MAX_LOG_FILE_SIZE, secConf.getMaxLogFileSize());
+		
+		int maxLogSize = (1024 * 1000);
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.MAX_LOG_FILE_SIZE, String.valueOf(maxLogSize));
+		Assert.assertEquals(maxLogSize, secConf.getMaxLogFileSize());
+	}
+
+	
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/EncoderTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/EncoderTest.java.svn-base
new file mode 100644
index 0000000..52feb91
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/EncoderTest.java.svn-base
@@ -0,0 +1,802 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.MySQLCodec;
+import org.owasp.esapi.codecs.OracleCodec;
+import org.owasp.esapi.codecs.PushbackString;
+import org.owasp.esapi.codecs.UnixCodec;
+import org.owasp.esapi.codecs.WindowsCodec;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.IntrusionException;
+
+/**
+ * The Class EncoderTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncoderTest extends TestCase {
+    
+	private static final String PREFERRED_ENCODING = "UTF-8";
+	
+    /**
+	 * Instantiates a new encoder test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public EncoderTest(String testName) {
+        super(testName);
+    }
+    
+    /**
+     * {@inheritDoc}
+     * @throws Exception 
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+    
+    /**
+     * {@inheritDoc}s
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+    
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(EncoderTest.class);        
+        return suite;
+    }
+    
+	/**
+	 * Test of canonicalize method, of class org.owasp.esapi.Encoder.
+	 * 
+	 * @throws EncodingException
+	 */
+	public void testCanonicalize() throws EncodingException {
+		System.out.println("canonicalize");
+
+        ArrayList<String> list = new ArrayList<String>();
+        list.add( "HTMLEntityCodec" );
+	    list.add( "PercentCodec" );
+		Encoder instance = new DefaultEncoder( list );
+		
+		// Test null paths
+		assertEquals( null, instance.canonicalize(null));
+		assertEquals( null, instance.canonicalize(null, true));
+		assertEquals( null, instance.canonicalize(null, false));
+		assertEquals( null, instance.canonicalize(null, true, true));
+		assertEquals( null, instance.canonicalize(null, true, false));
+		assertEquals( null, instance.canonicalize(null, false, true));
+		assertEquals( null, instance.canonicalize(null, false, false));
+		
+		// test exception paths
+		assertEquals( "%", instance.canonicalize("%25", true));
+		assertEquals( "%", instance.canonicalize("%25", false));
+
+        assertEquals( "%", instance.canonicalize("%25"));
+        assertEquals( "%F", instance.canonicalize("%25F"));
+        assertEquals( "<", instance.canonicalize("%3c"));
+        assertEquals( "<", instance.canonicalize("%3C"));
+        assertEquals( "%X1", instance.canonicalize("%X1"));
+
+        assertEquals( "<", instance.canonicalize("&lt"));
+        assertEquals( "<", instance.canonicalize("&LT"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        
+        assertEquals( "%", instance.canonicalize("%"));
+        assertEquals( "%", instance.canonicalize("&#37"));
+        assertEquals( "%b", instance.canonicalize("&#37b"));
+
+        assertEquals( "<", instance.canonicalize("&#x3c"));
+        assertEquals( "<", instance.canonicalize("&#x3c;"));
+        assertEquals( "<", instance.canonicalize("&#x3C"));
+        assertEquals( "<", instance.canonicalize("&#X3c"));
+        assertEquals( "<", instance.canonicalize("&#X3C"));
+        assertEquals( "<", instance.canonicalize("&#X3C;"));
+
+        // percent encoding
+        assertEquals( "<", instance.canonicalize("%3c"));
+        assertEquals( "<", instance.canonicalize("%3C"));
+
+        // html entity encoding
+        assertEquals( "<", instance.canonicalize("&#60"));
+        assertEquals( "<", instance.canonicalize("&#060"));
+        assertEquals( "<", instance.canonicalize("&#0060"));
+        assertEquals( "<", instance.canonicalize("&#00060"));
+        assertEquals( "<", instance.canonicalize("&#000060"));
+        assertEquals( "<", instance.canonicalize("&#0000060"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("&#x3c"));
+        assertEquals( "<", instance.canonicalize("&#x03c"));
+        assertEquals( "<", instance.canonicalize("&#x003c"));
+        assertEquals( "<", instance.canonicalize("&#x0003c"));
+        assertEquals( "<", instance.canonicalize("&#x00003c"));
+        assertEquals( "<", instance.canonicalize("&#x000003c"));
+        assertEquals( "<", instance.canonicalize("&#x3c;"));
+        assertEquals( "<", instance.canonicalize("&#x03c;"));
+        assertEquals( "<", instance.canonicalize("&#x003c;"));
+        assertEquals( "<", instance.canonicalize("&#x0003c;"));
+        assertEquals( "<", instance.canonicalize("&#x00003c;"));
+        assertEquals( "<", instance.canonicalize("&#x000003c;"));
+        assertEquals( "<", instance.canonicalize("&#X3c"));
+        assertEquals( "<", instance.canonicalize("&#X03c"));
+        assertEquals( "<", instance.canonicalize("&#X003c"));
+        assertEquals( "<", instance.canonicalize("&#X0003c"));
+        assertEquals( "<", instance.canonicalize("&#X00003c"));
+        assertEquals( "<", instance.canonicalize("&#X000003c"));
+        assertEquals( "<", instance.canonicalize("&#X3c;"));
+        assertEquals( "<", instance.canonicalize("&#X03c;"));
+        assertEquals( "<", instance.canonicalize("&#X003c;"));
+        assertEquals( "<", instance.canonicalize("&#X0003c;"));
+        assertEquals( "<", instance.canonicalize("&#X00003c;"));
+        assertEquals( "<", instance.canonicalize("&#X000003c;"));
+        assertEquals( "<", instance.canonicalize("&#x3C"));
+        assertEquals( "<", instance.canonicalize("&#x03C"));
+        assertEquals( "<", instance.canonicalize("&#x003C"));
+        assertEquals( "<", instance.canonicalize("&#x0003C"));
+        assertEquals( "<", instance.canonicalize("&#x00003C"));
+        assertEquals( "<", instance.canonicalize("&#x000003C"));
+        assertEquals( "<", instance.canonicalize("&#x3C;"));
+        assertEquals( "<", instance.canonicalize("&#x03C;"));
+        assertEquals( "<", instance.canonicalize("&#x003C;"));
+        assertEquals( "<", instance.canonicalize("&#x0003C;"));
+        assertEquals( "<", instance.canonicalize("&#x00003C;"));
+        assertEquals( "<", instance.canonicalize("&#x000003C;"));
+        assertEquals( "<", instance.canonicalize("&#X3C"));
+        assertEquals( "<", instance.canonicalize("&#X03C"));
+        assertEquals( "<", instance.canonicalize("&#X003C"));
+        assertEquals( "<", instance.canonicalize("&#X0003C"));
+        assertEquals( "<", instance.canonicalize("&#X00003C"));
+        assertEquals( "<", instance.canonicalize("&#X000003C"));
+        assertEquals( "<", instance.canonicalize("&#X3C;"));
+        assertEquals( "<", instance.canonicalize("&#X03C;"));
+        assertEquals( "<", instance.canonicalize("&#X003C;"));
+        assertEquals( "<", instance.canonicalize("&#X0003C;"));
+        assertEquals( "<", instance.canonicalize("&#X00003C;"));
+        assertEquals( "<", instance.canonicalize("&#X000003C;"));
+        assertEquals( "<", instance.canonicalize("&lt"));
+        assertEquals( "<", instance.canonicalize("&lT"));
+        assertEquals( "<", instance.canonicalize("&Lt"));
+        assertEquals( "<", instance.canonicalize("&LT"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("&lT;"));
+        assertEquals( "<", instance.canonicalize("≪"));
+        assertEquals( "<", instance.canonicalize("<"));
+        
+        assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript%3Ealert%28%22hello%22%29%3B%3C%2Fscript%3E") );
+        assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript&#x3E;alert%28%22hello&#34%29%3B%3C%2Fscript%3E", false) );
+        
+        // javascript escape syntax
+        ArrayList<String> js = new ArrayList<String>();
+        js.add( "JavaScriptCodec" );
+        instance = new DefaultEncoder( js );
+        System.out.println( "JavaScript Decoding" );
+
+        assertEquals( "\0", instance.canonicalize("\\0"));
+        assertEquals( "\b", instance.canonicalize("\\b"));
+        assertEquals( "\t", instance.canonicalize("\\t"));
+        assertEquals( "\n", instance.canonicalize("\\n"));
+        assertEquals( ""+(char)0x0b, instance.canonicalize("\\v"));
+        assertEquals( "\f", instance.canonicalize("\\f"));
+        assertEquals( "\r", instance.canonicalize("\\r"));
+        assertEquals( "\'", instance.canonicalize("\\'"));
+        assertEquals( "\"", instance.canonicalize("\\\""));
+        assertEquals( "\\", instance.canonicalize("\\\\"));
+        assertEquals( "<", instance.canonicalize("\\<"));
+        
+        assertEquals( "<", instance.canonicalize("\\u003c"));
+        assertEquals( "<", instance.canonicalize("\\U003c"));
+        assertEquals( "<", instance.canonicalize("\\u003C"));
+        assertEquals( "<", instance.canonicalize("\\U003C"));
+        assertEquals( "<", instance.canonicalize("\\x3c"));
+        assertEquals( "<", instance.canonicalize("\\X3c"));
+        assertEquals( "<", instance.canonicalize("\\x3C"));
+        assertEquals( "<", instance.canonicalize("\\X3C"));
+
+        // css escape syntax
+        // be careful because some codecs see \0 as null byte
+        ArrayList<String> css = new ArrayList<String>();
+        css.add( "CSSCodec" );
+        instance = new DefaultEncoder( css );
+        System.out.println( "CSS Decoding" );
+        assertEquals( "<", instance.canonicalize("\\3c"));  // add strings to prevent null byte
+        assertEquals( "<", instance.canonicalize("\\03c"));
+        assertEquals( "<", instance.canonicalize("\\003c"));
+        assertEquals( "<", instance.canonicalize("\\0003c"));
+        assertEquals( "<", instance.canonicalize("\\00003c"));
+        assertEquals( "<", instance.canonicalize("\\3C"));
+        assertEquals( "<", instance.canonicalize("\\03C"));
+        assertEquals( "<", instance.canonicalize("\\003C"));
+        assertEquals( "<", instance.canonicalize("\\0003C"));
+        assertEquals( "<", instance.canonicalize("\\00003C"));
+	}
+
+	
+    /**
+     * Test of canonicalize method, of class org.owasp.esapi.Encoder.
+     * 
+     * @throws EncodingException
+     */
+    public void testDoubleEncodingCanonicalization() throws EncodingException {
+        System.out.println("doubleEncodingCanonicalization");
+        Encoder instance = ESAPI.encoder();
+
+        // note these examples use the strict=false flag on canonicalize to allow
+        // full decoding without throwing an IntrusionException. Generally, you
+        // should use strict mode as allowing double-encoding is an abomination.
+        
+        // double encoding examples
+        assertEquals( "<", instance.canonicalize("&#x26;lt&#59", false )); //double entity
+        assertEquals( "\\", instance.canonicalize("%255c", false)); //double percent
+        assertEquals( "%", instance.canonicalize("%2525", false)); //double percent
+        
+        // double encoding with multiple schemes example
+        assertEquals( "<", instance.canonicalize("%26lt%3b", false)); //first entity, then percent
+        assertEquals( "&", instance.canonicalize("&#x25;26", false)); //first percent, then entity
+
+		//enforce multiple and mixed encoding detection
+		try {
+			instance.canonicalize("%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B", true, true);
+			fail("Multiple and mixed encoding not detected");
+		} catch (IntrusionException ie) {}
+
+		//enforce multiple but not mixed encoding detection
+		try {
+			instance.canonicalize("%252525253C", true, false); 
+			fail("Multiple encoding not detected");
+		} catch (IntrusionException ie) {}
+
+		//enforce mixed but not multiple encoding detection
+		try {
+			instance.canonicalize("%25 %2526 %26#X3c;script&#x3e; %3Cscript%25252525253e", false, true); 
+			fail("Mixed encoding not detected");
+		} catch (IntrusionException ie) {}
+
+		//enforce niether mixed nor multiple encoding detection -should canonicalize but not throw an error
+		assertEquals( "< < < < < < <", instance.canonicalize("%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B", 
+															 false, false)); 
+
+        // nested encoding examples
+        assertEquals( "<", instance.canonicalize("%253c", false)); //nested encode % with percent
+        assertEquals( "<", instance.canonicalize("%%33%63", false)); //nested encode both nibbles with percent
+        assertEquals( "<", instance.canonicalize("%%33c", false)); // nested encode first nibble with percent
+        assertEquals( "<", instance.canonicalize("%3%63", false));  //nested encode second nibble with percent
+        assertEquals( "<", instance.canonicalize("&lt;", false)); //nested encode l with entity
+        assertEquals( "<", instance.canonicalize("%2&#x35;3c", false)); //triple percent, percent, 5 with entity
+        
+        // nested encoding with multiple schemes examples
+        assertEquals( "<", instance.canonicalize("&%6ct;", false)); // nested encode l with percent
+        assertEquals( "<", instance.canonicalize("%&#x33;c", false)); //nested encode 3 with entity            
+        
+        // multiple encoding tests
+        assertEquals( "% & <script> <script>", instance.canonicalize( "%25 %2526 %26#X3c;script&#x3e; %3Cscript%25252525253e", false ) );
+        assertEquals( "< < < < < < <", instance.canonicalize( "%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B", false ) );
+
+        // test strict mode with both mixed and multiple encoding
+        try {
+            assertEquals( "< < < < < < <", instance.canonicalize( "%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B" ) );
+        } catch( IntrusionException e ) {
+            // expected
+        }
+        
+        try {
+            assertEquals( "<script", instance.canonicalize("%253Cscript" ) );
+        } catch( IntrusionException e ) {
+            // expected
+        }
+        try {
+            assertEquals( "<script", instance.canonicalize("%3Cscript" ) );
+        } catch( IntrusionException e ) {
+            // expected
+        }
+    }
+
+    /**
+	 * Test of encodeForHTML method, of class org.owasp.esapi.Encoder.
+     *
+     * @throws Exception
+     */
+    public void testEncodeForHTML() throws Exception {
+        System.out.println("encodeForHTML");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForHTML(null));
+        // test invalid characters are replaced with spaces
+        assertEquals("a&#xfffd;b&#xfffd;c&#xfffd;d&#xfffd;e&#xfffd;f&#x9;g", instance.encodeForHTML("a" + (char)0 + "b" + (char)4 + "c" + (char)128 + "d" + (char)150 + "e" +(char)159 + "f" + (char)9 + "g"));
+        
+        assertEquals("<script>", instance.encodeForHTML("<script>"));
+        assertEquals("&lt&#x3b;script&gt&#x3b;", instance.encodeForHTML("<script>"));
+        assertEquals("&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForHTML("!@$%()=+{}[]"));
+        assertEquals("&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForHTML(instance.canonicalize("!@$%()=+{}[]") ) );
+        assertEquals(",.-_ ", instance.encodeForHTML(",.-_ "));
+        assertEquals("dir&", instance.encodeForHTML("dir&"));
+        assertEquals("one&two", instance.encodeForHTML("one&two"));
+        assertEquals("" + (char)12345 + (char)65533 + (char)1244, "" + (char)12345 + (char)65533 + (char)1244 );
+    }
+    
+    /**
+	 * Test of encodeForHTMLAttribute method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForHTMLAttribute() {
+        System.out.println("encodeForHTMLAttribute");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForHTMLAttribute(null));
+        assertEquals("<script>", instance.encodeForHTMLAttribute("<script>"));
+        assertEquals(",.-_", instance.encodeForHTMLAttribute(",.-_"));
+        assertEquals("&#x20;&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForHTMLAttribute(" !@$%()=+{}[]"));
+    }
+    
+    
+    /**
+     *
+     */
+    public void testEncodeForCSS() {
+        System.out.println("encodeForCSS");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForCSS(null));
+        assertEquals("\\3c script\\3e ", instance.encodeForCSS("<script>"));
+        assertEquals("\\21 \\40 \\24 \\25 \\28 \\29 \\3d \\2b \\7b \\7d \\5b \\5d ", instance.encodeForCSS("!@$%()=+{}[]"));
+    }
+    
+
+    
+    /**
+	 * Test of encodeForJavaScript method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForJavascript() {
+        System.out.println("encodeForJavascript");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForJavaScript(null));
+        assertEquals("\\x3Cscript\\x3E", instance.encodeForJavaScript("<script>"));
+        assertEquals(",.\\x2D_\\x20", instance.encodeForJavaScript(",.-_ "));
+        assertEquals("\\x21\\x40\\x24\\x25\\x28\\x29\\x3D\\x2B\\x7B\\x7D\\x5B\\x5D", instance.encodeForJavaScript("!@$%()=+{}[]"));
+        // assertEquals( "\\0", instance.encodeForJavaScript("\0"));
+        // assertEquals( "\\b", instance.encodeForJavaScript("\b"));
+        // assertEquals( "\\t", instance.encodeForJavaScript("\t"));
+        // assertEquals( "\\n", instance.encodeForJavaScript("\n"));
+        // assertEquals( "\\v", instance.encodeForJavaScript("" + (char)0x0b));
+        // assertEquals( "\\f", instance.encodeForJavaScript("\f"));
+        // assertEquals( "\\r", instance.encodeForJavaScript("\r"));
+        // assertEquals( "\\'", instance.encodeForJavaScript("\'"));
+        // assertEquals( "\\\"", instance.encodeForJavaScript("\""));
+        // assertEquals( "\\\\", instance.encodeForJavaScript("\\"));
+    }
+        
+    /**
+     *
+     */
+    public void testEncodeForVBScript() {
+        System.out.println("encodeForVBScript");        
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForVBScript(null));
+        assertEquals( "chrw(60)&\"script\"&chrw(62)", instance.encodeForVBScript("<script>"));
+        assertEquals( "x\"&chrw(32)&chrw(33)&chrw(64)&chrw(36)&chrw(37)&chrw(40)&chrw(41)&chrw(61)&chrw(43)&chrw(123)&chrw(125)&chrw(91)&chrw(93)", instance.encodeForVBScript("x !@$%()=+{}[]"));
+        assertEquals( "alert\"&chrw(40)&chrw(39)&\"ESAPI\"&chrw(32)&\"test\"&chrw(33)&chrw(39)&chrw(41)", instance.encodeForVBScript("alert('ESAPI test!')" ));
+        assertEquals( "jeff.williams\"&chrw(64)&\"aspectsecurity.com", instance.encodeForVBScript("jeff.williams at aspectsecurity.com"));
+        assertEquals( "test\"&chrw(32)&chrw(60)&chrw(62)&chrw(32)&\"test", instance.encodeForVBScript("test <> test" ));
+    }
+
+        
+    /**
+	 * Test of encodeForXPath method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForXPath() {
+        System.out.println("encodeForXPath");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForXPath(null));
+        assertEquals("&#x27;or 1&#x3d;1", instance.encodeForXPath("'or 1=1"));
+    }
+    
+
+    
+    /**
+	 * Test of encodeForSQL method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForSQL() {
+        System.out.println("encodeForSQL");
+        Encoder instance = ESAPI.encoder();
+
+        Codec mySQL1 = new MySQLCodec( MySQLCodec.ANSI_MODE );
+        assertEquals("ANSI_MODE", null, instance.encodeForSQL(mySQL1, null));
+        assertEquals("ANSI_MODE", "Jeff'' or ''1''=''1", instance.encodeForSQL(mySQL1, "Jeff' or '1'='1"));
+        
+        Codec mySQL2 = new MySQLCodec( MySQLCodec.MYSQL_MODE );
+        assertEquals("MYSQL_MODE", null, instance.encodeForSQL(mySQL2, null));
+        assertEquals("MYSQL_MODE", "Jeff\\' or \\'1\\'\\=\\'1", instance.encodeForSQL(mySQL2, "Jeff' or '1'='1"));
+
+        Codec oracle = new OracleCodec();
+        assertEquals("Oracle", null, instance.encodeForSQL(oracle, null));
+        assertEquals("Oracle", "Jeff'' or ''1''=''1", instance.encodeForSQL(oracle, "Jeff' or '1'='1"));
+    }
+
+    public void testMySQLANSIModeQuoteInjection() {
+        Encoder instance = ESAPI.encoder();
+        Codec c = new MySQLCodec(MySQLCodec.Mode.ANSI);
+        assertEquals("MySQL Ansi Quote Injection Bug", " or 1=1 -- -", instance.encodeForSQL(c, "\" or 1=1 -- -"));
+    }
+
+    
+    /**
+	 * Test of encodeForLDAP method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForLDAP() {
+        System.out.println("encodeForLDAP");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForLDAP(null));
+        assertEquals("No special characters to escape", "Hi This is a test #��", instance.encodeForLDAP("Hi This is a test #��"));
+        assertEquals("Zeros", "Hi \\00", instance.encodeForLDAP("Hi \u0000"));
+        assertEquals("LDAP Christams Tree", "Hi \\28This\\29 = is \\2a a \\5c test # � � �", instance.encodeForLDAP("Hi (This) = is * a \\ test # � � �"));
+    }
+    
+    /**
+	 * Test of encodeForLDAP method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForDN() {
+        System.out.println("encodeForDN");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForDN(null));
+        assertEquals("No special characters to escape", "Hello�", instance.encodeForDN("Hello�"));
+        assertEquals("leading #", "\\# Hello�", instance.encodeForDN("# Hello�"));
+        assertEquals("leading space", "\\ Hello�", instance.encodeForDN(" Hello�"));
+        assertEquals("trailing space", "Hello�\\ ", instance.encodeForDN("Hello� "));
+        assertEquals("less than greater than", "Hello\\<\\>", instance.encodeForDN("Hello<>"));
+        assertEquals("only 3 spaces", "\\  \\ ", instance.encodeForDN("   "));
+        assertEquals("Christmas Tree DN", "\\ Hello\\\\ \\+ \\, \\\"World\\\" \\;\\ ", instance.encodeForDN(" Hello\\ + , \"World\" ; "));
+    }
+    
+    public void testEncodeForXMLNull() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForXML(null));
+    }
+
+    public void testEncodeForXMLSpace() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(" ", instance.encodeForXML(" "));
+    }
+
+    public void testEncodeForXMLScript() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#x3c;script&#x3e;", instance.encodeForXML("<script>"));
+    }
+
+    public void testEncodeForXMLImmune() {
+        System.out.println("encodeForXML");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(",.-_", instance.encodeForXML(",.-_"));
+    }
+    
+    public void testEncodeForXMLSymbol() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForXML("!@$%()=+{}[]"));
+    }
+
+    public void testEncodeForXMLPound() {
+        System.out.println("encodeForXML");
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#xa3;", instance.encodeForXML("\u00A3"));
+    }
+    
+    public void testEncodeForXMLAttributeNull() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForXMLAttribute(null));
+    }
+    
+    public void testEncodeForXMLAttributeSpace() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(" ", instance.encodeForXMLAttribute(" "));
+    }
+    
+    public void testEncodeForXMLAttributeScript() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#x3c;script&#x3e;", instance.encodeForXMLAttribute("<script>"));
+    }
+    
+    public void testEncodeForXMLAttributeImmune() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(",.-_", instance.encodeForXMLAttribute(",.-_"));
+    }
+    
+    public void testEncodeForXMLAttributeSymbol() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(" &#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForXMLAttribute(" !@$%()=+{}[]"));
+    }
+    
+    public void testEncodeForXMLAttributePound() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#xa3;", instance.encodeForXMLAttribute("\u00A3"));
+    }
+    
+    /**
+	 * Test of encodeForURL method, of class org.owasp.esapi.Encoder.
+     *
+     * @throws Exception
+     */
+    public void testEncodeForURL() throws Exception {
+        System.out.println("encodeForURL");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForURL(null));
+        assertEquals("%3Cscript%3E", instance.encodeForURL("<script>"));
+    }
+    
+    /**
+	 * Test of decodeFromURL method, of class org.owasp.esapi.Encoder.
+     *
+     * @throws Exception
+     */
+    public void testDecodeFromURL() throws Exception {
+        System.out.println("decodeFromURL");
+        Encoder instance = ESAPI.encoder();
+        try {
+        	assertEquals(null, instance.decodeFromURL(null));
+            assertEquals("<script>", instance.decodeFromURL("%3Cscript%3E"));
+            assertEquals("     ", instance.decodeFromURL("+++++") );
+        } catch ( Exception e ) {
+            fail();
+        }
+        try {
+        	instance.decodeFromURL( "%3xridiculous" );
+        	fail();
+        } catch( Exception e ) {
+        	// expected
+        }
+    }
+    
+    /**
+	 * Test of encodeForBase64 method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForBase64() {
+        System.out.println("encodeForBase64");
+        Encoder instance = ESAPI.encoder();
+        
+        try {
+        	assertEquals(null, instance.encodeForBase64(null, false));
+            assertEquals(null, instance.encodeForBase64(null, true));
+            assertEquals(null, instance.decodeFromBase64(null));
+            for ( int i=0; i < 100; i++ ) {
+                byte[] r = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS ).getBytes(PREFERRED_ENCODING);
+                String encoded = instance.encodeForBase64( r, ESAPI.randomizer().getRandomBoolean() );
+                byte[] decoded = instance.decodeFromBase64( encoded );
+                assertTrue( Arrays.equals( r, decoded ) );
+            }
+        } catch ( IOException e ) {
+            fail();
+        }
+    }
+    
+    /**
+	 * Test of decodeFromBase64 method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testDecodeFromBase64() {
+        System.out.println("decodeFromBase64");
+        Encoder instance = ESAPI.encoder();
+        for ( int i=0; i < 100; i++ ) {
+            try {
+                byte[] r = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS ).getBytes(PREFERRED_ENCODING);
+                String encoded = instance.encodeForBase64( r, ESAPI.randomizer().getRandomBoolean() );
+                byte[] decoded = instance.decodeFromBase64( encoded );
+                assertTrue( Arrays.equals( r, decoded ) );
+            } catch ( IOException e ) {
+                fail();
+	        }
+        }
+        for ( int i=0; i < 100; i++ ) {
+            try {
+                byte[] r = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS ).getBytes(PREFERRED_ENCODING);
+                String encoded = ESAPI.randomizer().getRandomString(1, EncoderConstants.CHAR_ALPHANUMERICS) + instance.encodeForBase64( r, ESAPI.randomizer().getRandomBoolean() );
+	            byte[] decoded = instance.decodeFromBase64( encoded );
+	            assertFalse( Arrays.equals(r, decoded) );
+            } catch( UnsupportedEncodingException ex) {
+            	fail();
+            } catch ( IOException e ) {
+            	// expected
+            }
+        }
+    }
+    
+
+    /**
+	 * Test of WindowsCodec
+	 */
+    public void testWindowsCodec() {
+        System.out.println("WindowsCodec");
+        Encoder instance = ESAPI.encoder();
+
+        Codec win = new WindowsCodec();
+        char[] immune = new char[0];
+        assertEquals(null, instance.encodeForOS(win, null));
+        
+        PushbackString npbs = new PushbackString("n");
+        assertEquals(null, win.decodeCharacter(npbs));
+
+        PushbackString epbs = new PushbackString("");
+        assertEquals(null, win.decodeCharacter(epbs));
+        
+        Character c = Character.valueOf('<');
+        PushbackString cpbs = new PushbackString(win.encodeCharacter(immune, c));
+        Character decoded = win.decodeCharacter(cpbs);
+        assertEquals(c, decoded);
+        
+        String orig = "c:\\jeff";
+        String enc = win.encode(EncoderConstants.CHAR_ALPHANUMERICS, orig);
+        assertEquals(orig, win.decode(enc));
+        assertEquals(orig, win.decode(orig));
+        
+     // TODO: Check that these are acceptable for Windows
+        assertEquals("c^:^\\jeff", instance.encodeForOS(win, "c:\\jeff"));		
+        assertEquals("c^:^\\jeff", win.encode(immune, "c:\\jeff"));
+        assertEquals("dir^ ^&^ foo", instance.encodeForOS(win, "dir & foo"));
+        assertEquals("dir^ ^&^ foo", win.encode(immune, "dir & foo"));
+    }
+
+    /**
+	 * Test of UnixCodec
+	 */
+    public void testUnixCodec() {
+        System.out.println("UnixCodec");
+        Encoder instance = ESAPI.encoder();
+
+        Codec unix = new UnixCodec();
+        char[] immune = new char[0];
+        assertEquals(null, instance.encodeForOS(unix, null));
+        
+        PushbackString npbs = new PushbackString("n");
+        assertEquals(null, unix.decodeCharacter(npbs));
+
+        Character c = Character.valueOf('<');
+        PushbackString cpbs = new PushbackString(unix.encodeCharacter(immune, c));
+        Character decoded = unix.decodeCharacter(cpbs);
+        assertEquals(c, decoded);
+        
+        PushbackString epbs = new PushbackString("");
+        assertEquals(null, unix.decodeCharacter(epbs));
+
+        String orig = "/etc/passwd";
+        String enc = unix.encode(immune, orig);
+        assertEquals(orig, unix.decode(enc));
+        assertEquals(orig, unix.decode(orig));
+        
+     // TODO: Check that these are acceptable for Unix hosts
+        assertEquals("c\\:\\\\jeff", instance.encodeForOS(unix, "c:\\jeff"));
+        assertEquals("c\\:\\\\jeff", unix.encode(immune, "c:\\jeff"));
+        assertEquals("dir\\ \\&\\ foo", instance.encodeForOS(unix, "dir & foo"));
+        assertEquals("dir\\ \\&\\ foo", unix.encode(immune, "dir & foo"));
+
+        // Unix paths (that must be encoded safely)
+        // TODO: Check that these are acceptable for Unix
+        assertEquals("\\/etc\\/hosts", instance.encodeForOS(unix, "/etc/hosts"));
+        assertEquals("\\/etc\\/hosts\\;\\ ls\\ -l", instance.encodeForOS(unix, "/etc/hosts; ls -l"));
+    }
+    
+    public void testCanonicalizePerformance() throws Exception {
+        System.out.println("Canonicalization Performance");
+    	Encoder encoder = ESAPI.encoder();
+    	int iterations = 100;
+    	String normal = "The quick brown fox jumped over the lazy dog";
+    	
+    	long start = System.currentTimeMillis();
+    	String temp = null;		// Trade in 1/2 doz warnings in Eclipse for one (never read)
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = normal;
+        }
+    	long stop = System.currentTimeMillis();
+        System.out.println( "Normal: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = encoder.canonicalize( normal, false );
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Normal Loose: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = encoder.canonicalize( normal, true );
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Normal Strict: " + (stop-start) );
+
+    	String attack = "%2&#x35;2%3525&#x32;\\u0036lt;\r\n\r\n%&#x%%%3333\\u0033;&%23101;";
+    	
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = attack;
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Attack: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = encoder.canonicalize( attack, false );
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Attack Loose: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	try {
+        		temp = encoder.canonicalize( attack, true );
+        	} catch( IntrusionException e ) { 
+        		// expected
+        	}
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Attack Strict: " + (stop-start) );
+    }
+    
+
+    public void testConcurrency() {
+        System.out.println("Encoder Concurrency");
+		for (int i = 0; i < 10; i++) {
+			new Thread( new EncoderConcurrencyMock( i )).start();
+		}
+	}
+
+    /**
+     *  A simple class that calls the Encoder to test thread safety
+     */
+    public class EncoderConcurrencyMock implements Runnable {
+    	public int num = 0;
+    	public EncoderConcurrencyMock( int num ) {
+    		this.num = num;
+    	}
+	    public void run() {
+			while( true ) {
+				String nonce = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS );
+				String result = javaScriptEncode( nonce );
+				// randomize the threads
+				try {
+					Thread.sleep( ESAPI.randomizer().getRandomInteger( 100, 500 ) );
+				} catch (InterruptedException e) {
+					// just continue
+				}
+				assertTrue( result.equals ( javaScriptEncode( nonce ) ) );
+			}
+		}
+		
+	    public String javaScriptEncode(String str) {
+			Encoder encoder = DefaultEncoder.getInstance();
+			return encoder.encodeForJavaScript(str);
+		}
+    }
+    
+}
+
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/ExecutorTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/ExecutorTest.java.svn-base
new file mode 100644
index 0000000..f97a81c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/ExecutorTest.java.svn-base
@@ -0,0 +1,269 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.ExecuteResult;
+import org.owasp.esapi.Executor;
+import org.owasp.esapi.SecurityConfiguration;
+import org.owasp.esapi.SecurityConfigurationWrapper;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.UnixCodec;
+import org.owasp.esapi.codecs.WindowsCodec;
+
+/**
+ * The Class ExecutorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ExecutorTest extends TestCase {
+
+	private SecurityConfiguration origConfig;
+
+	private static class Conf extends SecurityConfigurationWrapper
+	{
+		private final List allowedExes;
+		private final File workingDir;
+
+		Conf(SecurityConfiguration orig, List allowedExes, File workingDir)
+		{
+			super(orig);
+			this.allowedExes = allowedExes;
+			this.workingDir = workingDir;
+		}
+
+		@Override
+		public List getAllowedExecutables()
+		{
+			return allowedExes;
+		}
+
+		@Override
+		public File getWorkingDirectory()
+		{
+			return workingDir;
+		}
+	}
+
+	/**
+	 * Instantiates a new executor test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public ExecutorTest(String testName) {
+		super(testName);
+	}
+
+    @Override
+    protected void tearDown() throws Exception {
+        ESAPI.override(null);
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ExecutorTest.class);
+		return suite;
+	}
+
+	/**
+	 * Test of executeOSCommand method, of class org.owasp.esapi.Executor
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testExecuteWindowsSystemCommand() throws Exception {
+		System.out.println("executeWindowsSystemCommand");
+
+		if ( System.getProperty("os.name").indexOf("Windows") == -1 ) {
+			System.out.println("testExecuteWindowsSystemCommand - on non-Windows platform, exiting");
+			return;	// Not windows, not going to execute this path
+		}
+		File tmpDir = new File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
+		File sysRoot = new File(System.getenv("SystemRoot")).getCanonicalFile();
+		File sys32 = new File(sysRoot,"system32").getCanonicalFile();
+		File cmd = new File(sys32,"cmd.exe").getCanonicalFile();
+		ESAPI.override(
+			new Conf(
+				ESAPI.securityConfiguration(),
+				Collections.singletonList(cmd.getPath()),
+				tmpDir
+			)
+		);
+
+		Codec codec = new WindowsCodec();
+		System.out.println("executeSystemCommand");
+		Executor instance = ESAPI.executor();
+		List params = new ArrayList();
+		try {
+			params.add("/C");
+			params.add("dir");
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			assertTrue(result.getOutput().length() > 0);
+		} catch (Exception e) {
+			e.printStackTrace();
+			fail();
+		}
+		try {
+			File exec2 = new File( cmd.getPath() + ";inject.exe" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			File exec2 = new File( cmd.getPath() + "\\..\\cmd.exe" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			File workdir = new File( "c:\\ridiculous" );
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params), workdir, codec, false, false );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			params.add("&dir");
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+
+		try {
+			params.set( params.size()-1, "c:\\autoexec.bat" );
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+
+		try {
+			params.set( params.size()-1, "c:\\autoexec.bat c:\\config.sys" );
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+	}
+
+	/**
+	 * Test of executeOSCommand method, of class org.owasp.esapi.Executor
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testExecuteUnixSystemCommand() throws Exception {
+		System.out.println("executeUnixSystemCommand");
+
+		if ( System.getProperty("os.name").indexOf("Windows") != -1 ) {
+			System.out.println("executeUnixSystemCommand - on Windows platform, exiting");
+			return;
+		}
+
+		// FIXME: need more test cases to use this codec
+		Codec codec = new UnixCodec();
+
+		// make sure we have what /bin/sh is pointing at in the allowed exes for the test
+		// and a usable working dir
+		File binSh = new File("/bin/sh").getCanonicalFile();
+		ESAPI.override(
+			new Conf(
+				ESAPI.securityConfiguration(),
+				Collections.singletonList(binSh.getPath()),
+				new File("/tmp")
+			)
+		);
+
+		Executor instance = ESAPI.executor();
+		File executable = binSh;
+		List params = new ArrayList();
+		try {
+			params.add("-c");
+			params.add("ls");
+			params.add("/");
+			ExecuteResult result = instance.executeSystemCommand(executable, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			assertTrue(result.getOutput().length() > 0);
+		} catch (Exception e) {
+			fail(e.getMessage());
+		}
+		try {
+			File exec2 = new File( executable.getPath() + ";./inject" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			File exec2 = new File( executable.getPath() + "/../bin/sh" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			params.add(";ls");
+			ExecuteResult result = instance.executeSystemCommand(executable, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+
+		try {
+			File cwd = new File(".");
+			File script = File.createTempFile("ESAPI-ExecutorTest", "sh", cwd);
+			script.deleteOnExit();
+			FileWriter output = new FileWriter(script);
+			try {
+				output.write("i=0\nwhile [ $i -lt 8192 ]\ndo\necho stdout data\necho stderr data >&2\ni=$((i+1))\ndone\n");
+			} finally {
+				output.close();
+			}
+			List deadlockParams = new ArrayList();
+			deadlockParams.add(script.getName());
+			ExecuteResult result = instance.executeSystemCommand(executable, deadlockParams, cwd, codec, true, false);
+			System.out.println( "RESULT: " + result.getExitValue() );
+			assertEquals(0, result.getExitValue());
+		} catch (Exception e) {
+			fail();
+		}
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/HTTPUtilitiesTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/HTTPUtilitiesTest.java.svn-base
new file mode 100644
index 0000000..40f9e21
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/HTTPUtilitiesTest.java.svn-base
@@ -0,0 +1,507 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.User;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+import org.owasp.esapi.http.MockHttpSession;
+import org.owasp.esapi.util.FileTestUtils;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * The Class HTTPUtilitiesTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class HTTPUtilitiesTest extends TestCase
+{
+	private static final Class<HTTPUtilitiesTest> CLASS = HTTPUtilitiesTest.class;
+	private static final String CLASS_NAME = CLASS.getName();
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		return new TestSuite(HTTPUtilitiesTest.class);
+	}
+
+	/**
+	 * Instantiates a new HTTP utilities test.
+	 * 
+	 * @param testName the test name
+	 */
+	public HTTPUtilitiesTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @throws Exception
+	 */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @throws Exception
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	public void testCSRFToken() throws Exception {
+		System.out.println( "CSRFToken");
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user = ESAPI.authenticator().createUser(username, "addCSRFToken", "addCSRFToken");
+		ESAPI.authenticator().setCurrentUser( user );
+		String token = ESAPI.httpUtilities().getCSRFToken();
+		assertEquals( 8, token.length() );
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		try {
+			ESAPI.httpUtilities().verifyCSRFToken(request);
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+		request.addParameter( DefaultHTTPUtilities.CSRF_TOKEN_NAME, token );
+		ESAPI.httpUtilities().verifyCSRFToken(request);
+	}
+
+	/**
+	 * Test of addCSRFToken method, of class org.owasp.esapi.HTTPUtilities.
+	 * @throws AuthenticationException 
+	 */
+	public void testAddCSRFToken() throws AuthenticationException {
+		Authenticator instance = ESAPI.authenticator();
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user = instance.createUser(username, "addCSRFToken", "addCSRFToken");
+		instance.setCurrentUser( user );
+
+		System.out.println("addCSRFToken");
+		String csrf1=ESAPI.httpUtilities().addCSRFToken("/test1");
+		System.out.println( "CSRF1:" + csrf1);
+		assertTrue(csrf1.indexOf("?") > -1);
+
+		String csrf2=ESAPI.httpUtilities().addCSRFToken("/test1?one=two");
+		System.out.println( "CSRF1:" + csrf1);
+		assertTrue(csrf2.indexOf("&") > -1);
+	}
+
+
+	/**
+	 * Test of assertSecureRequest method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testAssertSecureRequest() {
+		System.out.println("assertSecureRequest");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		try {
+			request.setRequestURL( "http://example.com");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( "ftp://example.com");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( "");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( null );
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( "https://example.com");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			// pass
+		} catch( Exception e ) {
+			fail();
+		}
+	}
+
+
+	/**
+	 * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities.
+	 * 
+	 * @throws EnterpriseSecurityException
+	 */
+	public void testChangeSessionIdentifier() throws EnterpriseSecurityException {
+		System.out.println("changeSessionIdentifier");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		MockHttpSession session = (MockHttpSession) request.getSession();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		session.setAttribute("one", "one");
+		session.setAttribute("two", "two");
+		session.setAttribute("three", "three");
+		String id1 = session.getId();
+		session = (MockHttpSession) ESAPI.httpUtilities().changeSessionIdentifier( request );
+		String id2 = session.getId();
+		assertTrue(!id1.equals(id2));
+		assertEquals("one", (String) session.getAttribute("one"));
+	}
+
+	/**
+	 * Test of formatHttpRequestForLog method, of class org.owasp.esapi.HTTPUtilities.
+	 * @throws IOException 
+	 */
+	public void testGetFileUploads() throws IOException {
+		File home = null;
+
+		try
+		{
+			home = FileTestUtils.createTmpDirectory(CLASS_NAME);
+			String content = "--ridiculous\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"testupload.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nThis is a test of the multipart broadcast system.\r\nThis is only a test.\r\nStop.\r\n\r\n--ridiculous\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit Query\r\n--ridiculous--\r\nEpilogue";
+
+			MockHttpServletResponse response = new MockHttpServletResponse();    
+			MockHttpServletRequest request1 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding()));
+			ESAPI.httpUtilities().setCurrentHTTP(request1, response);
+			try {
+				ESAPI.httpUtilities().getFileUploads(request1, home);
+				fail();
+			} catch( ValidationException e ) {
+				// expected
+			}
+
+			MockHttpServletRequest request2 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding()));
+			request2.setContentType( "multipart/form-data; boundary=ridiculous");
+			ESAPI.httpUtilities().setCurrentHTTP(request2, response);
+			try {
+				List list = ESAPI.httpUtilities().getFileUploads(request2, home);
+				Iterator i = list.iterator();
+				while ( i.hasNext() ) {
+					File f = (File)i.next();
+					System.out.println( "  " + f.getAbsolutePath() );
+				}
+				assertTrue( list.size() > 0 );
+			} catch (ValidationException e) {
+				fail();
+			}
+
+			MockHttpServletRequest request4 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding()));
+			request4.setContentType( "multipart/form-data; boundary=ridiculous");
+			ESAPI.httpUtilities().setCurrentHTTP(request4, response);
+			System.err.println("UPLOAD DIRECTORY: " + ESAPI.securityConfiguration().getUploadDirectory());
+			try {
+				List list = ESAPI.httpUtilities().getFileUploads(request4, home);
+				Iterator i = list.iterator();
+				while ( i.hasNext() ) {
+					File f = (File)i.next();
+					System.out.println( "  " + f.getAbsolutePath() );
+				}
+				assertTrue( list.size() > 0 );
+			} catch (ValidationException e) {
+				System.err.println("ERROR: " + e.toString());
+				fail();
+			}
+
+			MockHttpServletRequest request3 = new MockHttpServletRequest("/test", content.replaceAll("txt", "ridiculous").getBytes(response.getCharacterEncoding()));
+			request3.setContentType( "multipart/form-data; boundary=ridiculous");
+			ESAPI.httpUtilities().setCurrentHTTP(request3, response);
+			try {
+				ESAPI.httpUtilities().getFileUploads(request3, home);
+				fail();
+			} catch (ValidationException e) {
+				// expected
+			}
+		}
+		finally
+		{
+			FileTestUtils.deleteRecursively(home);
+		}
+
+	}
+
+
+
+	/**
+	 * Test of killAllCookies method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testKillAllCookies() {
+		System.out.println("killAllCookies");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		assertTrue(response.getCookies().isEmpty());
+		ArrayList list = new ArrayList();
+		list.add(new Cookie("test1", "1"));
+		list.add(new Cookie("test2", "2"));
+		list.add(new Cookie("test3", "3"));
+		request.setCookies(list);
+		ESAPI.httpUtilities().killAllCookies(request, response);
+		assertTrue(response.getCookies().size() == 3);
+	}
+
+	/**
+	 * Test of killCookie method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testKillCookie() {
+		System.out.println("killCookie");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		assertTrue(response.getCookies().isEmpty());
+		ArrayList list = new ArrayList();
+		list.add(new Cookie("test1", "1"));
+		list.add(new Cookie("test2", "2"));
+		list.add(new Cookie("test3", "3"));
+		request.setCookies(list);
+		ESAPI.httpUtilities().killCookie( request, response, "test1" );
+		assertTrue(response.getCookies().size() == 1);
+	}
+
+	/**
+	 * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities.
+	 * 
+	 * @throws ValidationException the validation exception
+	 * @throws IOException Signals that an I/O exception has occurred.
+	 */
+	public void testSendSafeRedirect() throws Exception {
+		System.out.println("sendSafeRedirect");
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		try {
+			ESAPI.httpUtilities().sendRedirect(response, "/test1/abcdefg");
+			ESAPI.httpUtilities().sendRedirect(response,"/test2/1234567");
+		} catch (IOException e) {
+			fail();
+		}
+		try {
+			ESAPI.httpUtilities().sendRedirect(response,"http://www.aspectsecurity.com");
+			fail();
+		} catch (IOException e) {
+			// expected
+		}
+		try {
+			ESAPI.httpUtilities().sendRedirect(response,"/ridiculous");
+			fail();
+		} catch (IOException e) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of setCookie method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testSetCookie() {
+		System.out.println("setCookie");
+		HTTPUtilities instance = ESAPI.httpUtilities(); 
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		assertTrue(response.getHeaderNames().isEmpty());
+
+		instance.addCookie( response, new Cookie( "test1", "test1" ) );
+		assertTrue(response.getHeaderNames().size() == 1);
+
+		instance.addCookie( response, new Cookie( "test2", "test2" ) );
+		assertTrue(response.getHeaderNames().size() == 2);
+
+		// test illegal name
+		instance.addCookie( response, new Cookie( "tes<t3", "test3" ) );
+		assertTrue(response.getHeaderNames().size() == 2);
+
+		// test illegal value
+		instance.addCookie( response, new Cookie( "test3", "tes<t3" ) );
+		assertTrue(response.getHeaderNames().size() == 2);
+	}
+
+	/**
+	 *
+	 * @throws java.lang.Exception
+	 */
+	public void testGetStateFromEncryptedCookie() throws Exception {
+		System.out.println("getStateFromEncryptedCookie");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+
+		// test null cookie array
+		Map empty = ESAPI.httpUtilities().decryptStateFromCookie(request);
+		assertTrue( empty.isEmpty() );
+
+		HashMap map = new HashMap();
+		map.put( "one", "aspect" );
+		map.put( "two", "ridiculous" );
+		map.put( "test_hard", "&(@#*!^|;,." );
+		try {
+			ESAPI.httpUtilities().encryptStateInCookie(response, map);
+			String value = response.getHeader( "Set-Cookie" );
+			String encrypted = value.substring(value.indexOf("=")+1, value.indexOf(";"));
+			request.setCookie( DefaultHTTPUtilities.ESAPI_STATE, encrypted );
+			Map state = ESAPI.httpUtilities().decryptStateFromCookie(request);
+			Iterator i = map.entrySet().iterator();
+			while ( i.hasNext() ) {
+				Map.Entry entry = (Map.Entry)i.next();
+				String origname = (String)entry.getKey();
+				String origvalue = (String)entry.getValue();
+				if( !state.get( origname ).equals( origvalue ) ) {
+					fail();
+				}
+			}
+		} catch( EncryptionException e ) {
+			fail();
+		}
+	}
+
+	/**
+	 *
+	 */
+	public void testSaveStateInEncryptedCookie() {
+		System.out.println("saveStateInEncryptedCookie");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		HashMap map = new HashMap();
+		map.put( "one", "aspect" );
+		map.put( "two", "ridiculous" );
+		map.put( "test_hard", "&(@#*!^|;,." );
+		try {
+			ESAPI.httpUtilities().encryptStateInCookie(response,map);
+			String value = response.getHeader( "Set-Cookie" );
+			String encrypted = value.substring(value.indexOf("=")+1, value.indexOf(";"));
+			byte[] serializedCiphertext = Hex.decode(encrypted);
+	        CipherText restoredCipherText =
+	            CipherText.fromPortableSerializedBytes(serializedCiphertext);
+	        ESAPI.encryptor().decrypt(restoredCipherText);
+		} catch( EncryptionException e ) {
+			fail();
+		}
+	}
+
+
+	/**
+	 *
+	 */
+	public void testSaveTooLongStateInEncryptedCookieException() {
+		System.out.println("saveTooLongStateInEncryptedCookie");
+
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+
+		String foo = ESAPI.randomizer().getRandomString(4096, EncoderConstants.CHAR_ALPHANUMERICS);
+
+		HashMap map = new HashMap();
+		map.put("long", foo);
+		try {
+			ESAPI.httpUtilities().encryptStateInCookie(response, map);
+			fail("Should have thrown an exception");
+		}
+		catch (EncryptionException expected) {
+			//expected
+		}    	
+	}
+
+	/**
+	 * Test set no cache headers.
+	 */
+	public void testSetNoCacheHeaders() {
+		System.out.println("setNoCacheHeaders");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		assertTrue(response.getHeaderNames().isEmpty());
+		response.addHeader("test1", "1");
+		response.addHeader("test2", "2");
+		response.addHeader("test3", "3");
+		assertFalse(response.getHeaderNames().isEmpty());
+		ESAPI.httpUtilities().setNoCacheHeaders( response );
+		assertTrue(response.containsHeader("Cache-Control"));
+		assertTrue(response.containsHeader("Expires"));
+	}
+
+	/**
+	 *
+	 * @throws org.owasp.esapi.errors.AuthenticationException
+	 */
+	public void testSetRememberToken() throws AuthenticationException {
+		System.out.println("setRememberToken");
+		Authenticator instance = (Authenticator)ESAPI.authenticator();
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", accountName);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		instance.login( request, response);
+
+		int maxAge = ( 60 * 60 * 24 * 14 );
+		ESAPI.httpUtilities().setRememberToken( request, response, password, maxAge, "domain", "/" );
+		// Can't test this because we're using safeSetCookie, which sets a header, not a real cookie!
+		// String value = response.getCookie( Authenticator.REMEMBER_TOKEN_COOKIE_NAME ).getValue();
+		// assertEquals( user.getRememberToken(), value );
+	}
+
+	public void testGetSessionAttribute() throws Exception {
+		HttpServletRequest request = new MockHttpServletRequest();
+		HttpSession session = request.getSession();
+		session.setAttribute("testAttribute", 43f);
+
+		try {
+			Integer test1 = ESAPI.httpUtilities().getSessionAttribute( session, "testAttribute" );
+			fail();
+		} catch ( ClassCastException cce ) {}
+
+		Float test2 = ESAPI.httpUtilities().getSessionAttribute( session, "testAttribute" );
+		assertEquals( test2, 43f );
+	}
+
+	public void testGetRequestAttribute() throws Exception {
+		HttpServletRequest request = new MockHttpServletRequest();
+		request.setAttribute( "testAttribute", 43f );
+		try {
+			Integer test1 = ESAPI.httpUtilities().getRequestAttribute( request, "testAttribute" );
+			fail();
+		} catch ( ClassCastException cce ) {}
+
+		Float test2 = ESAPI.httpUtilities().getRequestAttribute( request, "testAttribute" );
+		assertEquals( test2, 43f );
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/IntegerAccessReferenceMapTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/IntegerAccessReferenceMapTest.java.svn-base
new file mode 100644
index 0000000..c4afc8e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/IntegerAccessReferenceMapTest.java.svn-base
@@ -0,0 +1,223 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The Class AccessReferenceMapTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntegerAccessReferenceMapTest extends TestCase {
+    
+    /**
+	 * Instantiates a new access reference map test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public IntegerAccessReferenceMapTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(IntegerAccessReferenceMapTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AuthenticationException
+     *             the authentication exception
+     * @throws EncryptionException
+	 */
+    public void testUpdate() throws AuthenticationException, EncryptionException {
+        System.out.println("update");
+    	IntegerAccessReferenceMap arm = new IntegerAccessReferenceMap();
+    	Authenticator auth = ESAPI.authenticator();
+    	
+    	String pass = auth.generateStrongPassword();
+    	User u = auth.createUser( "armUpdate", pass, pass );
+    	
+    	// test to make sure update returns something
+		arm.update(auth.getUserNames());
+		String indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect == null ) fail();
+		
+		// test to make sure update removes items that are no longer in the list
+		auth.removeUser( u.getAccountName() );
+		arm.update(auth.getUserNames());
+		indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect != null ) fail();
+		
+		// test to make sure old indirect reference is maintained after an update
+		arm.update(auth.getUserNames());
+		String newIndirect = arm.getIndirectReference( u.getAccountName() );
+		assertEquals(indirect, newIndirect);
+    }
+    
+    
+    /**
+	 * Test of iterator method, of class org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testIterator() {
+        System.out.println("iterator");
+    	IntegerAccessReferenceMap arm = new IntegerAccessReferenceMap();
+        Authenticator auth = ESAPI.authenticator();
+        
+		arm.update(auth.getUserNames());
+
+		Iterator i = arm.iterator();
+		while ( i.hasNext() ) {
+			String userName = (String)i.next();
+			User u = auth.getUser( userName );
+			if ( u == null ) fail();
+		}
+    }
+    
+    /**
+	 * Test of getIndirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testGetIndirectReference() {
+        System.out.println("getIndirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String expResult = directReference;
+        String result = instance.getIndirectReference(directReference);
+        assertNotSame(expResult, result);        
+    }
+
+    /**
+	 * Test of getDirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AccessControlException
+	 *             the access control exception
+	 */
+    public void testGetDirectReference() throws AccessControlException {
+        System.out.println("getDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String ind = instance.getIndirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+        try {
+        	instance.getDirectReference("invalid");
+        	fail();
+        } catch( AccessControlException e ) {
+        	// success
+        }
+    }
+    
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testAddDirectReference() throws AccessControlException {
+        System.out.println("addDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String newDirect = instance.addDirectReference("newDirect");
+        assertNotNull( newDirect );
+        String ind = instance.addDirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+    	String newInd = instance.addDirectReference(directReference);
+    	assertEquals(ind, newInd);
+    }
+
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testRemoveDirectReference() throws AccessControlException {
+        System.out.println("removeDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String indirect = instance.getIndirectReference(directReference);
+        assertNotNull(indirect);
+        String deleted = instance.removeDirectReference(directReference);
+        assertEquals(indirect,deleted);
+    	deleted = instance.removeDirectReference("ridiculous");
+    	assertNull(deleted);
+    }
+    
+    
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/IntrusionDetectorTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/IntrusionDetectorTest.java.svn-base
new file mode 100644
index 0000000..e7f79d5
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/IntrusionDetectorTest.java.svn-base
@@ -0,0 +1,132 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class IntrusionDetectorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntrusionDetectorTest extends TestCase {
+
+	/**
+	 * Instantiates a new intrusion detector test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public IntrusionDetectorTest(String testName) {
+		super(testName);
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(IntrusionDetectorTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of addException method, of class org.owasp.esapi.IntrusionDetector.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testAddException() throws AuthenticationException {
+		System.out.println("addException");
+		ESAPI.intrusionDetector().addException( new RuntimeException("message") );
+		ESAPI.intrusionDetector().addException( new ValidationException("user message", "log message") );
+		ESAPI.intrusionDetector().addException( new IntrusionException("user message", "log message") );
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator auth = ESAPI.authenticator();
+		User user = auth.createUser(username, "addException", "addException");
+		user.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		user.loginWithPassword("addException");
+		
+		// Now generate some exceptions to disable account
+		for ( int i = 0; i < ESAPI.securityConfiguration().getQuota(IntegrityException.class.getName()).count; i++ ) {
+            // EnterpriseSecurityExceptions are added to IntrusionDetector automatically
+            new IntegrityException( "IntegrityException " + i, "IntegrityException " + i );
+		}
+        assertFalse( user.isLoggedIn() );
+	}
+
+    
+    /**
+     * Test of addEvent method, of class org.owasp.esapi.IntrusionDetector.
+     * 
+     * @throws AuthenticationException
+     *             the authentication exception
+     */
+    public void testAddEvent() throws AuthenticationException {
+        System.out.println("addEvent");
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator auth = ESAPI.authenticator();
+		User user = auth.createUser(username, "addEvent", "addEvent");
+		user.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		user.loginWithPassword("addEvent");
+        
+        // Now generate some events to disable user account
+        for ( int i = 0; i < ESAPI.securityConfiguration().getQuota("event.test").count; i++ ) {
+            ESAPI.intrusionDetector().addEvent("test", "test message");
+        }
+        assertFalse( user.isEnabled() );
+    }
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/JavaLoggerTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/JavaLoggerTest.java.svn-base
new file mode 100644
index 0000000..88d42bc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/JavaLoggerTest.java.svn-base
@@ -0,0 +1,282 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class LoggerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class JavaLoggerTest extends TestCase {
+
+	private static int testCount = 0;
+	
+	private static Logger testLogger = null;
+
+	
+    /**
+	 * Instantiates a new logger test.
+	 * 
+	 * @param testName the test name
+	 */
+    public JavaLoggerTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+        UnitTestSecurityConfiguration tmpConfig = new UnitTestSecurityConfiguration((DefaultSecurityConfiguration) ESAPI.securityConfiguration());
+        tmpConfig.setLogImplementation( JavaLogFactory.class.getName() );
+        ESAPI.override(tmpConfig);
+    	//This ensures a clean logger between tests
+    	testLogger = ESAPI.getLogger( "test" + testCount++ );
+    	System.out.println("Test logger: " + testLogger);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	//this helps, with garbage collection
+    	testLogger = null;
+        ESAPI.override(null);
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(JavaLoggerTest.class);    
+        return suite;
+    }
+    
+    /**
+     * Test of logHTTPRequest method, of class org.owasp.esapi.Logger.
+     * 
+     * @throws ValidationException
+     *             the validation exception
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     * @throws AuthenticationException
+     *             the authentication exception
+     */
+    public void testLogHTTPRequest() throws ValidationException, IOException, AuthenticationException {
+        System.out.println("logHTTPRequest");
+        String[] ignore = {"password","ssn","ccn"};
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        Logger logger = ESAPI.getLogger("logger");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+        request.addParameter("one","one");
+        request.addParameter("two","two1");
+        request.addParameter("two","two2");
+        request.addParameter("password","jwilliams");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+    }    
+    
+    
+    /**
+     * Test of setLevel method of the inner class org.owasp.esapi.reference.JavaLogger that is defined in 
+     * org.owasp.esapi.reference.JavaLogFactory.
+     */
+    public void testSetLevel() {
+        System.out.println("setLevel");
+        
+        // The following tests that the default logging level is set to WARNING. Since the default might be changed
+        // in the ESAPI security configuration file, these are commented out.
+//       	assertTrue(testLogger.isWarningEnabled());
+//       	assertFalse(testLogger.isInfoEnabled());
+
+        // First, test all the different logging levels
+        testLogger.setLevel( Logger.ALL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.TRACE );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.DEBUG );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.INFO );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.WARNING );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.ERROR );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.FATAL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.OFF );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	//Now test to see if a change to the logging level in one log affects other logs
+       	Logger newLogger = ESAPI.getLogger( "test_num2" );
+       	testLogger.setLevel( Logger.OFF );
+       	newLogger.setLevel( Logger.INFO );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	assertTrue(newLogger.isFatalEnabled());
+       	assertTrue(newLogger.isErrorEnabled());
+       	assertTrue(newLogger.isWarningEnabled());
+       	assertTrue(newLogger.isInfoEnabled());
+       	assertFalse(newLogger.isDebugEnabled());
+       	assertFalse(newLogger.isTraceEnabled());
+    }
+
+    
+    /**
+	 * Test of info method, of class org.owasp.esapi.Logger.
+	 */
+    public void testInfo() {
+        System.out.println("info");
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message" );
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "%3escript%3f test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "<script> test message", null );
+    }
+
+    /**
+	 * Test of trace method, of class org.owasp.esapi.Logger.
+	 */
+    public void testTrace() {
+        System.out.println("trace");
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace" );
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace", null );
+    }
+
+    /**
+	 * Test of debug method, of class org.owasp.esapi.Logger.
+	 */
+    public void testDebug() {
+        System.out.println("debug");
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug" );
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug", null );
+    }
+
+    /**
+	 * Test of error method, of class org.owasp.esapi.Logger.
+	 */
+    public void testError() {
+        System.out.println("error");
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error" );
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error", null );
+    }
+
+    /**
+	 * Test of warning method, of class org.owasp.esapi.Logger.
+	 */
+    public void testWarning() {
+        System.out.println("warning");
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning" );
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning", null );
+    }
+
+    /**
+	 * Test of fatal method, of class org.owasp.esapi.Logger.
+	 */
+    public void testFatal() {
+        System.out.println("fatal");
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal" );
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal", null );
+    }
+    
+    /**
+     * Test of always method, of class org.owasp.esapi.Logger.
+     */
+    public void testAlways() {
+
+        System.out.println("always");
+        testLogger.always(Logger.SECURITY_SUCCESS, "test message always 1 (SECURITY_SUCCESS)" );
+        testLogger.always(Logger.SECURITY_AUDIT,   "test message always 2 (SECURITY_AUDIT)" );
+        testLogger.always(Logger.SECURITY_SUCCESS, "test message always 3 (SECURITY_SUCCESS)", null );
+        testLogger.always(Logger.SECURITY_AUDIT,   "test message always 4 (SECURITY_AUDIT)", null );
+        try {
+        	throw new RuntimeException("What? You call that a 'throw'? My grandmother throws " +
+        							   "better than that and she's been dead for more than 10 years!");
+        } catch(RuntimeException rtex) {
+            testLogger.always(Logger.SECURITY_AUDIT,   "test message always 5", rtex );
+        }
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/Log4JLoggerTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/Log4JLoggerTest.java.svn-base
new file mode 100644
index 0000000..cddf71e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/Log4JLoggerTest.java.svn-base
@@ -0,0 +1,463 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class LoggerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author August Detlefsen (augustd at codemagi dot com) <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ */
+public class Log4JLoggerTest extends TestCase {
+	private static int testCount = 0;
+	
+	private static Logger testLogger = null;
+
+	//a logger for explicit tests of log4j logging methods
+	private static Log4JLogger log4JLogger = null;
+
+    /**
+	 * Instantiates a new logger test.
+	 * 
+	 * @param testName the test name
+	 */
+    public Log4JLoggerTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+		//override default log configuration in ESAPI.properties to use Log4JLogFactory
+        UnitTestSecurityConfiguration tmpConfig = new UnitTestSecurityConfiguration((DefaultSecurityConfiguration) ESAPI.securityConfiguration());
+        tmpConfig.setLogImplementation( Log4JLogFactory.class.getName() );
+        ESAPI.override(tmpConfig);
+
+    	//This ensures a clean logger between tests
+    	testLogger = ESAPI.getLogger( "test ExampleExtendedLog4JLogFactory: " + testCount++ );
+    	System.out.println("Test ExampleExtendedLog4JLogFactory logger: " + testLogger);
+
+		//declare this one as Log4JLogger to be able to use Log4J logging methods
+		log4JLogger = (Log4JLogger)ESAPI.getLogger( "test Log4JLogFactory: " + testCount);
+		System.out.println("Test Log4JLogFactory logger: " + log4JLogger);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	//this helps, with garbage collection
+    	testLogger = null;
+		log4JLogger = null;
+
+		ESAPI.override(null);
+	}
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(Log4JLoggerTest.class);    
+        return suite;
+    }
+    
+    /**
+     * Test of logHTTPRequest method, of class org.owasp.esapi.Logger.
+     * 
+     * @throws ValidationException
+     *             the validation exception
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     * @throws AuthenticationException
+     *             the authentication exception
+     */
+    public void testLogHTTPRequest() throws ValidationException, IOException, AuthenticationException {
+        System.out.println("logHTTPRequest");
+        String[] ignore = {"password","ssn","ccn"};
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        Logger logger = ESAPI.getLogger("logger");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+        request.addParameter("one","one");
+        request.addParameter("two","two1");
+        request.addParameter("two","two2");
+        request.addParameter("password","jwilliams");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+    } 
+    
+    
+    /**
+     * Test of setLevel method of the inner class org.owasp.esapi.reference.JavaLogger that is defined in 
+     * org.owasp.esapi.reference.JavaLogFactory.
+     */
+    public void testSetLevel() {
+        System.out.println("setLevel");
+        
+        // The following tests that the default logging level is set to WARNING. Since the default might be changed
+        // in the ESAPI security configuration file, these are commented out.
+//       	assertTrue(testLogger.isWarningEnabled());
+//       	assertFalse(testLogger.isInfoEnabled());
+
+        // First, test all the different logging levels
+        testLogger.setLevel( Logger.ALL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.TRACE );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.DEBUG );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.INFO );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.WARNING );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.ERROR );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.FATAL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.OFF );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	//Now test to see if a change to the logging level in one log affects other logs
+       	Logger newLogger = ESAPI.getLogger( "test_num2" );
+       	testLogger.setLevel( Logger.OFF );
+       	newLogger.setLevel( Logger.INFO );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	assertTrue(newLogger.isFatalEnabled());
+       	assertTrue(newLogger.isErrorEnabled());
+       	assertTrue(newLogger.isWarningEnabled());
+       	assertTrue(newLogger.isInfoEnabled());
+       	assertFalse(newLogger.isDebugEnabled());
+       	assertFalse(newLogger.isTraceEnabled());
+    }
+
+	/**
+	 * test of loggers without setting explicit log levels
+	 * (log levels set from log4j.xml configuration)
+	 */
+	public void testLogLevels() {
+
+		Logger traceLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestTrace");
+		Logger debugLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestDebug");
+		Logger infoLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestInfo");
+		Logger errorLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestError");
+		Logger warningLogger		= ESAPI.getLogger("org.owasp.esapi.reference.TestWarning");
+		Logger fatalLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestFatal");
+		Logger unspecifiedLogger	= ESAPI.getLogger("org.owasp.esapi.reference");  //should use package-wide log level configuration (info)
+
+
+		//traceLogger - all log levels should be enabled
+		assertTrue(traceLogger.isTraceEnabled());
+		assertTrue(traceLogger.isDebugEnabled());
+		assertTrue(traceLogger.isInfoEnabled());
+		assertTrue(traceLogger.isWarningEnabled());
+		assertTrue(traceLogger.isErrorEnabled());
+		assertTrue(traceLogger.isFatalEnabled());
+
+		//debugLogger - all log levels should be enabled EXCEPT trace
+		assertFalse(debugLogger.isTraceEnabled());
+		assertTrue(debugLogger.isDebugEnabled());
+		assertTrue(debugLogger.isInfoEnabled());
+		assertTrue(debugLogger.isWarningEnabled());
+		assertTrue(debugLogger.isErrorEnabled());
+		assertTrue(debugLogger.isFatalEnabled());
+
+		//infoLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(infoLogger.isTraceEnabled());
+		assertFalse(infoLogger.isDebugEnabled());
+		assertTrue(infoLogger.isInfoEnabled());
+		assertTrue(infoLogger.isWarningEnabled());
+		assertTrue(infoLogger.isErrorEnabled());
+		assertTrue(infoLogger.isFatalEnabled());
+
+		//warningLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(warningLogger.isTraceEnabled());
+		assertFalse(warningLogger.isDebugEnabled());
+		assertFalse(warningLogger.isInfoEnabled());
+		assertTrue(warningLogger.isWarningEnabled());
+		assertTrue(warningLogger.isErrorEnabled());
+		assertTrue(warningLogger.isFatalEnabled());
+
+		//errorLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(errorLogger.isTraceEnabled());
+		assertFalse(errorLogger.isDebugEnabled());
+		assertFalse(errorLogger.isInfoEnabled());
+		assertFalse(errorLogger.isWarningEnabled());
+		assertTrue(errorLogger.isErrorEnabled());
+		assertTrue(errorLogger.isFatalEnabled());
+
+		//fatalLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(fatalLogger.isTraceEnabled());
+		assertFalse(fatalLogger.isDebugEnabled());
+		assertFalse(fatalLogger.isInfoEnabled());
+		assertFalse(fatalLogger.isWarningEnabled());
+		assertFalse(fatalLogger.isErrorEnabled());
+		assertTrue(fatalLogger.isFatalEnabled());
+
+		//unspecifiedLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(unspecifiedLogger.isTraceEnabled());
+		assertFalse(unspecifiedLogger.isDebugEnabled());
+		assertTrue(unspecifiedLogger.isInfoEnabled());
+		assertTrue(unspecifiedLogger.isWarningEnabled());
+		assertTrue(unspecifiedLogger.isErrorEnabled());
+		assertTrue(unspecifiedLogger.isFatalEnabled());
+	}
+
+	/**
+	 * test of loggers without setting explicit log levels
+	 * (log levels set from log4j.xml configuration)
+	 */
+	public void testLogLevelsWithClass() {
+
+		Logger traceLogger			= ESAPI.getLogger(TestTrace.class);
+		Logger debugLogger			= ESAPI.getLogger(TestDebug.class);
+		Logger infoLogger			= ESAPI.getLogger(TestInfo.class);
+		Logger errorLogger			= ESAPI.getLogger(TestError.class);
+		Logger warningLogger		= ESAPI.getLogger(TestWarning.class);
+		Logger fatalLogger			= ESAPI.getLogger(TestFatal.class);
+		Logger unspecifiedLogger	= ESAPI.getLogger(TestUnspecified.class);  //should use package-wide log level configuration (info)
+
+		//traceLogger - all log levels should be enabled
+		assertTrue(traceLogger.isTraceEnabled());
+		assertTrue(traceLogger.isDebugEnabled());
+		assertTrue(traceLogger.isInfoEnabled());
+		assertTrue(traceLogger.isWarningEnabled());
+		assertTrue(traceLogger.isErrorEnabled());
+		assertTrue(traceLogger.isFatalEnabled());
+
+		//debugLogger - all log levels should be enabled EXCEPT trace
+		assertFalse(debugLogger.isTraceEnabled());
+		assertTrue(debugLogger.isDebugEnabled());
+		assertTrue(debugLogger.isInfoEnabled());
+		assertTrue(debugLogger.isWarningEnabled());
+		assertTrue(debugLogger.isErrorEnabled());
+		assertTrue(debugLogger.isFatalEnabled());
+
+		//infoLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(infoLogger.isTraceEnabled());
+		assertFalse(infoLogger.isDebugEnabled());
+		assertTrue(infoLogger.isInfoEnabled());
+		assertTrue(infoLogger.isWarningEnabled());
+		assertTrue(infoLogger.isErrorEnabled());
+		assertTrue(infoLogger.isFatalEnabled());
+
+		//warningLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(warningLogger.isTraceEnabled());
+		assertFalse(warningLogger.isDebugEnabled());
+		assertFalse(warningLogger.isInfoEnabled());
+		assertTrue(warningLogger.isWarningEnabled());
+		assertTrue(warningLogger.isErrorEnabled());
+		assertTrue(warningLogger.isFatalEnabled());
+
+		//errorLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(errorLogger.isTraceEnabled());
+		assertFalse(errorLogger.isDebugEnabled());
+		assertFalse(errorLogger.isInfoEnabled());
+		assertFalse(errorLogger.isWarningEnabled());
+		assertTrue(errorLogger.isErrorEnabled());
+		assertTrue(errorLogger.isFatalEnabled());
+
+		//fatalLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(fatalLogger.isTraceEnabled());
+		assertFalse(fatalLogger.isDebugEnabled());
+		assertFalse(fatalLogger.isInfoEnabled());
+		assertFalse(fatalLogger.isWarningEnabled());
+		assertFalse(fatalLogger.isErrorEnabled());
+		assertTrue(fatalLogger.isFatalEnabled());
+
+		//unspecifiedLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(unspecifiedLogger.isTraceEnabled());
+		assertFalse(unspecifiedLogger.isDebugEnabled());
+		assertTrue(unspecifiedLogger.isInfoEnabled());
+		assertTrue(unspecifiedLogger.isWarningEnabled());
+		assertTrue(unspecifiedLogger.isErrorEnabled());
+		assertTrue(unspecifiedLogger.isFatalEnabled());
+	}
+
+    /**
+	 * Test of info method, of class org.owasp.esapi.Logger.
+	 */
+    public void testInfo() {
+        System.out.println("info");
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message" );
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "%3escript%3f test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "<script> test message", null );
+
+        log4JLogger.info("test message" );
+        log4JLogger.info("test message", null );
+        log4JLogger.info("%3escript%3f test message", null );
+        log4JLogger.info("<script> test message", null );
+
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "test message" );
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "test message", null );
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "%3escript%3f test message", null );
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "<script> test message", null );
+	}
+
+    /**
+	 * Test of trace method, of class org.owasp.esapi.Logger.
+	 */
+    public void testTrace() {
+        System.out.println("trace");
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace" );
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace", null );
+
+        log4JLogger.trace("test message trace" );
+        log4JLogger.trace("test message trace", null );
+	}
+
+    /**
+	 * Test of debug method, of class org.owasp.esapi.Logger.
+	 */
+    public void testDebug() {
+        System.out.println("debug");
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug" );
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug", null );
+
+	    log4JLogger.debug("test message debug" );
+		log4JLogger.debug("test message debug", null );
+	}
+
+    /**
+	 * Test of error method, of class org.owasp.esapi.Logger.
+	 */
+    public void testError() {
+        System.out.println("error");
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error" );
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error", null );
+
+	    log4JLogger.error("test message error" );
+		log4JLogger.error("test message error", null );
+	}
+
+    /**
+	 * Test of warning method, of class org.owasp.esapi.Logger.
+	 */
+    public void testWarning() {
+        System.out.println("warning");
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning" );
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning", null );
+
+	    log4JLogger.warn("test message warning" );
+		log4JLogger.warn("test message warning", null );
+    }
+
+    /**
+	 * Test of fatal method, of class org.owasp.esapi.Logger.
+	 */
+    public void testFatal() {
+        System.out.println("fatal");
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal" );
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal", null );
+
+	    log4JLogger.fatal("test message fatal" );
+		log4JLogger.fatal("test message fatal", null );    
+	}
+    
+    /**
+     * Test of always method, of class org.owasp.esapi.Logger.
+     */
+    public void testAlways() {
+        System.out.println("always");
+        testLogger.always(Logger.SECURITY_SUCCESS, "test message always 1 (SECURITY_SUCCESS)" );
+        testLogger.always(Logger.SECURITY_AUDIT, "test message always 2 (SECURITY_AUDIT)", null );
+
+	    log4JLogger.always("test message always 3" );
+		log4JLogger.always("test message always 4", null );
+
+        try {
+        	throw new RuntimeException("What? You call that a 'throw'??? You couldn't hit the " +
+        							   "broad side of a barn (assuming that barns wore bras).");
+        } catch(RuntimeException rtex) {
+            testLogger.always(Logger.SECURITY_AUDIT, "test message always 5", rtex );
+            log4JLogger.always("test message always 6", rtex);
+        }
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/RandomizerTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/RandomizerTest.java.svn-base
new file mode 100644
index 0000000..2fc27f9
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/RandomizerTest.java.svn-base
@@ -0,0 +1,144 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Randomizer;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * The Class RandomizerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class RandomizerTest extends TestCase {
+    
+    /**
+	 * Instantiates a new randomizer test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public RandomizerTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(RandomizerTest.class);        
+        return suite;
+    }
+
+    /**
+	 * Test of getRandomString method, of class org.owasp.esapi.Randomizer.
+	 */
+    public void testGetRandomString() {
+        System.out.println("getRandomString");
+        int length = 20;
+        Randomizer instance = ESAPI.randomizer();
+        for ( int i = 0; i < 100; i++ ) {
+            String result = instance.getRandomString(length, EncoderConstants.CHAR_ALPHANUMERICS );
+            for ( int j=0;j<result.length();j++ ) {
+            	if ( !Codec.containsCharacter( result.charAt(j), EncoderConstants.CHAR_ALPHANUMERICS) ) {
+            		fail();
+            	}
+            }
+            assertEquals(length, result.length());
+        }
+    }
+
+    /**
+	 * Test of getRandomInteger method, of class org.owasp.esapi.Randomizer.
+	 */
+    public void testGetRandomInteger() {
+        System.out.println("getRandomInteger");        
+        int min = -20;
+        int max = 100;
+        Randomizer instance = ESAPI.randomizer();        
+        int minResult = ( max - min ) / 2;
+        int maxResult = ( max - min ) / 2;
+        for ( int i = 0; i < 100; i++ ) {
+            int result = instance.getRandomInteger(min, max);
+            if ( result < minResult ) minResult = result;
+            if ( result > maxResult ) maxResult = result;
+        }
+        assertEquals(true, (minResult >= min && maxResult < max) );
+    }
+
+    /**
+	 * Test of getRandomReal method, of class org.owasp.esapi.Randomizer.
+	 */
+    public void testGetRandomReal() {
+        System.out.println("getRandomReal");
+        float min = -20.5234F;
+        float max = 100.12124F;
+        Randomizer instance = ESAPI.randomizer();
+        float minResult = ( max - min ) / 2;
+        float maxResult = ( max - min ) / 2;
+        for ( int i = 0; i < 100; i++ ) {
+            float result = instance.getRandomReal(min, max);
+            if ( result < minResult ) minResult = result;
+            if ( result > maxResult ) maxResult = result;
+        }
+        assertEquals(true, (minResult >= min && maxResult < max));
+    }
+    
+    
+    /**
+     * Test of getRandomGUID method, of class org.owasp.esapi.Randomizer.
+     * @throws EncryptionException
+     */
+    public void testGetRandomGUID() throws EncryptionException {
+        System.out.println("getRandomGUID");
+        Randomizer instance = ESAPI.randomizer();
+        ArrayList list = new ArrayList();
+        for ( int i = 0; i < 100; i++ ) {
+            String guid = instance.getRandomGUID();
+            if ( list.contains( guid ) ) fail();
+            list.add( guid );
+        }
+    }
+
+     
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/SafeFileTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/SafeFileTest.java.svn-base
new file mode 100644
index 0000000..ef24f44
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/SafeFileTest.java.svn-base
@@ -0,0 +1,279 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.SafeFile;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.util.FileTestUtils;
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class SafeFileTest extends TestCase
+{
+	private static final Class CLASS = SafeFileTest.class;
+	private static final String CLASS_NAME = CLASS.getName();
+	/** Name of the file in the temporary directory */
+	private static final String TEST_FILE_NAME = "test.file";
+	private static final Set GOOD_FILE_CHARS = CollectionsUtil.strToUnmodifiableSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" /* + "." */);
+	private static final Set BAD_FILE_CHARS = CollectionsUtil.strToUnmodifiableSet("\u0000" + /*(File.separatorChar == '/' ? '\\' : '/') +*/ "*|<>?:" /*+ "~!@#$%^&(){}[],`;"*/);
+
+	private File testDir = null;
+	private File testFile = null;
+
+	String pathWithNullByte = "/temp/file.txt" + (char)0;
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception
+	{
+		// create a file to test with
+		testDir = FileTestUtils.createTmpDirectory(CLASS_NAME).getCanonicalFile();
+		testFile = new File(testDir, TEST_FILE_NAME);
+		testFile.createNewFile();
+		testFile = testFile.getCanonicalFile();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception
+	{
+		FileTestUtils.deleteRecursively(testDir);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SafeFileTest.class);
+		return suite;
+	}
+
+	public void testEscapeCharactersInFilename() {
+		System.out.println("testEscapeCharactersInFilenameInjection");
+		File tf = testFile;
+		if ( tf.exists() ) {
+			System.out.println( "File is there: " + tf );
+		}
+
+		File sf = new File(testDir, "test^.file" );
+		if ( sf.exists() ) {
+			System.out.println( "  Injection allowed "+ sf.getAbsolutePath() );
+		} else {
+			System.out.println( "  Injection didn't work "+ sf.getAbsolutePath() );
+		}
+	}
+
+	public void testEscapeCharacterInDirectoryInjection() {
+		System.out.println("testEscapeCharacterInDirectoryInjection");
+		File sf = new File(testDir, "test\\^.^.\\file");
+		if ( sf.exists() ) {
+			System.out.println( "  Injection allowed "+ sf.getAbsolutePath() );
+		} else {
+			System.out.println( "  Injection didn't work "+ sf.getAbsolutePath() );
+		}
+	}
+
+	public void testJavaFileInjectionGood() throws ValidationException
+	{
+		for(Iterator i=GOOD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+			sf = new SafeFile(testDir, TEST_FILE_NAME + ch + "test");
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+		}		
+	}
+
+	public void testJavaFileInjectionBad()
+	{
+		for(Iterator i=BAD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch  + "test");
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+		}		
+	}
+
+	public void testMultipleJavaFileInjectionGood() throws ValidationException
+	{
+		for(Iterator i=GOOD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			ch = ch + ch + ch;
+			File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+			sf = new SafeFile(testDir, TEST_FILE_NAME + ch + "test");
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+		}		
+	}
+
+	public void testMultipleJavaFileInjectionBad()
+	{
+		for(Iterator i=BAD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			ch = ch + ch + ch;
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch  + "test");
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+		}		
+	}
+
+	public void testAlternateDataStream() {
+		try
+		{
+			File sf = new SafeFile(testDir, TEST_FILE_NAME + ":secret.txt");
+			fail("Able to construct SafeFile for alternate data stream: " + sf.getPath());
+		}
+		catch(ValidationException expected)
+		{
+		}
+	}
+
+	static public String toHex(final byte b) {
+		final char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+		final char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
+		return new String(array);
+	}	
+
+	public void testCreatePath() throws Exception
+	{
+		SafeFile sf = new SafeFile(testFile.getPath());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateParentPathName() throws Exception
+	{
+		SafeFile sf = new SafeFile(testDir, testFile.getName());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateParentFileName() throws Exception
+	{
+		SafeFile sf = new SafeFile(testFile.getParentFile(), testFile.getName());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateURI() throws Exception
+	{
+		SafeFile sf = new SafeFile(testFile.toURI());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateFileNamePercentNull()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testDir + File.separator + "file%00.txt");
+			fail("no exception thrown for file name with percent encoded null");
+		}
+		catch(ValidationException expected)
+		{
+		}
+	}
+
+	public void testCreateFileNameQuestion()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file?.txt");
+			fail("no exception thrown for file name with question mark in it");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+	public void testCreateFileNameNull()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file" + ((char)0) + ".txt");
+			fail("no exception thrown for file name with null in it");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+	public void testCreateFileHighByte()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file" + ((char)160) + ".txt");
+			fail("no exception thrown for file name with high byte in it");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+	public void testCreateParentPercentNull()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file%00.txt");
+			fail("no exception thrown for file name with percent encoded null");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestDebug.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestDebug.java.svn-base
new file mode 100644
index 0000000..7b456e4
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestDebug.java.svn-base
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestDebug extends TestCase {
+
+	/** 
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestError.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestError.java.svn-base
new file mode 100644
index 0000000..98d9845
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestError.java.svn-base
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestError extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestFatal.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestFatal.java.svn-base
new file mode 100644
index 0000000..5caa11c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestFatal.java.svn-base
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestFatal extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestInfo.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestInfo.java.svn-base
new file mode 100644
index 0000000..17cd3df
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestInfo.java.svn-base
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestInfo extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestTrace.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestTrace.java.svn-base
new file mode 100644
index 0000000..b38e65b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestTrace.java.svn-base
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestTrace extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestUnspecified.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestUnspecified.java.svn-base
new file mode 100644
index 0000000..dc35da3
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestUnspecified.java.svn-base
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestUnspecified extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestWarning.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestWarning.java.svn-base
new file mode 100644
index 0000000..16c14e3
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/TestWarning.java.svn-base
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestWarning extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/UnitTestSecurityConfiguration.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/UnitTestSecurityConfiguration.java.svn-base
new file mode 100644
index 0000000..82b8427
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/UnitTestSecurityConfiguration.java.svn-base
@@ -0,0 +1,87 @@
+package org.owasp.esapi.reference;
+
+import java.util.Properties;
+
+public class UnitTestSecurityConfiguration extends DefaultSecurityConfiguration {
+    public UnitTestSecurityConfiguration(DefaultSecurityConfiguration cfg) {
+        super(cfg.getESAPIProperties());
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setApplicationName(String v) {
+    	getESAPIProperties().setProperty(APPLICATION_NAME, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setLogImplementation(String v) {
+    	getESAPIProperties().setProperty(LOG_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setAuthenticationImplementation(String v) {
+    	getESAPIProperties().setProperty(AUTHENTICATION_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setEncoderImplementation(String v) {
+    	getESAPIProperties().setProperty(ENCODER_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setAccessControlImplementation(String v) {
+    	getESAPIProperties().setProperty(ACCESS_CONTROL_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setEncryptionImplementation(String v) {
+    	getESAPIProperties().setProperty(ENCRYPTION_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setIntrusionDetectionImplementation(String v) {
+    	getESAPIProperties().setProperty(INTRUSION_DETECTION_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setRandomizerImplementation(String v) {
+    	getESAPIProperties().setProperty(RANDOMIZER_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setExecutorImplementation(String v) {
+    	getESAPIProperties().setProperty(EXECUTOR_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setHTTPUtilitiesImplementation(String v) {
+    	getESAPIProperties().setProperty(HTTP_UTILITIES_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setValidationImplementation(String v) {
+    	getESAPIProperties().setProperty(VALIDATOR_IMPLEMENTATION, v);
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/UserTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/UserTest.java.svn-base
new file mode 100644
index 0000000..630976e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/UserTest.java.svn-base
@@ -0,0 +1,739 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.servlet.http.HttpSession;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+import org.owasp.esapi.http.MockHttpSession;
+
+/**
+ * The Class UserTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class UserTest extends TestCase {
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UserTest.class);
+		return suite;
+	}
+	
+	/**
+	 * Instantiates a new user test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public UserTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * Creates the test user.
+	 * 
+	 * @param password
+	 *            the password
+	 * 
+	 * @return the user
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	private DefaultUser createTestUser(String password) throws AuthenticationException {
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		Exception e = new Exception();
+		System.out.println("Creating user " + username + " for " + e.getStackTrace()[1].getMethodName());
+		DefaultUser user = (DefaultUser) ESAPI.authenticator().createUser(username, password, password);
+		return user;
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Test of testAddRole method, of class org.owasp.esapi.User.
+	 * 
+	 * @exception Exception
+	 * 				any Exception thrown by testing addRole()
+	 */
+	public void testAddRole() throws Exception {
+		System.out.println("addRole");
+		Authenticator instance = ESAPI.authenticator();
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = ESAPI.authenticator().generateStrongPassword();
+		String role = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_LOWERS);
+		User user = instance.createUser(accountName, password, password);
+		user.addRole(role);
+		assertTrue(user.isInRole(role));
+		assertFalse(user.isInRole("ridiculous"));
+	}
+
+	/**
+	 * Test of addRoles method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testAddRoles() throws AuthenticationException {
+		System.out.println("addRoles");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		Set set = new HashSet();
+		set.add("rolea");
+		set.add("roleb");
+		user.addRoles(set);
+		assertTrue(user.isInRole("rolea"));
+		assertTrue(user.isInRole("roleb"));
+		assertFalse(user.isInRole("ridiculous"));
+	}
+
+	/**
+	 * Test of changePassword method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testChangePassword() throws Exception {
+		System.out.println("changePassword");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = "Password12!@";
+		DefaultUser user = createTestUser(oldPassword);
+		System.out.println("Hash of " + oldPassword + " = " + ((FileBasedAuthenticator)instance).getHashedPassword(user));
+		String password1 = "SomethingElse34#$";
+		user.changePassword(oldPassword, password1, password1);
+		System.out.println("Hash of " + password1 + " = " + ((FileBasedAuthenticator)instance).getHashedPassword(user));
+		assertTrue(user.verifyPassword(password1));
+		String password2 = "YetAnother56%^";
+		user.changePassword(password1, password2, password2);
+		System.out.println("Hash of " + password2 + " = " + ((FileBasedAuthenticator)instance).getHashedPassword(user));
+		try {
+			user.changePassword(password2, password1, password1);
+			fail("Shouldn't be able to reuse a password");
+		} catch( AuthenticationException e ) {
+			// expected
+		}
+		assertTrue(user.verifyPassword(password2));
+		assertFalse(user.verifyPassword("badpass"));
+	}
+
+	/**
+	 * Test of disable method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testDisable() throws AuthenticationException {
+		System.out.println("disable");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.enable();
+		assertTrue(user.isEnabled());
+		user.disable();
+		assertFalse(user.isEnabled());
+	}
+
+	/**
+	 * Test of enable method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testEnable() throws AuthenticationException {
+		System.out.println("enable");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.enable();
+		assertTrue(user.isEnabled());
+		user.disable();
+		assertFalse(user.isEnabled());
+	}
+
+	/**
+	 * Test of failedLoginCount lockout, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 * @throws EncryptionException
+	 *             any EncryptionExceptions thrown by testing failedLoginLockout()
+	 */
+	public void testFailedLoginLockout() throws AuthenticationException, EncryptionException {
+		System.out.println("failedLoginLockout");
+		DefaultUser user = createTestUser("failedLoginLockout");
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        
+		user.loginWithPassword("failedLoginLockout");
+		
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+ 		System.out.println("FAILED: " + user.getFailedLoginCount());
+		assertFalse(user.isLocked());
+
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		System.out.println("FAILED: " + user.getFailedLoginCount());
+		assertFalse(user.isLocked());
+
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		System.out.println("FAILED: " + user.getFailedLoginCount());
+		assertTrue(user.isLocked());
+	}
+
+	/**
+	 * Test of getAccountName method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetAccountName() throws AuthenticationException {
+		System.out.println("getAccountName");
+		DefaultUser user = createTestUser("getAccountName");
+		String accountName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setAccountName(accountName);
+		assertEquals(accountName.toLowerCase(), user.getAccountName());
+		assertFalse("ridiculous".equals(user.getAccountName()));
+	}
+
+	/**
+	 * Test get last failed login time.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testGetLastFailedLoginTime() throws Exception {
+		System.out.println("getLastLoginTime");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		Date llt1 = user.getLastFailedLoginTime();
+		Thread.sleep(100); // need a short delay to separate attempts
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		Date llt2 = user.getLastFailedLoginTime();
+		assertTrue(llt1.before(llt2));
+	}
+
+	/**
+	 * Test get last login time.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testGetLastLoginTime() throws Exception {
+		System.out.println("getLastLoginTime");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.verifyPassword(oldPassword);
+		Date llt1 = user.getLastLoginTime();
+		Thread.sleep(10); // need a short delay to separate attempts
+		user.verifyPassword(oldPassword);
+		Date llt2 = user.getLastLoginTime();
+		assertTrue(llt1.before(llt2));
+	}
+
+	/**
+	 * Test getLastPasswordChangeTime method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testGetLastPasswordChangeTime() throws Exception {
+		System.out.println("getLastPasswordChangeTime");
+		DefaultUser user = createTestUser("getLastPasswordChangeTime");
+		Date t1 = user.getLastPasswordChangeTime();
+		Thread.sleep(10); // need a short delay to separate attempts
+		String newPassword = ESAPI.authenticator().generateStrongPassword(user, "getLastPasswordChangeTime");
+		user.changePassword("getLastPasswordChangeTime", newPassword, newPassword);
+		Date t2 = user.getLastPasswordChangeTime();
+		assertTrue(t2.after(t1));
+	}
+
+	/**
+	 * Test of getRoles method, of class org.owasp.esapi.User.
+     *
+     * @throws Exception
+     */
+	public void testGetRoles() throws Exception {
+		System.out.println("getRoles");
+		Authenticator instance = ESAPI.authenticator();
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = ESAPI.authenticator().generateStrongPassword();
+		String role = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_LOWERS);
+		User user = instance.createUser(accountName, password, password);
+		user.addRole(role);
+		Set roles = user.getRoles();
+		assertTrue(roles.size() > 0);
+	}
+
+	/**
+	 * Test of getScreenName method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetScreenName() throws AuthenticationException {
+		System.out.println("getScreenName");
+		DefaultUser user = createTestUser("getScreenName");
+		String screenName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setScreenName(screenName);
+		assertEquals(screenName, user.getScreenName());
+		assertFalse("ridiculous".equals(user.getScreenName()));
+	}
+
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AuthenticationException
+     */
+    public void testGetSessions() throws AuthenticationException {
+        System.out.println("getSessions");
+        Authenticator instance = ESAPI.authenticator();
+        String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        String password = ESAPI.authenticator().generateStrongPassword();
+        User user = instance.createUser(accountName, password, password);
+        HttpSession session1 = new MockHttpSession();
+        user.addSession( session1 );
+        HttpSession session2 = new MockHttpSession();
+        user.addSession( session2 );
+        HttpSession session3 = new MockHttpSession();
+        user.addSession( session3 );
+        Set sessions = user.getSessions();
+        Iterator i = sessions.iterator();
+        while ( i.hasNext() ) {
+            HttpSession s = (HttpSession)i.next();
+            System.out.println( ">>>" + s.getId() );
+        }
+        assertTrue(sessions.size() == 3);
+	}
+	
+	
+    /**
+     *
+     */
+    public void testAddSession() {
+	    // TODO
+	}
+	
+    /**
+     *
+     */
+    public void testRemoveSession() {
+	    // TODO
+	}
+	
+	/**
+	 * Test of incrementFailedLoginCount method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIncrementFailedLoginCount() throws AuthenticationException {
+		System.out.println("incrementFailedLoginCount");
+		DefaultUser user = createTestUser("incrementFailedLoginCount");
+		user.enable();
+		assertEquals(0, user.getFailedLoginCount());
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertEquals(1, user.getFailedLoginCount());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertEquals(2, user.getFailedLoginCount());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertEquals(3, user.getFailedLoginCount());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertTrue(user.isLocked());
+	}
+
+	/**
+	 * Test of isEnabled method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsEnabled() throws AuthenticationException {
+		System.out.println("isEnabled");
+		DefaultUser user = createTestUser("isEnabled");
+		user.disable();
+		assertFalse(user.isEnabled());
+		user.enable();
+		assertTrue(user.isEnabled());
+	}
+
+    
+    
+	/**
+	 * Test of isInRole method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsInRole() throws AuthenticationException {
+		System.out.println("isInRole");
+		DefaultUser user = createTestUser("isInRole");
+		String role = "TestRole";
+		assertFalse(user.isInRole(role));
+		user.addRole(role);
+		assertTrue(user.isInRole(role));
+		assertFalse(user.isInRole("Ridiculous"));
+	}
+
+	/**
+	 * Test of isLocked method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsLocked() throws AuthenticationException {
+		System.out.println("isLocked");
+		DefaultUser user = createTestUser("isLocked");
+		user.lock();
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertFalse(user.isLocked());
+	}
+
+	/**
+	 * Test of isSessionAbsoluteTimeout method, of class
+	 * org.owasp.esapi.IntrusionDetector.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsSessionAbsoluteTimeout() throws AuthenticationException {
+		System.out.println("isSessionAbsoluteTimeout");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		long now = System.currentTimeMillis();
+		// setup request and response
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		MockHttpSession session = (MockHttpSession)request.getSession();
+				
+		// set session creation -3 hours (default is 2 hour timeout)		
+		session.setCreationTime( now - (1000 * 60 * 60 * 3) );
+		assertTrue(user.isSessionAbsoluteTimeout());
+		
+		// set session creation -1 hour (default is 2 hour timeout)
+		session.setCreationTime( now - (1000 * 60 * 60 * 1) );
+		assertFalse(user.isSessionAbsoluteTimeout());
+	}
+
+	/**
+	 * Test of isSessionTimeout method, of class
+	 * org.owasp.esapi.IntrusionDetector.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsSessionTimeout() throws AuthenticationException {
+		System.out.println("isSessionTimeout");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		long now = System.currentTimeMillis();
+		// setup request and response
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		MockHttpSession session = (MockHttpSession)request.getSession();
+		
+		// set creation -30 mins (default is 20 min timeout)
+		session.setAccessedTime( now - 1000 * 60 * 30 );
+		assertTrue(user.isSessionTimeout());
+		
+		// set creation -1 hour (default is 20 min timeout)
+		session.setAccessedTime( now - 1000 * 60 * 10 );
+		assertFalse(user.isSessionTimeout());
+	}
+
+	/**
+	 * Test of lockAccount method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLock() throws AuthenticationException {
+		System.out.println("lock");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.lock();
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertFalse(user.isLocked());
+	}
+
+	/**
+	 * Test of loginWithPassword method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLoginWithPassword() throws AuthenticationException {
+		System.out.println("loginWithPassword");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpSession session = (MockHttpSession) request.getSession();
+		assertFalse(session.getInvalidated());
+		DefaultUser user = createTestUser("loginWithPassword");
+		user.enable();
+		user.loginWithPassword("loginWithPassword");
+		assertTrue(user.isLoggedIn());
+		user.logout();
+		assertFalse(user.isLoggedIn());
+		assertFalse(user.isLocked());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertFalse(user.isLoggedIn());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertTrue(user.getFailedLoginCount() == 0 );
+	}
+
+
+	/**
+	 * Test of logout method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLogout() throws AuthenticationException {
+		System.out.println("logout");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		MockHttpSession session = (MockHttpSession) request.getSession();
+		assertFalse(session.getInvalidated());
+		Authenticator instance = ESAPI.authenticator();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.enable();
+		System.out.println(user.getLastLoginTime());
+		user.loginWithPassword(oldPassword);
+		assertTrue(user.isLoggedIn());
+		// get new session after user logs in
+		session = (MockHttpSession) request.getSession();
+		assertFalse(session.getInvalidated());
+		user.logout();
+		assertFalse(user.isLoggedIn());
+		assertTrue(session.getInvalidated());
+	}
+
+	/**
+	 * Test of testRemoveRole method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testRemoveRole() throws AuthenticationException {
+		System.out.println("removeRole");
+		String role = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_LOWERS);
+		DefaultUser user = createTestUser("removeRole");
+		user.addRole(role);
+		assertTrue(user.isInRole(role));
+		user.removeRole(role);
+		assertFalse(user.isInRole(role));
+	}
+
+	/**
+	 * Test of testResetCSRFToken method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testResetCSRFToken() throws AuthenticationException {
+		System.out.println("resetCSRFToken");
+		DefaultUser user = createTestUser("resetCSRFToken");
+        String token1 = user.resetCSRFToken();
+        String token2 = user.resetCSRFToken();
+        assertFalse( token1.equals( token2 ) );
+	}
+	
+	/**
+	 * Test of setAccountName method, of class org.owasp.esapi.User.
+     *
+     * @throws AuthenticationException
+     */
+	public void testSetAccountName() throws AuthenticationException {
+		System.out.println("setAccountName");
+		DefaultUser user = createTestUser("setAccountName");
+		String accountName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setAccountName(accountName);
+		assertEquals(accountName.toLowerCase(), user.getAccountName());
+		assertFalse("ridiculous".equals(user.getAccountName()));
+	}
+
+	/**
+	 * Test of setExpirationTime method, of class org.owasp.esapi.User.
+     *
+     * @throws Exception
+     */
+	public void testSetExpirationTime() throws Exception {
+		Date longAgo = new Date(0);
+		Date now = new Date();
+		assertTrue("new Date(0) returned " + longAgo + " which is considered before new Date() " + now + ". Please report this output to the email list or as a issue", longAgo.before(now));
+		String password=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		DefaultUser user = createTestUser(password);
+		user.setExpirationTime(longAgo);
+		assertTrue( user.isExpired() );
+	}
+
+	
+	/**
+	 * Test of setRoles method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetRoles() throws AuthenticationException {
+		System.out.println("setRoles");
+		DefaultUser user = createTestUser("setRoles");
+		user.addRole("user");
+		assertTrue(user.isInRole("user"));
+		Set set = new HashSet();
+		set.add("rolea");
+		set.add("roleb");
+		user.setRoles(set);
+		assertFalse(user.isInRole("user"));
+		assertTrue(user.isInRole("rolea"));
+		assertTrue(user.isInRole("roleb"));
+		assertFalse(user.isInRole("ridiculous"));
+	}
+
+	/**
+	 * Test of setScreenName method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetScreenName() throws AuthenticationException {
+		System.out.println("setScreenName");
+		DefaultUser user = createTestUser("setScreenName");
+		String screenName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setScreenName(screenName);
+		assertEquals(screenName, user.getScreenName());
+		assertFalse("ridiculous".equals(user.getScreenName()));
+	}
+
+	/**
+	 * Test of unlockAccount method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testUnlock() throws AuthenticationException {
+		System.out.println("unlockAccount");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.lock();
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertFalse(user.isLocked());
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/.svn/text-base/ValidatorTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/.svn/text-base/ValidatorTest.java.svn-base
new file mode 100644
index 0000000..9402630
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/.svn/text-base/ValidatorTest.java.svn-base
@@ -0,0 +1,1150 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.*;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.filters.SecurityWrapperRequest;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+import org.owasp.esapi.reference.validation.HTMLValidationRule;
+import org.owasp.esapi.reference.validation.StringValidationRule;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * The Class ValidatorTest.
+ *
+ * @author Mike Fauzy (mike.fauzy at aspectsecurity.com)
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidatorTest extends TestCase {
+
+    private static final String PREFERRED_ENCODING = "UTF-8";
+
+    public static Test suite() {
+        return new TestSuite(ValidatorTest.class);
+    }
+
+    /**
+     * Instantiates a new HTTP utilities test.
+     *
+     * @param testName the test name
+     */
+    public ValidatorTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+        // none
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+        // none
+    }
+
+    public void testAddRule() {
+        Validator validator = ESAPI.validator();
+        ValidationRule rule = new StringValidationRule("ridiculous");
+        validator.addRule(rule);
+        assertEquals(rule, validator.getRule("ridiculous"));
+    }
+
+    public void testAssertValidFileUpload() {
+        //		assertValidFileUpload(String, String, String, byte[], int, boolean, ValidationErrorList)
+    }
+
+    public void testGetPrintable1() {
+        //		getValidPrintable(String, char[], int, boolean, ValidationErrorList)
+    }
+
+    public void testGetPrintable2() {
+        //		getValidPrintable(String, String, int, boolean, ValidationErrorList)
+    }
+
+    public void testGetRule() {
+        Validator validator = ESAPI.validator();
+        ValidationRule rule = new StringValidationRule("rule");
+        validator.addRule(rule);
+        assertEquals(rule, validator.getRule("rule"));
+        assertFalse(rule == validator.getRule("ridiculous"));
+    }
+
+    public void testGetValidCreditCard() {
+        System.out.println("getValidCreditCard");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+
+        assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false));
+        assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false));
+        assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false));
+        assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false));
+
+        instance.getValidCreditCard("cctest5", "1234 9876 0000 0008", false, errors);
+        assertEquals(0, errors.size());
+        instance.getValidCreditCard("cctest6", "1234987600000008", false, errors);
+        assertEquals(0, errors.size());
+        instance.getValidCreditCard("cctest7", "12349876000000081", false, errors);
+        assertEquals(1, errors.size());
+        instance.getValidCreditCard("cctest8", "4417 1234 5678 9112", false, errors);
+        assertEquals(2, errors.size());
+
+        assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false, errors));
+        assertTrue(errors.size()==2);
+        assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false, errors));
+        assertTrue(errors.size()==3);
+        assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false, errors));
+        assertTrue(errors.size()==4);
+    }
+
+    public void testGetValidDate() throws Exception {
+    	System.out.println("getValidDate");
+    	Validator instance = ESAPI.validator();
+    	ValidationErrorList errors = new ValidationErrorList();
+    	assertTrue(instance.getValidDate("datetest1", "June 23, 1967", DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US), false) != null);
+    	instance.getValidDate("datetest2", "freakshow", DateFormat.getDateInstance(), false, errors);
+    	assertEquals(1, errors.size());
+
+    	// TODO: This test case fails due to an apparent bug in SimpleDateFormat
+    	// Note: This seems to be fixed in JDK 6. Will leave it commented out since
+    	//		 we only require JDK 5. -kww
+    	instance.getValidDate("test", "June 32, 2008", DateFormat.getDateInstance(), false, errors);
+    	// assertEquals( 2, errors.size() );
+    }
+    
+    // FIXME: Should probably use SecurityConfigurationWrapper and force
+    //		  Validator.AcceptLenientDates to be false.
+    public void testLenientDate() {
+    	System.out.println("testLenientDate");
+    	boolean acceptLenientDates = ESAPI.securityConfiguration().getLenientDatesAccepted();
+    	if ( acceptLenientDates ) {
+    		assertTrue("Lenient date test skipped because Validator.AcceptLenientDates set to true", true);
+    		return;
+    	}
+
+    	Date lenientDateTest = null;
+    	try {
+    		// lenientDateTest will be null when Validator.AcceptLenientDates
+    		// is set to false (the default).
+    		Validator instance = ESAPI.validator();
+    		lenientDateTest = instance.getValidDate("datatest3-lenient", "15/2/2009 11:83:00",
+    				                                DateFormat.getDateInstance(DateFormat.SHORT, Locale.US),
+    				                                false);
+    		fail("Failed to throw expected ValidationException when Validator.AcceptLenientDates set to false.");
+    	} catch (ValidationException ve) {
+    		assertNull( lenientDateTest );
+    		Throwable cause = ve.getCause();
+    		assertTrue( cause.getClass().getName().equals("java.text.ParseException") );
+    	} catch (Exception e) {
+    		fail("Caught unexpected exception: " + e.getClass().getName() + "; msg: " + e);
+    	}
+    }
+
+    public void testGetValidDirectoryPath() throws Exception {
+        System.out.println("getValidDirectoryPath");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // find a directory that exists
+        File parent = new File("/");
+        String path = ESAPI.securityConfiguration().getResourceFile("ESAPI.properties").getParentFile().getCanonicalPath();
+        instance.getValidDirectoryPath("dirtest1", path, parent, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidDirectoryPath("dirtest2", null, parent, false, errors);
+        assertEquals(1, errors.size());
+        instance.getValidDirectoryPath("dirtest3", "ridicul%00ous", parent, false, errors);
+        assertEquals(2, errors.size());
+    }
+
+    public void testGetValidDouble() {
+        System.out.println("getValidDouble");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        instance.getValidDouble("dtest1", "1.0", 0, 20, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidDouble("dtest2", null, 0, 20, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidDouble("dtest3", null, 0, 20, false, errors);
+        assertEquals(1, errors.size());
+        instance.getValidDouble("dtest4", "ridiculous", 0, 20, true, errors);
+        assertEquals(2, errors.size());
+        instance.getValidDouble("dtest5", "" + (Double.MAX_VALUE), 0, 20, true, errors);
+        assertEquals(3, errors.size());
+        instance.getValidDouble("dtest6", "" + (Double.MAX_VALUE + .00001), 0, 20, true, errors);
+        assertEquals(4, errors.size());
+    }
+
+    public void testGetValidFileContent() {
+        System.out.println("getValidFileContent");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        byte[] bytes = null;
+        try {
+            bytes = "12345".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!");
+        }
+        instance.getValidFileContent("test", bytes, 5, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidFileContent("test", bytes, 4, true, errors);
+        assertEquals(1, errors.size());
+    }
+
+    public void testGetValidFileName() throws Exception {
+        System.out.println("getValidFileName");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        String testName = "aspe%20ct.jar";
+        assertEquals("Percent encoding is not changed", testName, instance.getValidFileName("test", testName, ESAPI.securityConfiguration().getAllowedFileExtensions(), false, errors));
+    }
+
+    public void testGetValidInput() {
+        System.out.println("getValidInput");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidInput(String, String, String, int, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidInteger() {
+        System.out.println("getValidInteger");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidInteger(String, String, int, int, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidListItem() {
+        System.out.println("getValidListItem");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidListItem(String, String, List, ValidationErrorList)
+    }
+
+    public void testGetValidNumber() {
+        System.out.println("getValidNumber");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidNumber(String, String, long, long, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidRedirectLocation() {
+        System.out.println("getValidRedirectLocation");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidRedirectLocation(String, String, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidSafeHTML() throws Exception {
+        System.out.println("getValidSafeHTML");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+
+        // new school test case setup
+        HTMLValidationRule rule = new HTMLValidationRule("test");
+        ESAPI.validator().addRule(rule);
+
+        assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. <script>alert(document.cookie)</script>"));
+
+        String test1 = "<b>Jeff</b>";
+        String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors);
+        assertEquals(test1, result1);
+
+        String test2 = "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>";
+        String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors);
+        assertEquals(test2, result2);
+
+        String test3 = "Test. <script>alert(document.cookie)</script>";
+        assertEquals("Test.", rule.getSafe("test", test3));
+
+        assertEquals("Test. <<div>load=alert()</div>", rule.getSafe("test", "Test. <<div on<script></script>load=alert()"));
+        assertEquals("Test. <div>b</div>", rule.getSafe("test", "Test. <div style={xss:expression(xss)}>b</div>"));
+        assertEquals("Test.", rule.getSafe("test", "Test. <s%00cript>alert(document.cookie)</script>"));
+        assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. <s\tcript>alert(document.cookie)</script>"));
+        assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. <s\tcript>alert(document.cookie)</script>"));
+        // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts
+        // This would be nice to catch, but just looks like text to AntiSamy
+        // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" "));
+        // String result4 = instance.getValidSafeHTML("test", test4);
+        // assertEquals("", result4);
+    }
+
+    public void testIsInvalidFilename() {
+        System.out.println("testIsInvalidFilename");
+        Validator instance = ESAPI.validator();
+        char invalidChars[] = "/\\:*?\"<>|".toCharArray();
+        for (int i = 0; i < invalidChars.length; i++) {
+            assertFalse(invalidChars[i] + " is an invalid character for a filename",
+                    instance.isValidFileName("test", "as" + invalidChars[i] + "pect.jar", false));
+        }
+        assertFalse("Files must have an extension", instance.isValidFileName("test", "", false));
+        assertFalse("Files must have a valid extension", instance.isValidFileName("test.invalidExtension", "", false));
+        assertFalse("Filennames cannot be the empty string", instance.isValidFileName("test", "", false));
+    }
+
+    public void testIsValidDate() {
+        System.out.println("isValidDate");
+        Validator instance = ESAPI.validator();
+        DateFormat format = SimpleDateFormat.getDateInstance();
+        assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true));
+        assertFalse(instance.isValidDate("datetest2", null, format, false));
+        assertFalse(instance.isValidDate("datetest3", "", format, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidDate("datetest2", null, format, false, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidDate("datetest3", "", format, false, errors));
+        assertTrue(errors.size()==2);
+
+    }
+
+    public void testIsValidDirectoryPath() throws IOException {
+        System.out.println("isValidDirectoryPath");
+
+        // get an encoder with a special list of codecs and make a validator out of it
+        List list = new ArrayList();
+        list.add("HTMLEntityCodec");
+        Encoder encoder = new DefaultEncoder(list);
+        Validator instance = new DefaultValidator(encoder);
+
+        boolean isWindows = (System.getProperty("os.name").indexOf("Windows") != -1) ? true : false;
+        File parent = new File("/");
+
+        ValidationErrorList errors = new ValidationErrorList();
+
+        if (isWindows) {
+            String sysRoot = new File(System.getenv("SystemRoot")).getCanonicalPath();
+            // Windows paths that don't exist and thus should fail
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\jeff", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false));
+
+            // Windows paths
+            assertTrue(instance.isValidDirectoryPath("test", "C:\\", parent, false));                        // Windows root directory
+            assertTrue(instance.isValidDirectoryPath("test", sysRoot, parent, false));                  // Windows always exist directory
+            assertFalse(instance.isValidDirectoryPath("test", sysRoot + "\\System32\\cmd.exe", parent, false));      // Windows command shell
+
+            // Unix specific paths should not pass
+            assertFalse(instance.isValidDirectoryPath("test", "/tmp", parent, false));      // Unix Temporary directory
+            assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false));   // Unix Standard shell
+            assertFalse(instance.isValidDirectoryPath("test", "/etc/config", parent, false));
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false));
+
+            assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors));
+            assertTrue(errors.size()==1);
+            assertFalse(instance.isValidDirectoryPath("test2", "c:\\jeff", parent, false, errors));
+            assertTrue(errors.size()==2);
+            assertFalse(instance.isValidDirectoryPath("test3", "c:\\temp\\..\\etc", parent, false, errors));
+            assertTrue(errors.size()==3);
+
+            // Windows paths
+            assertTrue(instance.isValidDirectoryPath("test4", "C:\\", parent, false, errors));                        // Windows root directory
+            assertTrue(errors.size()==3);
+            assertTrue(instance.isValidDirectoryPath("test5", sysRoot, parent, false, errors));                  // Windows always exist directory
+            assertTrue(errors.size()==3);
+            assertFalse(instance.isValidDirectoryPath("test6", sysRoot + "\\System32\\cmd.exe", parent, false, errors));      // Windows command shell
+            assertTrue(errors.size()==4);
+
+            // Unix specific paths should not pass
+            assertFalse(instance.isValidDirectoryPath("test7", "/tmp", parent, false, errors));      // Unix Temporary directory
+            assertTrue(errors.size()==5);
+            assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors));   // Unix Standard shell
+            assertTrue(errors.size()==6);
+            assertFalse(instance.isValidDirectoryPath("test9", "/etc/config", parent, false, errors));
+            assertTrue(errors.size()==7);
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test10", "/etc/ridiculous", parent, false, errors));
+            assertTrue(errors.size()==8);
+            assertFalse(instance.isValidDirectoryPath("test11", "/tmp/../etc", parent, false, errors));
+            assertTrue(errors.size()==9);
+
+        } else {
+            // Windows paths should fail
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false));
+
+            // Standard Windows locations should fail
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\", parent, false));                        // Windows root directory
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\temp", parent, false));               // Windows temporary directory
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\System32\\cmd.exe", parent, false));   // Windows command shell
+
+            // Unix specific paths should pass
+            assertTrue(instance.isValidDirectoryPath("test", "/", parent, false));         // Root directory
+            assertTrue(instance.isValidDirectoryPath("test", "/bin", parent, false));      // Always exist directory
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false));   // Standard shell, not dir
+            assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false));
+
+            // Windows paths should fail
+            assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors));
+            assertTrue(errors.size()==1);
+            assertFalse(instance.isValidDirectoryPath("test2", "c:\\temp\\..\\etc", parent, false, errors));
+            assertTrue(errors.size()==2);
+
+            // Standard Windows locations should fail
+            assertFalse(instance.isValidDirectoryPath("test3", "c:\\", parent, false, errors));                        // Windows root directory
+            assertTrue(errors.size()==3);
+            assertFalse(instance.isValidDirectoryPath("test4", "c:\\Windows\\temp", parent, false, errors));               // Windows temporary directory
+            assertTrue(errors.size()==4);
+            assertFalse(instance.isValidDirectoryPath("test5", "c:\\Windows\\System32\\cmd.exe", parent, false, errors));   // Windows command shell
+            assertTrue(errors.size()==5);
+
+            // Unix specific paths should pass
+            assertTrue(instance.isValidDirectoryPath("test6", "/", parent, false, errors));         // Root directory
+            assertTrue(errors.size()==5);
+            assertTrue(instance.isValidDirectoryPath("test7", "/bin", parent, false, errors));      // Always exist directory
+            assertTrue(errors.size()==5);
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors));   // Standard shell, not dir
+            assertTrue(errors.size()==6);
+            assertFalse(instance.isValidDirectoryPath("test9", "/etc/ridiculous", parent, false, errors));
+            assertTrue(errors.size()==7);
+            assertFalse(instance.isValidDirectoryPath("test10", "/tmp/../etc", parent, false, errors));
+            assertTrue(errors.size()==8);
+        }
+    }
+
+    public void TestIsValidDirectoryPath() {
+        // isValidDirectoryPath(String, String, boolean)
+    }
+
+    public void testIsValidDouble() {
+        // isValidDouble(String, String, double, double, boolean)
+    	Validator instance = ESAPI.validator();
+    	ValidationErrorList errors = new ValidationErrorList();
+    	//testing negative range
+        assertFalse(instance.isValidDouble("test1", "-4", 1, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        assertTrue(instance.isValidDouble("test2", "-4", -10, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        //testing null value
+        assertTrue(instance.isValidDouble("test3", null, -10, 10, true, errors));
+        assertTrue(errors.size() == 1);
+        assertFalse(instance.isValidDouble("test4", null, -10, 10, false, errors));
+        assertTrue(errors.size() == 2);
+        //testing empty string
+        assertTrue(instance.isValidDouble("test5", "", -10, 10, true, errors));
+        assertTrue(errors.size() == 2);
+        assertFalse(instance.isValidDouble("test6", "", -10, 10, false, errors));
+        assertTrue(errors.size() == 3);
+        //testing improper range
+        assertFalse(instance.isValidDouble("test7", "50.0", 10, -10, false, errors));
+        assertTrue(errors.size() == 4);
+        //testing non-integers
+        assertTrue(instance.isValidDouble("test8", "4.3214", -10, 10, true, errors));
+        assertTrue(errors.size() == 4);
+        assertTrue(instance.isValidDouble("test9", "-1.65", -10, 10, true, errors));
+        assertTrue(errors.size() == 4);
+        //other testing
+        assertTrue(instance.isValidDouble("test10", "4", 1, 10, false, errors));
+        assertTrue(errors.size() == 4);
+        assertTrue(instance.isValidDouble("test11", "400", 1, 10000, false, errors));
+        assertTrue(errors.size() == 4);
+        assertTrue(instance.isValidDouble("test12", "400000000", 1, 400000000, false, errors));
+        assertTrue(errors.size() == 4);
+        assertFalse(instance.isValidDouble("test13", "4000000000000", 1, 10000, false, errors));
+        assertTrue(errors.size() == 5);
+        assertFalse(instance.isValidDouble("test14", "alsdkf", 10, 10000, false, errors));
+        assertTrue(errors.size() == 6);
+        assertFalse(instance.isValidDouble("test15", "--10", 10, 10000, false, errors));
+        assertTrue(errors.size() == 7);
+        assertFalse(instance.isValidDouble("test16", "14.1414234x", 10, 10000, false, errors));
+        assertTrue(errors.size() == 8);
+        assertFalse(instance.isValidDouble("test17", "Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 9);
+        assertFalse(instance.isValidDouble("test18", "-Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 10);
+        assertFalse(instance.isValidDouble("test19", "NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 11);
+        assertFalse(instance.isValidDouble("test20", "-NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 12);
+        assertFalse(instance.isValidDouble("test21", "+NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 13);
+        assertTrue(instance.isValidDouble("test22", "1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 13);
+        assertTrue(instance.isValidDouble("test23", "-1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 13);
+    }
+
+    public void testIsValidFileContent() {
+        System.out.println("isValidFileContent");
+        byte[] content = null;
+        try {
+            content = "This is some file content".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidFileContent("test", content, 100, false));
+    }
+
+    public void testIsValidFileName() {
+        System.out.println("isValidFileName");
+        Validator instance = ESAPI.validator();
+        assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false));
+        assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false));
+        assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false, errors));
+        assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false, errors));
+        assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false, errors));
+        assertTrue(errors.size() == 0);
+    }
+
+    public void testIsValidFileUpload() throws IOException {
+        System.out.println("isValidFileUpload");
+        String filepath = new File(System.getProperty("user.dir")).getCanonicalPath();
+        String filename = "aspect.jar";
+        File parent = new File("/").getCanonicalFile();
+        ValidationErrorList errors = new ValidationErrorList();
+        byte[] content = null;
+        try {
+            content = "This is some file content".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false));
+        assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors));
+        assertTrue(errors.size() == 0);
+
+        filepath = "/ridiculous";
+        filename = "aspect.jar";
+        try {
+            content = "This is some file content".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false));
+        assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors));
+        assertTrue(errors.size() == 1);
+    }
+
+    public void testIsValidHTTPRequestParameterSet() {
+        //		isValidHTTPRequestParameterSet(String, Set, Set)
+    }
+
+    public void testisValidInput() {
+        System.out.println("isValidInput");
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidInput("test", "jeff.williams at aspectsecurity.com", "Email", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff.williams@@aspectsecurity.com", "Email", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff.williams at aspectsecurity", "Email", 100, false));
+        assertTrue(instance.isValidInput("test", "jeff.wil'liams at aspectsecurity.com", "Email", 100, false));
+        assertTrue(instance.isValidInput("test", "jeff.wil''liams at aspectsecurity.com", "Email", 100, false));
+        assertTrue(instance.isValidInput("test", "123.168.100.234", "IPAddress", 100, false));
+        assertTrue(instance.isValidInput("test", "192.168.1.234", "IPAddress", 100, false));
+        assertFalse(instance.isValidInput("test", "..168.1.234", "IPAddress", 100, false));
+        assertFalse(instance.isValidInput("test", "10.x.1.234", "IPAddress", 100, false));
+        assertTrue(instance.isValidInput("test", "http://www.aspectsecurity.com", "URL", 100, false));
+        assertFalse(instance.isValidInput("test", "http:///www.aspectsecurity.com", "URL", 100, false));
+        assertFalse(instance.isValidInput("test", "http://www.aspect security.com", "URL", 100, false));
+        assertTrue(instance.isValidInput("test", "078-05-1120", "SSN", 100, false));
+        assertTrue(instance.isValidInput("test", "078 05 1120", "SSN", 100, false));
+        assertTrue(instance.isValidInput("test", "078051120", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "987-65-4320", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "000-00-0000", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "(555) 555-5555", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "test", "SSN", 100, false));
+        assertTrue(instance.isValidInput("test", "jeffWILLIAMS123", "HTTPParameterValue", 100, false));
+        assertTrue(instance.isValidInput("test", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false));
+        // Removed per Issue 116 - The '*' character is valid as a parameter character
+//        assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff^WILLIAMS", "HTTPParameterValue", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false));
+
+        assertTrue(instance.isValidInput("test", null, "Email", 100, true));
+        assertFalse(instance.isValidInput("test", null, "Email", 100, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+
+        assertTrue(instance.isValidInput("test1", "jeff.williams at aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidInput("test2", "jeff.williams@@aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidInput("test3", "jeff.williams at aspectsecurity", "Email", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test4", "jeff.wil'liams at aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test5", "jeff.wil''liams at aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test6", "123.168.100.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test7", "192.168.1.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertFalse(instance.isValidInput("test8", "..168.1.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==3);
+        assertFalse(instance.isValidInput("test9", "10.x.1.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidInput("test10", "http://www.aspectsecurity.com", "URL", 100, false, errors));
+        assertTrue(errors.size()==4);
+        assertFalse(instance.isValidInput("test11", "http:///www.aspectsecurity.com", "URL", 100, false, errors));
+        assertTrue(errors.size()==5);
+        assertFalse(instance.isValidInput("test12", "http://www.aspect security.com", "URL", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertTrue(instance.isValidInput("test13", "078-05-1120", "SSN", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertTrue(instance.isValidInput("test14", "078 05 1120", "SSN", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertTrue(instance.isValidInput("test15", "078051120", "SSN", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertFalse(instance.isValidInput("test16", "987-65-4320", "SSN", 100, false, errors));
+        assertTrue(errors.size()==7);
+        assertFalse(instance.isValidInput("test17", "000-00-0000", "SSN", 100, false, errors));
+        assertTrue(errors.size()==8);
+        assertFalse(instance.isValidInput("test18", "(555) 555-5555", "SSN", 100, false, errors));
+        assertTrue(errors.size()==9);
+        assertFalse(instance.isValidInput("test19", "test", "SSN", 100, false, errors));
+        assertTrue(errors.size()==10);
+        assertTrue(instance.isValidInput("test20", "jeffWILLIAMS123", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==10);
+        assertTrue(instance.isValidInput("test21", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==10);
+        // Removed per Issue 116 - The '*' character is valid as a parameter character
+//        assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false));
+        assertFalse(instance.isValidInput("test22", "jeff^WILLIAMS", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==11);
+        assertFalse(instance.isValidInput("test23", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==12);
+
+        assertTrue(instance.isValidInput("test", null, "Email", 100, true, errors));
+        assertFalse(instance.isValidInput("test", null, "Email", 100, false, errors));
+    }
+
+    public void testIsValidInteger() {
+        System.out.println("isValidInteger");
+        Validator instance = ESAPI.validator();
+        //testing negative range
+        assertFalse(instance.isValidInteger("test", "-4", 1, 10, false));
+        assertTrue(instance.isValidInteger("test", "-4", -10, 10, false));
+        //testing null value
+        assertTrue(instance.isValidInteger("test", null, -10, 10, true));
+        assertFalse(instance.isValidInteger("test", null, -10, 10, false));
+        //testing empty string
+        assertTrue(instance.isValidInteger("test", "", -10, 10, true));
+        assertFalse(instance.isValidInteger("test", "", -10, 10, false));
+        //testing improper range
+        assertFalse(instance.isValidInteger("test", "50", 10, -10, false));
+        //testing non-integers
+        assertFalse(instance.isValidInteger("test", "4.3214", -10, 10, true));
+        assertFalse(instance.isValidInteger("test", "-1.65", -10, 10, true));
+        //other testing
+        assertTrue(instance.isValidInteger("test", "4", 1, 10, false));
+        assertTrue(instance.isValidInteger("test", "400", 1, 10000, false));
+        assertTrue(instance.isValidInteger("test", "400000000", 1, 400000000, false));
+        assertFalse(instance.isValidInteger("test", "4000000000000", 1, 10000, false));
+        assertFalse(instance.isValidInteger("test", "alsdkf", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "--10", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "14.1414234x", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "Infinity", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "-Infinity", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "NaN", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "-NaN", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "+NaN", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "1e-6", -999999999, 999999999, false));
+        assertFalse(instance.isValidInteger("test", "-1e-6", -999999999, 999999999, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        //testing negative range
+        assertFalse(instance.isValidInteger("test1", "-4", 1, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        assertTrue(instance.isValidInteger("test2", "-4", -10, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        //testing null value
+        assertTrue(instance.isValidInteger("test3", null, -10, 10, true, errors));
+        assertTrue(errors.size() == 1);
+        assertFalse(instance.isValidInteger("test4", null, -10, 10, false, errors));
+        assertTrue(errors.size() == 2);
+        //testing empty string
+        assertTrue(instance.isValidInteger("test5", "", -10, 10, true, errors));
+        assertTrue(errors.size() == 2);
+        assertFalse(instance.isValidInteger("test6", "", -10, 10, false, errors));
+        assertTrue(errors.size() == 3);
+        //testing improper range
+        assertFalse(instance.isValidInteger("test7", "50", 10, -10, false, errors));
+        assertTrue(errors.size() == 4);
+        //testing non-integers
+        assertFalse(instance.isValidInteger("test8", "4.3214", -10, 10, true, errors));
+        assertTrue(errors.size() == 5);
+        assertFalse(instance.isValidInteger("test9", "-1.65", -10, 10, true, errors));
+        assertTrue(errors.size() == 6);
+        //other testing
+        assertTrue(instance.isValidInteger("test10", "4", 1, 10, false, errors));
+        assertTrue(errors.size() == 6);
+        assertTrue(instance.isValidInteger("test11", "400", 1, 10000, false, errors));
+        assertTrue(errors.size() == 6);
+        assertTrue(instance.isValidInteger("test12", "400000000", 1, 400000000, false, errors));
+        assertTrue(errors.size() == 6);
+        assertFalse(instance.isValidInteger("test13", "4000000000000", 1, 10000, false, errors));
+        assertTrue(errors.size() == 7);
+        assertFalse(instance.isValidInteger("test14", "alsdkf", 10, 10000, false, errors));
+        assertTrue(errors.size() == 8);
+        assertFalse(instance.isValidInteger("test15", "--10", 10, 10000, false, errors));
+        assertTrue(errors.size() == 9);
+        assertFalse(instance.isValidInteger("test16", "14.1414234x", 10, 10000, false, errors));
+        assertTrue(errors.size() == 10);
+        assertFalse(instance.isValidInteger("test17", "Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 11);
+        assertFalse(instance.isValidInteger("test18", "-Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 12);
+        assertFalse(instance.isValidInteger("test19", "NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 13);
+        assertFalse(instance.isValidInteger("test20", "-NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 14);
+        assertFalse(instance.isValidInteger("test21", "+NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 15);
+        assertFalse(instance.isValidInteger("test22", "1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 16);
+        assertFalse(instance.isValidInteger("test23", "-1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 17);
+
+    }
+
+    public void testIsValidListItem() {
+        System.out.println("isValidListItem");
+        Validator instance = ESAPI.validator();
+        List list = new ArrayList();
+        list.add("one");
+        list.add("two");
+        assertTrue(instance.isValidListItem("test", "one", list));
+        assertFalse(instance.isValidListItem("test", "three", list));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidListItem("test1", "one", list, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidListItem("test2", "three", list, errors));
+        assertTrue(errors.size()==1);
+    }
+
+    public void testIsValidNumber() {
+        System.out.println("isValidNumber");
+        Validator instance = ESAPI.validator();
+        //testing negative range
+        assertFalse(instance.isValidNumber("test", "-4", 1, 10, false));
+        assertTrue(instance.isValidNumber("test", "-4", -10, 10, false));
+        //testing null value
+        assertTrue(instance.isValidNumber("test", null, -10, 10, true));
+        assertFalse(instance.isValidNumber("test", null, -10, 10, false));
+        //testing empty string
+        assertTrue(instance.isValidNumber("test", "", -10, 10, true));
+        assertFalse(instance.isValidNumber("test", "", -10, 10, false));
+        //testing improper range
+        assertFalse(instance.isValidNumber("test", "5", 10, -10, false));
+        //testing non-integers
+        assertTrue(instance.isValidNumber("test", "4.3214", -10, 10, true));
+        assertTrue(instance.isValidNumber("test", "-1.65", -10, 10, true));
+        //other testing
+        assertTrue(instance.isValidNumber("test", "4", 1, 10, false));
+        assertTrue(instance.isValidNumber("test", "400", 1, 10000, false));
+        assertTrue(instance.isValidNumber("test", "400000000", 1, 400000000, false));
+        assertFalse(instance.isValidNumber("test", "4000000000000", 1, 10000, false));
+        assertFalse(instance.isValidNumber("test", "alsdkf", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "--10", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "14.1414234x", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "Infinity", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "-Infinity", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "NaN", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "-NaN", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "+NaN", 10, 10000, false));
+        assertTrue(instance.isValidNumber("test", "1e-6", -999999999, 999999999, false));
+        assertTrue(instance.isValidNumber("test", "-1e-6", -999999999, 999999999, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+      //testing negative range
+        assertFalse(instance.isValidNumber("test1", "-4", 1, 10, false, errors));
+        assertTrue(errors.size()==1);
+        assertTrue(instance.isValidNumber("test2", "-4", -10, 10, false, errors));
+        assertTrue(errors.size()==1);
+        //testing null value
+        assertTrue(instance.isValidNumber("test3", null, -10, 10, true, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidNumber("test4", null, -10, 10, false, errors));
+        assertTrue(errors.size()==2);
+        //testing empty string
+        assertTrue(instance.isValidNumber("test5", "", -10, 10, true, errors));
+        assertTrue(errors.size()==2);
+        assertFalse(instance.isValidNumber("test6", "", -10, 10, false, errors));
+        assertTrue(errors.size()==3);
+        //testing improper range
+        assertFalse(instance.isValidNumber("test7", "5", 10, -10, false, errors));
+        assertTrue(errors.size()==4);
+        //testing non-integers
+        assertTrue(instance.isValidNumber("test8", "4.3214", -10, 10, true, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidNumber("test9", "-1.65", -10, 10, true, errors));
+        assertTrue(errors.size()==4);
+        //other testing
+        assertTrue(instance.isValidNumber("test10", "4", 1, 10, false, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidNumber("test11", "400", 1, 10000, false, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidNumber("test12", "400000000", 1, 400000000, false, errors));
+        assertTrue(errors.size()==4);
+        assertFalse(instance.isValidNumber("test13", "4000000000000", 1, 10000, false, errors));
+        assertTrue(errors.size()==5);
+        assertFalse(instance.isValidNumber("test14", "alsdkf", 10, 10000, false, errors));
+        assertTrue(errors.size()==6);
+        assertFalse(instance.isValidNumber("test15", "--10", 10, 10000, false, errors));
+        assertTrue(errors.size()==7);
+        assertFalse(instance.isValidNumber("test16", "14.1414234x", 10, 10000, false, errors));
+        assertTrue(errors.size()==8);
+        assertFalse(instance.isValidNumber("test17", "Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size()==9);
+        assertFalse(instance.isValidNumber("test18", "-Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size()==10);
+        assertFalse(instance.isValidNumber("test19", "NaN", 10, 10000, false, errors));
+        assertTrue(errors.size()==11);
+        assertFalse(instance.isValidNumber("test20", "-NaN", 10, 10000, false, errors));
+        assertTrue(errors.size()==12);
+        assertFalse(instance.isValidNumber("test21", "+NaN", 10, 10000, false, errors));
+        assertTrue(errors.size()==13);
+        assertTrue(instance.isValidNumber("test22", "1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size()==13);
+        assertTrue(instance.isValidNumber("test23", "-1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size()==13);
+    }
+
+    public void testIsValidParameterSet() {
+        System.out.println("isValidParameterSet");
+        Set requiredNames = new HashSet();
+        requiredNames.add("p1");
+        requiredNames.add("p2");
+        requiredNames.add("p3");
+        Set optionalNames = new HashSet();
+        optionalNames.add("p4");
+        optionalNames.add("p5");
+        optionalNames.add("p6");
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        request.addParameter("p1", "value");
+        request.addParameter("p2", "value");
+        request.addParameter("p3", "value");
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames));
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames,errors));
+        assertTrue(errors.size()==0);
+        request.addParameter("p4", "value");
+        request.addParameter("p5", "value");
+        request.addParameter("p6", "value");
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames));
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors));
+        assertTrue(errors.size()==0);
+        request.removeParameter("p1");
+        assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames));
+        assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors));
+        assertTrue(errors.size() ==1);
+    }
+
+    public void testIsValidPrintable() {
+        System.out.println("isValidPrintable");
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidPrintable("name", "abcDEF", 100, false));
+        assertTrue(instance.isValidPrintable("name", "!@#R()*$;><()", 100, false));
+        char[] chars = {0x60, (char) 0xFF, 0x10, 0x25};
+        assertFalse(instance.isValidPrintable("name", chars, 100, false));
+        assertFalse(instance.isValidPrintable("name", "%08", 100, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidPrintable("name1", "abcDEF", 100, false, errors));
+        assertTrue(errors.size()==0);
+        assertTrue(instance.isValidPrintable("name2", "!@#R()*$;><()", 100, false, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidPrintable("name3", chars, 100, false, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidPrintable("name4", "%08", 100, false, errors));
+        assertTrue(errors.size()==2);
+
+    }
+
+    public void testIsValidRedirectLocation() {
+        //		isValidRedirectLocation(String, String, boolean)
+    }
+
+    public void testIsValidSafeHTML() {
+        System.out.println("isValidSafeHTML");
+        Validator instance = ESAPI.validator();
+
+        assertTrue(instance.isValidSafeHTML("test", "<b>Jeff</b>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <script>alert(document.cookie)</script>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <div style={xss:expression(xss)}>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <s%00cript>alert(document.cookie)</script>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <s\tcript>alert(document.cookie)</script>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <s\r\n\0cript>alert(document.cookie)</script>", 100, false));
+
+        // TODO: waiting for a way to validate text headed for an attribute for scripts
+        // This would be nice to catch, but just looks like text to AntiSamy
+        // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" "));
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidSafeHTML("test1", "<b>Jeff</b>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test2", "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test3", "Test. <script>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test4", "Test. <div style={xss:expression(xss)}>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test5", "Test. <s%00cript>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test6", "Test. <s\tcript>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test7", "Test. <s\r\n\0cript>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(errors.size() == 0);
+
+    }
+
+    public void testSafeReadLine() {
+        System.out.println("safeReadLine");
+
+        byte[] bytes = null;
+        try {
+            bytes = "testString".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e1) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        ByteArrayInputStream s = new ByteArrayInputStream(bytes);
+        Validator instance = ESAPI.validator();
+        try {
+            instance.safeReadLine(s, -1);
+            fail();
+        }
+        catch (ValidationException e) {
+            // Expected
+        }
+        s.reset();
+        try {
+            instance.safeReadLine(s, 4);
+            fail();
+        }
+        catch (ValidationException e) {
+            // Expected
+        }
+        s.reset();
+        try {
+            String u = instance.safeReadLine(s, 20);
+            assertEquals("testString", u);
+        }
+        catch (ValidationException e) {
+            fail();
+        }
+
+        // This sub-test attempts to validate that BufferedReader.readLine() and safeReadLine() are similar in operation
+        // for the nominal case
+        try {
+            s.reset();
+            InputStreamReader isr = new InputStreamReader(s);
+            BufferedReader br = new BufferedReader(isr);
+            String u = br.readLine();
+            s.reset();
+            String v = instance.safeReadLine(s, 20);
+            assertEquals(u, v);
+        }
+        catch (IOException e) {
+            fail();
+        }
+        catch (ValidationException e) {
+            fail();
+        }
+    }
+
+    public void testIssue82_SafeString_Bad_Regex() {
+        Validator instance = ESAPI.validator();
+        try {
+            instance.getValidInput("address", "55 main st. pasadena ak", "SafeString", 512, false);
+        }
+        catch (ValidationException e) {
+            fail(e.getLogMessage());
+        }
+    }
+
+    public void testGetParameterMap() {
+//testing Validator.HTTPParameterName and Validator.HTTPParameterValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+//an example of a parameter from displaytag, should pass
+        request.addParameter("d-49653-p", "pass");
+        request.addParameter("<img ", "fail");
+        request.addParameter(generateStringOfLength(32), "pass");
+        request.addParameter(generateStringOfLength(33), "fail");
+        assertEquals(safeRequest.getParameterMap().size(), 2);
+        assertNull(safeRequest.getParameterMap().get("<img"));
+        assertNull(safeRequest.getParameterMap().get(generateStringOfLength(33)));
+    }
+
+    public void testGetParameterNames() {
+//testing Validator.HTTPParameterName
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+//an example of a parameter from displaytag, should pass
+        request.addParameter("d-49653-p", "pass");
+        request.addParameter("<img ", "fail");
+        request.addParameter(generateStringOfLength(32), "pass");
+        request.addParameter(generateStringOfLength(33), "fail");
+        assertEquals(Collections.list(safeRequest.getParameterNames()).size(), 2);
+    }
+
+    public void testGetParameter() {
+//testing Validator.HTTPParameterValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.addParameter("p1", "Alice");
+        request.addParameter("p2", "bob at alice.com");//mail-address from a submit-form
+        request.addParameter("p3", ESAPI.authenticator().generateStrongPassword());
+        request.addParameter("p4", new String(EncoderConstants.CHAR_PASSWORD_SPECIALS));
+        //TODO - I think this should fair request.addParameter("p5", "�������?����"); //some special characters from european languages;
+        request.addParameter("f1", "<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>");
+        request.addParameter("f2", "<IMG SRC=javascript:alert('XSS')>");
+        request.addParameter("f3", "<IMG SRC=javascript:alert('XSS')>");
+        for (int i = 1; i <= 4; i++) {
+            assertTrue(safeRequest.getParameter("p" + i).equals(request.getParameter("p" + i)));
+        }
+        for (int i = 1; i <= 2; i++) {
+        	boolean testResult = false;
+        	try {
+        		testResult = safeRequest.getParameter("f" + i).equals(request.getParameter("f" + i));
+        	} catch (NullPointerException npe) {
+        		//the test is this block SHOULD fail. a NPE is an acceptable failure state
+        		testResult = false; //redundant, just being descriptive here
+        	}
+        	assertFalse(testResult);
+        }
+        assertNull(safeRequest.getParameter("e1"));
+
+        //This is revealing problems with Jeff's original SafeRequest
+        //mishandling of the AllowNull parameter. I'm adding a new Google code
+        //bug to track this.
+        //
+        //assertNotNull(safeRequest.getParameter("e1", false));
+    }
+
+    public void testGetCookies() {
+//testing Validator.HTTPCookieName and Validator.HTTPCookieValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+//should support a base64-encode value
+        request.setCookie("p1", "34=VJhjv7jiDu7tsdLrQQ2KcUwpfWUM2_mBae6UA8ttk4wBHdxxQ-1IBxyCOn3LWE08SDhpnBcJ7N5Vze48F2t8a1R_hXt7PX1BvgTM0pn-T4JkqGTm_tlmV4RmU3GT-dgn");
+        request.setCookie("f1", "<A HREF=\"http://66.102.7.147/\">XSS</A>");
+        request.setCookie("load-balancing", "pass");
+        request.setCookie("'bypass", "fail");
+        Cookie[] cookies = safeRequest.getCookies();
+        assertEquals(cookies[0].getValue(), request.getCookies()[0].getValue());
+        assertEquals(cookies[1].getName(), request.getCookies()[2].getName());
+        assertTrue(cookies.length == 2);
+    }
+
+    public void testGetHeader() {
+//testing Validator.HTTPHeaderValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.addHeader("p1", "login");
+        request.addHeader("f1", "<A HREF=\"http://0x42.0x0000066.0x7.0x93/\">XSS</A>");
+        request.addHeader("p2", generateStringOfLength(150));
+        request.addHeader("f2", generateStringOfLength(151));
+        assertEquals(safeRequest.getHeader("p1"), request.getHeader("p1"));
+        assertEquals(safeRequest.getHeader("p2"), request.getHeader("p2"));
+        assertFalse(safeRequest.getHeader("f1").equals(request.getHeader("f1")));
+        assertFalse(safeRequest.getHeader("f2").equals(request.getHeader("f2")));
+        assertNull(safeRequest.getHeader("p3"));
+    }
+
+    public void testGetHeaderNames() {
+//testing Validator.HTTPHeaderName
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.addHeader("d-49653-p", "pass");
+        request.addHeader("<img ", "fail");
+        request.addHeader(generateStringOfLength(32), "pass");
+        request.addHeader(generateStringOfLength(33), "fail");
+        assertEquals(Collections.list(safeRequest.getHeaderNames()).size(), 2);
+    }
+
+    public void testGetQueryString() {
+//testing Validator.HTTPQueryString
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.setQueryString("mail=bob at alice.com&passwd=" + new String(EncoderConstants.CHAR_PASSWORD_SPECIALS));// TODO, fix this + "&special=�����");
+        assertEquals(safeRequest.getQueryString(), request.getQueryString());
+        request.setQueryString("mail=<IMG SRC=\"jav\tascript:alert('XSS');\">");
+        assertFalse(safeRequest.getQueryString().equals(request.getQueryString()));
+        request.setQueryString("mail=bob at alice.com-passwd=johny");
+        assertTrue(safeRequest.getQueryString().equals(request.getQueryString()));
+        request.setQueryString("mail=bob at alice.com-passwd=johny&special"); //= is missing!
+        assertFalse(safeRequest.getQueryString().equals(request.getQueryString()));
+    }
+
+    public void testGetRequestURI() {
+//testing Validator.HTTPURI
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        try {
+            request.setRequestURI("/app/page.jsp");
+        } catch (UnsupportedEncodingException ignored) {
+        }
+        assertEquals(safeRequest.getRequestURI(), request.getRequestURI());
+    }
+
+    private String generateStringOfLength(int length) {
+        StringBuilder longString = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            longString.append("a");
+        }
+        return longString.toString();
+    }
+
+    public void testGetContextPath() {
+        // Root Context Path ("")
+        assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "", "HTTPContextPath", 512, true));
+        // Deployed Context Path ("/context")
+        assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "/context", "HTTPContextPath", 512, true));
+        // Fail-case - URL Splitting
+        assertFalse(ESAPI.validator().isValidInput("HTTPContextPath", "/\\nGET http://evil.com", "HTTPContextPath", 512, true));
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java b/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java
new file mode 100644
index 0000000..b3c2a6d
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java
@@ -0,0 +1,358 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+
+
+/**
+ * The Class AccessControllerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AccessControllerTest extends TestCase {
+
+	/**
+	 * Instantiates a new access controller test.
+	 * 
+	 * @param testName
+     *            the test name
+     * @throws Exception
+	 */
+	public AccessControllerTest(String testName) throws Exception {
+		super(testName);
+		
+		Authenticator authenticator = ESAPI.authenticator();
+		String password = authenticator.generateStrongPassword();
+
+		// create a user with the "user" role for this test
+		User alice = authenticator.getUser("testuser1");
+		if ( alice == null ) {
+			alice = authenticator.createUser( "testuser1", password, password);
+		}
+		alice.addRole("user");		
+
+		// create a user with the "admin" role for this test
+		User bob = authenticator.getUser("testuser2");
+		if ( bob == null ) {
+			bob = authenticator.createUser( "testuser2", password, password);
+		}
+		bob.addRole("admin");
+		
+		// create a user with the "user" and "admin" roles for this test
+		User mitch = authenticator.getUser("testuser3");
+		if ( mitch == null ) {
+			mitch = authenticator.createUser( "testuser3", password, password);
+		}
+		mitch.addRole("admin");
+		mitch.addRole("user");
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AccessControllerTest.class);
+		return suite;
+	}
+
+    /**
+     *
+     */
+    public void testMatchRule() {
+		ESAPI.authenticator().setCurrentUser(null);
+		assertFalse(ESAPI.accessController().isAuthorizedForURL("/nobody"));
+	}
+	
+	/**
+	 * Test of isAuthorizedForURL method, of class
+	 * org.owasp.esapi.AccessController.
+     *
+     * @throws Exception
+     */
+	public void testIsAuthorizedForURL() throws Exception {
+		System.out.println("isAuthorizedForURL");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertFalse(instance.isAuthorizedForURL("/nobody"));
+		assertFalse(instance.isAuthorizedForURL("/test/admin"));
+		assertTrue(instance.isAuthorizedForURL("/test/user"));
+		assertTrue(instance.isAuthorizedForURL("/test/all"));
+		assertFalse(instance.isAuthorizedForURL("/test/none"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.gif"));
+		assertFalse(instance.isAuthorizedForURL("/test/none/test.exe"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.png"));
+		assertFalse(instance.isAuthorizedForURL("/test/moderator"));
+		assertTrue(instance.isAuthorizedForURL("/test/profile"));
+		assertFalse(instance.isAuthorizedForURL("/upload"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForURL("/nobody"));
+		assertTrue(instance.isAuthorizedForURL("/test/admin"));
+		assertFalse(instance.isAuthorizedForURL("/test/user"));
+		assertTrue(instance.isAuthorizedForURL("/test/all"));
+		assertFalse(instance.isAuthorizedForURL("/test/none"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.png"));
+		assertFalse(instance.isAuthorizedForURL("/test/moderator"));
+		assertTrue(instance.isAuthorizedForURL("/test/profile"));
+		assertFalse(instance.isAuthorizedForURL("/upload"));
+		
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertFalse(instance.isAuthorizedForURL("/nobody"));
+		assertTrue(instance.isAuthorizedForURL("/test/admin"));
+		assertTrue(instance.isAuthorizedForURL("/test/user"));
+		assertTrue(instance.isAuthorizedForURL("/test/all"));
+		assertFalse(instance.isAuthorizedForURL("/test/none"));
+		assertTrue(instance.isAuthorizedForURL("/test/none/test.png"));
+		assertFalse(instance.isAuthorizedForURL("/test/moderator"));
+		assertTrue(instance.isAuthorizedForURL("/test/profile"));
+		assertFalse(instance.isAuthorizedForURL("/upload"));
+		
+		try {
+			instance.assertAuthorizedForURL("/test/admin");
+			instance.assertAuthorizedForURL( "/nobody" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of isAuthorizedForFunction method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForFunction() {
+		System.out.println("isAuthorizedForFunction");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForFunction("/FunctionA"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionAdeny"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionB"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionBdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionC"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionCdeny"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForFunction("/FunctionA"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionAdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionB"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionBdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionD"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionDdeny"));
+
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForFunction("/FunctionA"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionAdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionB"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionBdeny"));
+		assertTrue(instance.isAuthorizedForFunction("/FunctionC"));
+		assertFalse(instance.isAuthorizedForFunction("/FunctionCdeny"));
+
+		try {
+			instance.assertAuthorizedForFunction("/FunctionA");
+			instance.assertAuthorizedForFunction( "/FunctionDdeny" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of isAuthorizedForData method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForData() {
+		System.out.println("isAuthorizedForData");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		Class adminR = null;
+		Class adminRW = null;
+		Class userW = null;
+		Class userRW = null;
+		Class anyR = null;
+		Class userAdminR = null;
+		Class userAdminRW = null;
+		Class undefined = null;
+		
+		try{
+			adminR = Class.forName("java.util.ArrayList");
+			adminRW = Class.forName("java.lang.Math");
+			userW = Class.forName("java.util.Date");
+			userRW = Class.forName("java.lang.String");
+			anyR = Class.forName("java.io.BufferedReader");
+			userAdminR = Class.forName("java.util.Random");
+			userAdminRW = Class.forName("java.awt.event.MouseWheelEvent");
+			undefined = Class.forName("java.io.FileWriter");
+			
+		}catch(ClassNotFoundException cnf){
+			System.out.println("CLASS NOT FOUND.");
+			cnf.printStackTrace();
+		}
+		//test User
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForData("read", userRW));
+		assertFalse(instance.isAuthorizedForData("read", undefined));
+		assertFalse(instance.isAuthorizedForData("write", undefined));
+		assertFalse(instance.isAuthorizedForData("read", userW));
+		assertFalse(instance.isAuthorizedForData("read", adminRW));
+		assertTrue(instance.isAuthorizedForData("write", userRW));
+		assertTrue(instance.isAuthorizedForData("write", userW));
+		assertFalse(instance.isAuthorizedForData("write", anyR));
+		assertTrue(instance.isAuthorizedForData("read", anyR));
+		assertTrue(instance.isAuthorizedForData("read", userAdminR));
+		assertTrue(instance.isAuthorizedForData("write", userAdminRW));
+		
+		//test Admin
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertTrue(instance.isAuthorizedForData("read", adminRW));
+		assertFalse(instance.isAuthorizedForData("read", undefined));
+		assertFalse(instance.isAuthorizedForData("write", undefined));
+		assertFalse(instance.isAuthorizedForData("read", userRW));
+		assertTrue(instance.isAuthorizedForData("write", adminRW));
+		assertFalse(instance.isAuthorizedForData("write", anyR));
+		assertTrue(instance.isAuthorizedForData("read", anyR));
+		assertTrue(instance.isAuthorizedForData("read", userAdminR));
+		assertTrue(instance.isAuthorizedForData("write", userAdminRW));
+		
+		//test User/Admin
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForData("read", userRW));
+		assertFalse(instance.isAuthorizedForData("read", undefined));
+		assertFalse(instance.isAuthorizedForData("write", undefined));
+		assertFalse(instance.isAuthorizedForData("read", userW));
+		assertTrue(instance.isAuthorizedForData("read", adminR));
+		assertTrue(instance.isAuthorizedForData("write", userRW));
+		assertTrue(instance.isAuthorizedForData("write", userW));
+		assertFalse(instance.isAuthorizedForData("write", anyR));
+		assertTrue(instance.isAuthorizedForData("read", anyR));
+		assertTrue(instance.isAuthorizedForData("read", userAdminR));
+		assertTrue(instance.isAuthorizedForData("write", userAdminRW));
+		try {
+			instance.assertAuthorizedForData("read", userRW);
+			instance.assertAuthorizedForData( "write", adminR );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+		
+	}
+
+	/**
+	 * Test of isAuthorizedForFile method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForFile() {
+		System.out.println("isAuthorizedForFile");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForFile("/Dir/File1"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/File2"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File3"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForFile("/Dir/File1"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File2"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File4"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForFile("/Dir/File1"));
+		assertTrue(instance.isAuthorizedForFile("/Dir/File2"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/File5"));
+		assertFalse(instance.isAuthorizedForFile("/Dir/ridiculous"));
+
+		try {
+			instance.assertAuthorizedForFile("/Dir/File1");
+			instance.assertAuthorizedForFile( "/Dir/File6" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of isAuthorizedForService method, of class
+	 * org.owasp.esapi.AccessController.
+	 */
+	public void testIsAuthorizedForService() {
+		System.out.println("isAuthorizedForService");
+		AccessController instance = ESAPI.accessController();
+		Authenticator auth = ESAPI.authenticator();
+		
+		auth.setCurrentUser( auth.getUser("testuser1") );
+		assertTrue(instance.isAuthorizedForService("/services/ServiceA"));
+		assertFalse(instance.isAuthorizedForService("/services/ServiceB"));
+		assertTrue(instance.isAuthorizedForService("/services/ServiceC"));
+		
+		assertFalse(instance.isAuthorizedForService("/test/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser2") );
+		assertFalse(instance.isAuthorizedForService("/services/ServiceA"));
+		assertTrue(instance.isAuthorizedForService("/services/ServiceB"));
+		assertFalse(instance.isAuthorizedForService("/services/ServiceF"));
+		assertFalse(instance.isAuthorizedForService("/test/ridiculous"));
+
+		auth.setCurrentUser( auth.getUser("testuser3") );
+		assertTrue(instance.isAuthorizedForService("/services/ServiceA"));
+		assertTrue(instance.isAuthorizedForService("/services/ServiceB"));
+		assertFalse(instance.isAuthorizedForService("/services/ServiceE"));
+		assertFalse(instance.isAuthorizedForService("/test/ridiculous"));
+
+		try {
+			instance.assertAuthorizedForService("/services/ServiceD");
+			instance.assertAuthorizedForService( "/test/ridiculous" );
+			fail();
+		} catch ( AccessControlException e ) {
+			// expected
+		}
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/AccessReferenceMapTest.java b/src/test/java/org/owasp/esapi/reference/AccessReferenceMapTest.java
new file mode 100644
index 0000000..ec98329
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/AccessReferenceMapTest.java
@@ -0,0 +1,223 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The Class AccessReferenceMapTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AccessReferenceMapTest extends TestCase {
+    
+    /**
+	 * Instantiates a new access reference map test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public AccessReferenceMapTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(AccessReferenceMapTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AuthenticationException
+     *             the authentication exception
+     * @throws EncryptionException
+	 */
+    public void testUpdate() throws AuthenticationException, EncryptionException {
+        System.out.println("update");
+    	RandomAccessReferenceMap arm = new RandomAccessReferenceMap();
+    	Authenticator auth = ESAPI.authenticator();
+    	
+    	String pass = auth.generateStrongPassword();
+    	User u = auth.createUser( "armUpdate", pass, pass );
+    	
+    	// test to make sure update returns something
+		arm.update(auth.getUserNames());
+		String indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect == null ) fail();
+		
+		// test to make sure update removes items that are no longer in the list
+		auth.removeUser( u.getAccountName() );
+		arm.update(auth.getUserNames());
+		indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect != null ) fail();
+		
+		// test to make sure old indirect reference is maintained after an update
+		arm.update(auth.getUserNames());
+		String newIndirect = arm.getIndirectReference( u.getAccountName() );
+		assertEquals(indirect, newIndirect);
+    }
+    
+    
+    /**
+	 * Test of iterator method, of class org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testIterator() {
+        System.out.println("iterator");
+    	RandomAccessReferenceMap arm = new RandomAccessReferenceMap();
+        Authenticator auth = ESAPI.authenticator();
+        
+		arm.update(auth.getUserNames());
+
+		Iterator i = arm.iterator();
+		while ( i.hasNext() ) {
+			String userName = (String)i.next();
+			User u = auth.getUser( userName );
+			if ( u == null ) fail();
+		}
+    }
+    
+    /**
+	 * Test of getIndirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testGetIndirectReference() {
+        System.out.println("getIndirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String expResult = directReference;
+        String result = instance.getIndirectReference(directReference);
+        assertNotSame(expResult, result);        
+    }
+
+    /**
+	 * Test of getDirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AccessControlException
+	 *             the access control exception
+	 */
+    public void testGetDirectReference() throws AccessControlException {
+        System.out.println("getDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String ind = instance.getIndirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+        try {
+        	instance.getDirectReference("invalid");
+        	fail();
+        } catch( AccessControlException e ) {
+        	// success
+        }
+    }
+    
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testAddDirectReference() throws AccessControlException {
+        System.out.println("addDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String newDirect = instance.addDirectReference("newDirect");
+        assertNotNull( newDirect );
+        String ind = instance.addDirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+    	String newInd = instance.addDirectReference(directReference);
+    	assertEquals(ind, newInd);
+    }
+
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testRemoveDirectReference() throws AccessControlException {
+        System.out.println("removeDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        RandomAccessReferenceMap instance = new RandomAccessReferenceMap( list );
+        
+        String indirect = instance.getIndirectReference(directReference);
+        assertNotNull(indirect);
+        String deleted = instance.removeDirectReference(directReference);
+        assertEquals(indirect,deleted);
+    	deleted = instance.removeDirectReference("ridiculous");
+    	assertNull(deleted);
+    }
+    
+    
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java b/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java
new file mode 100644
index 0000000..31699cc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java
@@ -0,0 +1,617 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class AuthenticatorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class AuthenticatorTest extends TestCase {
+
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AuthenticatorTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Instantiates a new authenticator test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public AuthenticatorTest(String testName) {
+		super(testName);
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	
+	/**
+	 * Test of createAccount method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+     *             the authentication exception
+     * @throws EncryptionException
+	 */
+	public void testCreateUser() throws AuthenticationException, EncryptionException {
+		System.out.println("createUser");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(accountName, password, password);
+		assertTrue(user.verifyPassword(password));
+        try {
+            instance.createUser(accountName, password, password); // duplicate user
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS), "password1", "password2"); // don't match
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS), "weak1", "weak1");  // weak password
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(null, "weak1", "weak1");  // null username
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+            instance.createUser(ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS), null, null);  // null password
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+        try {
+        	String uName = "ea234kEknr";	//sufficiently random password that also works as a username
+            instance.createUser(uName, uName, uName);  // using username as password
+            fail();
+        } catch (AuthenticationException e) {
+            // success
+        }
+	}
+
+	/**
+	 * Test of generateStrongPassword method, of class
+	 * org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGenerateStrongPassword() throws AuthenticationException {
+		System.out.println("generateStrongPassword");		
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = "iiiiiiiiii";  // i is not allowed in passwords - this prevents failures from containing pieces of old password
+		String newPassword = null;
+		String username = "FictionalEsapiUser";
+		User user = new DefaultUser(username);
+		for (int i = 0; i < 100; i++) {
+            try {
+                newPassword = instance.generateStrongPassword();
+                instance.verifyPasswordStrength(oldPassword, newPassword, user);
+            } catch( AuthenticationException e ) {
+            	System.out.println( "  FAILED >> " + newPassword + " : " + e.getLogMessage());
+                fail();
+            }
+		}
+		try {
+			instance.verifyPasswordStrength("test56^$test", "abcdx56^$sl", user );
+		} catch( AuthenticationException e ) {
+			// expected
+		}
+	}
+
+
+	/**
+	 * Test of getCurrentUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+     *
+     * @throws Exception
+     */
+	public void testGetCurrentUser() throws Exception {
+		System.out.println("getCurrentUser");
+        Authenticator instance = ESAPI.authenticator();
+		String username1 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String username2 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user1 = instance.createUser(username1, "getCurrentUser", "getCurrentUser");
+		User user2 = instance.createUser(username2, "getCurrentUser", "getCurrentUser");		
+		user1.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		user1.loginWithPassword("getCurrentUser");
+		User currentUser = instance.getCurrentUser();
+		assertEquals( currentUser, user1 );
+		instance.setCurrentUser( user2 );
+		assertFalse( currentUser.getAccountName().equals( user2.getAccountName() ) );
+		
+		Runnable echo = new Runnable() {
+			private int count = 1;
+            private boolean result = false;
+			public void run() {
+		        Authenticator auth = ESAPI.authenticator();
+				User a = null;
+				try {
+					String password = auth.generateStrongPassword();
+					String accountName = "TestAccount" + count++;
+					a = auth.getUser(accountName);
+					if ( a != null ) {
+						auth.removeUser(accountName);
+					}
+					a = auth.createUser(accountName, password, password);
+					auth.setCurrentUser(a);
+				} catch (AuthenticationException e) {
+					e.printStackTrace();
+				}
+				User b = auth.getCurrentUser();
+				result &= a.equals(b);
+			}
+		};
+        ThreadGroup tg = new ThreadGroup("test");
+		for ( int i = 0; i<10; i++ ) {
+			new Thread( tg, echo ).start();
+		}
+        while (tg.activeCount() > 0 ) {
+            Thread.sleep(100);
+        }
+	}
+
+	/**
+	 * Test of getUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetUser() throws AuthenticationException {
+		System.out.println("getUser");
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		instance.createUser(accountName, password, password);
+		assertNotNull(instance.getUser( accountName ));
+		assertNull(instance.getUser( ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS) ));
+	}
+	
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AuthenticationException
+     */
+    public void testGetUserFromRememberToken() throws AuthenticationException {
+		System.out.println("getUserFromRememberToken");
+        Authenticator instance = ESAPI.authenticator();
+        instance.logout();  // in case anyone is logged in
+		String password = instance.generateStrongPassword();
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user = instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		
+		System.out.println("getUserFromRememberToken - expecting failure");
+		request.setCookie( HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME, "ridiculous" );
+		try {
+			instance.login( request, response );  // wrong cookie will fail
+		} catch( AuthenticationException e ) {
+			// expected
+		}
+
+		System.out.println("getUserFromRememberToken - expecting success");
+		request = new MockHttpServletRequest();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		ESAPI.authenticator().setCurrentUser(user);
+		String newToken = ESAPI.httpUtilities().setRememberToken(request, response, password, 10000, "test.com", request.getContextPath() );
+		request.setCookie( HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME, newToken );
+        user.logout();  // logout the current user so we can log them in with the remember cookie
+		User test2 = instance.login( request, response );
+		assertSame( user, test2 );
+	}
+	
+
+	
+	/**
+	 * Test get user from session.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetUserFromSession() throws AuthenticationException {
+		System.out.println("getUserFromSession");
+        FileBasedAuthenticator instance = (FileBasedAuthenticator)ESAPI.authenticator();
+        instance.logout();  // in case anyone is logged in
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", accountName);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP( request, response );
+		instance.login( request, response);
+		User test = instance.getUserFromSession();
+		assertEquals( user, test );
+	}
+
+	/**
+	 * Test get user names.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetUserNames() throws AuthenticationException {
+		System.out.println("getUserNames");
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		String[] testnames = new String[10];
+		for(int i=0;i<testnames.length;i++) {
+			testnames[i] = ESAPI.randomizer().getRandomString(8,EncoderConstants.CHAR_ALPHANUMERICS);
+		}
+		for(int i=0;i<testnames.length;i++) {
+			instance.createUser(testnames[i], password, password);
+		}
+		Set names = instance.getUserNames();
+		for(int i=0;i<testnames.length;i++) {
+			assertTrue(names.contains(testnames[i].toLowerCase()));
+		}
+	}
+	
+	/**
+	 * Test of hashPassword method, of class org.owasp.esapi.Authenticator.
+     *
+     * @throws EncryptionException
+     */
+	public void testHashPassword() throws EncryptionException {
+		System.out.println("hashPassword");
+		String username = "Jeff";
+		String password = "test";
+        Authenticator instance = ESAPI.authenticator();
+		String result1 = instance.hashPassword(password, username);
+		String result2 = instance.hashPassword(password, username);
+		assertTrue(result1.equals(result2));
+	}
+
+	/**
+	 * Test of login method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLogin() throws AuthenticationException {
+		System.out.println("login");
+        Authenticator instance = ESAPI.authenticator();
+        String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(username, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", username);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		User test = instance.login( request, response);
+		assertTrue( test.isLoggedIn() );
+	}
+	
+	/**
+	 * Test of removeAccount method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testRemoveUser() throws Exception {
+		System.out.println("removeUser");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		instance.createUser(accountName, password, password);
+		assertTrue( instance.exists(accountName));
+		instance.removeUser(accountName);
+		assertFalse( instance.exists(accountName));
+	}
+
+	/**
+	 * Test of saveUsers method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testSaveUsers() throws Exception {
+		System.out.println("saveUsers");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        FileBasedAuthenticator instance = (FileBasedAuthenticator)ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		instance.createUser(accountName, password, password);
+		instance.saveUsers();
+		assertNotNull( instance.getUser(accountName) );
+		instance.removeUser(accountName);
+		assertNull( instance.getUser(accountName) );
+	}
+
+	
+	/**
+	 * Test of setCurrentUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetCurrentUser() throws AuthenticationException {
+		System.out.println("setCurrentUser");
+        final Authenticator instance = ESAPI.authenticator();
+		String user1 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_UPPERS);
+		String user2 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_UPPERS);
+		User userOne = instance.createUser(user1, "getCurrentUser", "getCurrentUser");
+		userOne.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		userOne.loginWithPassword("getCurrentUser");
+		User currentUser = instance.getCurrentUser();
+		assertEquals( currentUser, userOne );
+		User userTwo = instance.createUser(user2, "getCurrentUser", "getCurrentUser");		
+		instance.setCurrentUser( userTwo );
+		assertFalse( currentUser.getAccountName().equals( userTwo.getAccountName() ) );
+		
+		Runnable echo = new Runnable() {
+			private int count = 1;
+			public void run() {
+				User u=null;
+				try {
+					String password = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+					u = instance.createUser("test" + count++, password, password);
+					instance.setCurrentUser(u);
+					ESAPI.getLogger("test").info( Logger.SECURITY_SUCCESS, "Got current user" );
+					// ESAPI.authenticator().removeUser( u.getAccountName() );
+				} catch (AuthenticationException e) {
+					e.printStackTrace();
+				}
+			}
+		};
+		for ( int i = 0; i<10; i++ ) {
+			new Thread( echo ).start();
+		}
+	}
+	
+
+	/**
+	 * Test of setCurrentUser method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetCurrentUserWithRequest() throws AuthenticationException {
+		System.out.println("setCurrentUser(req,resp)");
+        Authenticator instance = ESAPI.authenticator();
+        instance.logout();  // in case anyone is logged in
+		String password = instance.generateStrongPassword();
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		DefaultUser user = (DefaultUser) instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", accountName);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		instance.login( request, response );
+		assertEquals( user, instance.getCurrentUser() );
+		try {
+			user.disable();
+			instance.login( request, response );
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			user.enable();
+			user.lock();
+			instance.login( request, response );
+		} catch( Exception e ) {
+			// expected
+		}
+		try {
+			user.unlock();
+			user.setExpirationTime( new Date() );
+			instance.login( request, response );
+		} catch( Exception e ) {
+			// expected
+		}
+	}
+	
+	
+	
+	/**
+	 * Test of validatePasswordStrength method, of class
+	 * org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testValidatePasswordStrength() throws AuthenticationException {
+		System.out.println("validatePasswordStrength");
+        Authenticator instance = ESAPI.authenticator();
+        
+        String username = "FictionalEsapiUser";
+		User user = new DefaultUser(username);
+
+		// should fail
+		try {
+			instance.verifyPasswordStrength("password", "jeff", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("diff123bang", "same123string", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "JEFF", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "1234", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "password", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "-1", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "password123", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "test123", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		//jtm - 11/16/2010 - fix for bug http://code.google.com/p/owasp-esapi-java/issues/detail?id=108
+		try {
+			instance.verifyPasswordStrength("password", "FictionalEsapiUser", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+		try {
+			instance.verifyPasswordStrength("password", "FICTIONALESAPIUSER", user);
+			fail();
+		} catch (AuthenticationException e) {
+			// success
+		}
+
+		// should pass
+		instance.verifyPasswordStrength("password", "jeffJEFF12!", user);
+		instance.verifyPasswordStrength("password", "super calif ragil istic", user);
+		instance.verifyPasswordStrength("password", "TONYTONYTONYTONY", user);
+		instance.verifyPasswordStrength("password", instance.generateStrongPassword(), user);
+
+        // chrisisbeef - Issue 65 - http://code.google.com/p/owasp-esapi-java/issues/detail?id=65
+        instance.verifyPasswordStrength("password", "b!gbr0ther", user);
+	}
+
+	/**
+	 * Test of exists method, of class org.owasp.esapi.Authenticator.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testExists() throws Exception {
+		System.out.println("exists");
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator instance = ESAPI.authenticator();
+		String password = instance.generateStrongPassword();
+		instance.createUser(accountName, password, password);
+		assertTrue(instance.exists(accountName));
+		instance.removeUser(accountName);
+		assertFalse(instance.exists(accountName));
+	}
+
+    /**
+     * Test of main method, of class org.owasp.esapi.Authenticator.
+     * @throws Exception
+     */
+    public void testMain() throws Exception {
+        System.out.println("Authenticator Main");
+        Authenticator instance = ESAPI.authenticator();
+        String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        String password = instance.generateStrongPassword();
+        String role = "test";
+        
+        // test wrong parameters - missing role parameter
+        String[] badargs = { accountName, password };
+        FileBasedAuthenticator.main( badargs );
+        // load users since the new user was added in another instance
+        ((FileBasedAuthenticator)instance).loadUsersImmediately();
+        User u1 = instance.getUser(accountName);
+        assertNull( u1 );
+
+        // test good parameters
+        String[] args = { accountName, password, role };
+        FileBasedAuthenticator.main(args);
+        // load users since the new user was added in another instance
+        ((FileBasedAuthenticator)instance).loadUsersImmediately();
+        DefaultUser u2 = (DefaultUser) instance.getUser(accountName);
+        assertNotNull( u2 );
+        assertTrue( u2.isInRole(role));
+        assertEquals( instance.hashPassword(password, accountName), ((FileBasedAuthenticator)instance).getHashedPassword(u2) );
+    }
+    
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java
new file mode 100644
index 0000000..4e1d1fe
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java
@@ -0,0 +1,390 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.ConfigurationException;
+
+public class DefaultSecurityConfigurationTest {
+
+	private DefaultSecurityConfiguration createWithProperty(String key, String val) {
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(key, val);
+		return new DefaultSecurityConfiguration(properties);
+	}
+	
+	@Test
+	public void testGetApplicationName() {
+		final String expected = "ESAPI_UnitTests";
+		DefaultSecurityConfiguration secConf = this.createWithProperty(DefaultSecurityConfiguration.APPLICATION_NAME, expected);
+		Assert.assertEquals(expected, secConf.getApplicationName());
+	}
+	
+	@Test
+	public void testGetLogImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_LOG_IMPLEMENTATION, secConf.getLogImplementation());
+		
+		final String expected = "TestLogger";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getLogImplementation());
+	}
+	
+	@Test
+	public void testAuthenticationImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_AUTHENTICATION_IMPLEMENTATION, secConf.getAuthenticationImplementation());
+		
+		final String expected = "TestAuthentication";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.AUTHENTICATION_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getAuthenticationImplementation());
+	}
+	
+	@Test
+	public void testEncoderImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCODER_IMPLEMENTATION, secConf.getEncoderImplementation());
+		
+		final String expected = "TestEncoder";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCODER_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getEncoderImplementation());
+	}
+	
+	@Test
+	public void testAccessControlImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ACCESS_CONTROL_IMPLEMENTATION, secConf.getAccessControlImplementation());
+		
+		final String expected = "TestAccessControl";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ACCESS_CONTROL_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getAccessControlImplementation());
+	}
+	
+	@Test
+	public void testEncryptionImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCRYPTION_IMPLEMENTATION, secConf.getEncryptionImplementation());
+		
+		final String expected = "TestEncryption";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCRYPTION_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getEncryptionImplementation());
+	}
+	
+	@Test
+	public void testIntrusionDetectionImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION, secConf.getIntrusionDetectionImplementation());
+		
+		final String expected = "TestIntrusionDetection";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.INTRUSION_DETECTION_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getIntrusionDetectionImplementation());
+	}
+	
+	@Test
+	public void testRandomizerImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_RANDOMIZER_IMPLEMENTATION, secConf.getRandomizerImplementation());
+		
+		final String expected = "TestRandomizer";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.RANDOMIZER_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getRandomizerImplementation());
+	}
+	
+	@Test
+	public void testExecutorImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_EXECUTOR_IMPLEMENTATION, secConf.getExecutorImplementation());
+		
+		final String expected = "TestExecutor";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.EXECUTOR_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getExecutorImplementation());
+	}
+	
+	@Test
+	public void testHTTPUtilitiesImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_HTTP_UTILITIES_IMPLEMENTATION, secConf.getHTTPUtilitiesImplementation());
+		
+		final String expected = "TestHTTPUtilities";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.HTTP_UTILITIES_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getHTTPUtilitiesImplementation());
+	}
+	
+	@Test
+	public void testValidationImplementation() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_VALIDATOR_IMPLEMENTATION, secConf.getValidationImplementation());
+		
+		final String expected = "TestValidation";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.VALIDATOR_IMPLEMENTATION, expected);
+		Assert.assertEquals(expected, secConf.getValidationImplementation());
+	}
+	
+	@Test
+	public void testGetEncryptionKeyLength() {
+		// test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(128, secConf.getEncryptionKeyLength());
+		
+		final int expected = 256;
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.KEY_LENGTH, String.valueOf(expected));
+		Assert.assertEquals(expected, secConf.getEncryptionKeyLength());
+	}
+	
+	@Test
+	public void testGetKDFPseudoRandomFunction() {
+		// test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("HmacSHA256", secConf.getKDFPseudoRandomFunction());
+		
+		final String expected = "HmacSHA1";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.KDF_PRF_ALG, expected);
+		Assert.assertEquals(expected, secConf.getKDFPseudoRandomFunction());
+	}
+	
+	@Test
+	public void testGetMasterSalt() {
+		try {
+			DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+			secConf.getMasterSalt();
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+		
+		final String salt = "53081";
+		final String property = ESAPI.encoder().encodeForBase64(salt.getBytes(), false);
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.MASTER_SALT, property);
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals(salt, new String(secConf.getMasterSalt()));
+	}
+	
+	@Test
+	public void testGetAllowedExecutables() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		java.util.List<String> allowedExecutables = secConf.getAllowedExecutables();
+		
+		//is this really what should be returned? what about an empty list?
+		Assert.assertEquals(1, allowedExecutables.size());
+		Assert.assertEquals("", allowedExecutables.get(0));
+		
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.APPROVED_EXECUTABLES, String.valueOf("/bin/bzip2,/bin/diff, /bin/cvs"));
+		secConf = new DefaultSecurityConfiguration(properties);
+		allowedExecutables = secConf.getAllowedExecutables();
+		Assert.assertEquals(3, allowedExecutables.size());
+		Assert.assertEquals("/bin/bzip2", allowedExecutables.get(0));
+		Assert.assertEquals("/bin/diff", allowedExecutables.get(1));
+		
+		//this seems less than optimal, maybe each value should have a trim() done to it
+		//at least we know that this behavior exists, the property should'nt have spaces between values
+		Assert.assertEquals(" /bin/cvs", allowedExecutables.get(2));
+	}
+	
+	@Test
+	public void testGetAllowedFileExtensions() {
+		
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		java.util.List<String> allowedFileExtensions = secConf.getAllowedFileExtensions();
+		Assert.assertFalse(allowedFileExtensions.isEmpty());
+		
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.APPROVED_UPLOAD_EXTENSIONS, String.valueOf(".txt,.xml,.html,.png"));
+		secConf = new DefaultSecurityConfiguration(properties);
+		allowedFileExtensions = secConf.getAllowedFileExtensions();
+		Assert.assertEquals(4, allowedFileExtensions.size());
+		Assert.assertEquals(".html", allowedFileExtensions.get(2));
+	}
+	
+	@Test
+	public void testGetAllowedFileUploadSize() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		//assert that the default is of some reasonable size
+		Assert.assertTrue(secConf.getAllowedFileUploadSize() > (1024 * 100));
+		
+		final int expected = (1024 * 1000);
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.MAX_UPLOAD_FILE_BYTES, String.valueOf(expected));
+		Assert.assertEquals(expected, secConf.getAllowedFileUploadSize());
+	}
+	
+	@Test
+	public void testGetParameterNames() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("password", secConf.getPasswordParameterName());
+		Assert.assertEquals("username", secConf.getUsernameParameterName());
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.PASSWORD_PARAMETER_NAME, "j_password");
+		properties.setProperty(DefaultSecurityConfiguration.USERNAME_PARAMETER_NAME, "j_username");
+		secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals("j_password", secConf.getPasswordParameterName());
+		Assert.assertEquals("j_username", secConf.getUsernameParameterName());
+	}
+	
+	@Test
+	public void testGetEncryptionAlgorithm() {
+		//test the default
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("AES", secConf.getEncryptionAlgorithm());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCRYPTION_ALGORITHM, "3DES");
+		Assert.assertEquals("3DES", secConf.getEncryptionAlgorithm());
+	}
+	
+	@Test
+	public void testGetCipherXProperties() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("AES/CBC/PKCS5Padding", secConf.getCipherTransformation());
+		//Assert.assertEquals("AES/CBC/PKCS5Padding", secConf.getC);
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.CIPHER_TRANSFORMATION_IMPLEMENTATION, "Blowfish/CFB/ISO10126Padding");
+		secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation());
+		
+		secConf.setCipherTransformation("DESede/PCBC/PKCS5Padding");
+		Assert.assertEquals("DESede/PCBC/PKCS5Padding", secConf.getCipherTransformation());
+		
+		secConf.setCipherTransformation(null);//sets it back to default
+		Assert.assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation());
+	}
+	
+	@Test
+	public void testIV() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("random", secConf.getIVType());
+		try {
+			secConf.getFixedIV();
+			Assert.fail();
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+		
+		java.util.Properties properties = new java.util.Properties();
+		properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "fixed");
+		properties.setProperty(DefaultSecurityConfiguration.FIXED_IV, "ivValue");
+		secConf = new DefaultSecurityConfiguration(properties);
+		Assert.assertEquals("fixed", secConf.getIVType());
+		Assert.assertEquals("ivValue", secConf.getFixedIV());
+		
+		properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "illegal");
+		secConf = new DefaultSecurityConfiguration(properties);
+		try {
+			secConf.getIVType();
+			Assert.fail();
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+		try {
+			secConf.getFixedIV();
+			Assert.fail();
+		}
+		catch (ConfigurationException ce) {
+			Assert.assertNotNull(ce.getMessage());
+		}
+	}
+	
+	@Test
+	public void testGetAllowMultipleEncoding() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertFalse(secConf.getAllowMultipleEncoding());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "yes");
+		Assert.assertTrue(secConf.getAllowMultipleEncoding());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "true");
+		Assert.assertTrue(secConf.getAllowMultipleEncoding());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "no");
+		Assert.assertFalse(secConf.getAllowMultipleEncoding());
+	}
+	
+	@Test
+	public void testGetDefaultCanonicalizationCodecs() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertFalse(secConf.getDefaultCanonicalizationCodecs().isEmpty());
+		
+		String property = "org.owasp.esapi.codecs.TestCodec1,org.owasp.esapi.codecs.TestCodec2";
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.CANONICALIZATION_CODECS, property);
+		Assert.assertTrue(secConf.getDefaultCanonicalizationCodecs().contains("org.owasp.esapi.codecs.TestCodec1"));
+	}
+	
+	@Test
+	public void testGetDisableIntrusionDetection() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertFalse(secConf.getDisableIntrusionDetection());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "TRUE");
+		Assert.assertTrue(secConf.getDisableIntrusionDetection());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "true");
+		Assert.assertTrue(secConf.getDisableIntrusionDetection());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "false");
+		Assert.assertFalse(secConf.getDisableIntrusionDetection());
+	}
+	
+	@Test
+	public void testGetLogLevel() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(Logger.WARNING, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "trace");
+		Assert.assertEquals(Logger.TRACE, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "Off");
+		Assert.assertEquals(Logger.OFF, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "all");
+		Assert.assertEquals(Logger.ALL, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "DEBUG");
+		Assert.assertEquals(Logger.DEBUG, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "info");
+		Assert.assertEquals(Logger.INFO, secConf.getLogLevel());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "ERROR");
+		Assert.assertEquals(Logger.ERROR, secConf.getLogLevel());
+	}
+	
+	@Test
+	public void testGetLogFileName() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals("ESAPI_logging_file", secConf.getLogFileName());
+		
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_FILE_NAME, "log.txt");
+		Assert.assertEquals("log.txt", secConf.getLogFileName());
+	}
+	
+	@Test
+	public void testGetMaxLogFileSize() {
+		DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties());
+		Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_MAX_LOG_FILE_SIZE, secConf.getMaxLogFileSize());
+		
+		int maxLogSize = (1024 * 1000);
+		secConf = this.createWithProperty(DefaultSecurityConfiguration.MAX_LOG_FILE_SIZE, String.valueOf(maxLogSize));
+		Assert.assertEquals(maxLogSize, secConf.getMaxLogFileSize());
+	}
+
+	
+}
diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java
new file mode 100644
index 0000000..52feb91
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java
@@ -0,0 +1,802 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.MySQLCodec;
+import org.owasp.esapi.codecs.OracleCodec;
+import org.owasp.esapi.codecs.PushbackString;
+import org.owasp.esapi.codecs.UnixCodec;
+import org.owasp.esapi.codecs.WindowsCodec;
+import org.owasp.esapi.errors.EncodingException;
+import org.owasp.esapi.errors.IntrusionException;
+
+/**
+ * The Class EncoderTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncoderTest extends TestCase {
+    
+	private static final String PREFERRED_ENCODING = "UTF-8";
+	
+    /**
+	 * Instantiates a new encoder test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public EncoderTest(String testName) {
+        super(testName);
+    }
+    
+    /**
+     * {@inheritDoc}
+     * @throws Exception 
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+    
+    /**
+     * {@inheritDoc}s
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+    
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(EncoderTest.class);        
+        return suite;
+    }
+    
+	/**
+	 * Test of canonicalize method, of class org.owasp.esapi.Encoder.
+	 * 
+	 * @throws EncodingException
+	 */
+	public void testCanonicalize() throws EncodingException {
+		System.out.println("canonicalize");
+
+        ArrayList<String> list = new ArrayList<String>();
+        list.add( "HTMLEntityCodec" );
+	    list.add( "PercentCodec" );
+		Encoder instance = new DefaultEncoder( list );
+		
+		// Test null paths
+		assertEquals( null, instance.canonicalize(null));
+		assertEquals( null, instance.canonicalize(null, true));
+		assertEquals( null, instance.canonicalize(null, false));
+		assertEquals( null, instance.canonicalize(null, true, true));
+		assertEquals( null, instance.canonicalize(null, true, false));
+		assertEquals( null, instance.canonicalize(null, false, true));
+		assertEquals( null, instance.canonicalize(null, false, false));
+		
+		// test exception paths
+		assertEquals( "%", instance.canonicalize("%25", true));
+		assertEquals( "%", instance.canonicalize("%25", false));
+
+        assertEquals( "%", instance.canonicalize("%25"));
+        assertEquals( "%F", instance.canonicalize("%25F"));
+        assertEquals( "<", instance.canonicalize("%3c"));
+        assertEquals( "<", instance.canonicalize("%3C"));
+        assertEquals( "%X1", instance.canonicalize("%X1"));
+
+        assertEquals( "<", instance.canonicalize("&lt"));
+        assertEquals( "<", instance.canonicalize("&LT"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        
+        assertEquals( "%", instance.canonicalize("%"));
+        assertEquals( "%", instance.canonicalize("&#37"));
+        assertEquals( "%b", instance.canonicalize("&#37b"));
+
+        assertEquals( "<", instance.canonicalize("&#x3c"));
+        assertEquals( "<", instance.canonicalize("&#x3c;"));
+        assertEquals( "<", instance.canonicalize("&#x3C"));
+        assertEquals( "<", instance.canonicalize("&#X3c"));
+        assertEquals( "<", instance.canonicalize("&#X3C"));
+        assertEquals( "<", instance.canonicalize("&#X3C;"));
+
+        // percent encoding
+        assertEquals( "<", instance.canonicalize("%3c"));
+        assertEquals( "<", instance.canonicalize("%3C"));
+
+        // html entity encoding
+        assertEquals( "<", instance.canonicalize("&#60"));
+        assertEquals( "<", instance.canonicalize("&#060"));
+        assertEquals( "<", instance.canonicalize("&#0060"));
+        assertEquals( "<", instance.canonicalize("&#00060"));
+        assertEquals( "<", instance.canonicalize("&#000060"));
+        assertEquals( "<", instance.canonicalize("&#0000060"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("&#x3c"));
+        assertEquals( "<", instance.canonicalize("&#x03c"));
+        assertEquals( "<", instance.canonicalize("&#x003c"));
+        assertEquals( "<", instance.canonicalize("&#x0003c"));
+        assertEquals( "<", instance.canonicalize("&#x00003c"));
+        assertEquals( "<", instance.canonicalize("&#x000003c"));
+        assertEquals( "<", instance.canonicalize("&#x3c;"));
+        assertEquals( "<", instance.canonicalize("&#x03c;"));
+        assertEquals( "<", instance.canonicalize("&#x003c;"));
+        assertEquals( "<", instance.canonicalize("&#x0003c;"));
+        assertEquals( "<", instance.canonicalize("&#x00003c;"));
+        assertEquals( "<", instance.canonicalize("&#x000003c;"));
+        assertEquals( "<", instance.canonicalize("&#X3c"));
+        assertEquals( "<", instance.canonicalize("&#X03c"));
+        assertEquals( "<", instance.canonicalize("&#X003c"));
+        assertEquals( "<", instance.canonicalize("&#X0003c"));
+        assertEquals( "<", instance.canonicalize("&#X00003c"));
+        assertEquals( "<", instance.canonicalize("&#X000003c"));
+        assertEquals( "<", instance.canonicalize("&#X3c;"));
+        assertEquals( "<", instance.canonicalize("&#X03c;"));
+        assertEquals( "<", instance.canonicalize("&#X003c;"));
+        assertEquals( "<", instance.canonicalize("&#X0003c;"));
+        assertEquals( "<", instance.canonicalize("&#X00003c;"));
+        assertEquals( "<", instance.canonicalize("&#X000003c;"));
+        assertEquals( "<", instance.canonicalize("&#x3C"));
+        assertEquals( "<", instance.canonicalize("&#x03C"));
+        assertEquals( "<", instance.canonicalize("&#x003C"));
+        assertEquals( "<", instance.canonicalize("&#x0003C"));
+        assertEquals( "<", instance.canonicalize("&#x00003C"));
+        assertEquals( "<", instance.canonicalize("&#x000003C"));
+        assertEquals( "<", instance.canonicalize("&#x3C;"));
+        assertEquals( "<", instance.canonicalize("&#x03C;"));
+        assertEquals( "<", instance.canonicalize("&#x003C;"));
+        assertEquals( "<", instance.canonicalize("&#x0003C;"));
+        assertEquals( "<", instance.canonicalize("&#x00003C;"));
+        assertEquals( "<", instance.canonicalize("&#x000003C;"));
+        assertEquals( "<", instance.canonicalize("&#X3C"));
+        assertEquals( "<", instance.canonicalize("&#X03C"));
+        assertEquals( "<", instance.canonicalize("&#X003C"));
+        assertEquals( "<", instance.canonicalize("&#X0003C"));
+        assertEquals( "<", instance.canonicalize("&#X00003C"));
+        assertEquals( "<", instance.canonicalize("&#X000003C"));
+        assertEquals( "<", instance.canonicalize("&#X3C;"));
+        assertEquals( "<", instance.canonicalize("&#X03C;"));
+        assertEquals( "<", instance.canonicalize("&#X003C;"));
+        assertEquals( "<", instance.canonicalize("&#X0003C;"));
+        assertEquals( "<", instance.canonicalize("&#X00003C;"));
+        assertEquals( "<", instance.canonicalize("&#X000003C;"));
+        assertEquals( "<", instance.canonicalize("&lt"));
+        assertEquals( "<", instance.canonicalize("&lT"));
+        assertEquals( "<", instance.canonicalize("&Lt"));
+        assertEquals( "<", instance.canonicalize("&LT"));
+        assertEquals( "<", instance.canonicalize("<"));
+        assertEquals( "<", instance.canonicalize("&lT;"));
+        assertEquals( "<", instance.canonicalize("≪"));
+        assertEquals( "<", instance.canonicalize("<"));
+        
+        assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript%3Ealert%28%22hello%22%29%3B%3C%2Fscript%3E") );
+        assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript&#x3E;alert%28%22hello&#34%29%3B%3C%2Fscript%3E", false) );
+        
+        // javascript escape syntax
+        ArrayList<String> js = new ArrayList<String>();
+        js.add( "JavaScriptCodec" );
+        instance = new DefaultEncoder( js );
+        System.out.println( "JavaScript Decoding" );
+
+        assertEquals( "\0", instance.canonicalize("\\0"));
+        assertEquals( "\b", instance.canonicalize("\\b"));
+        assertEquals( "\t", instance.canonicalize("\\t"));
+        assertEquals( "\n", instance.canonicalize("\\n"));
+        assertEquals( ""+(char)0x0b, instance.canonicalize("\\v"));
+        assertEquals( "\f", instance.canonicalize("\\f"));
+        assertEquals( "\r", instance.canonicalize("\\r"));
+        assertEquals( "\'", instance.canonicalize("\\'"));
+        assertEquals( "\"", instance.canonicalize("\\\""));
+        assertEquals( "\\", instance.canonicalize("\\\\"));
+        assertEquals( "<", instance.canonicalize("\\<"));
+        
+        assertEquals( "<", instance.canonicalize("\\u003c"));
+        assertEquals( "<", instance.canonicalize("\\U003c"));
+        assertEquals( "<", instance.canonicalize("\\u003C"));
+        assertEquals( "<", instance.canonicalize("\\U003C"));
+        assertEquals( "<", instance.canonicalize("\\x3c"));
+        assertEquals( "<", instance.canonicalize("\\X3c"));
+        assertEquals( "<", instance.canonicalize("\\x3C"));
+        assertEquals( "<", instance.canonicalize("\\X3C"));
+
+        // css escape syntax
+        // be careful because some codecs see \0 as null byte
+        ArrayList<String> css = new ArrayList<String>();
+        css.add( "CSSCodec" );
+        instance = new DefaultEncoder( css );
+        System.out.println( "CSS Decoding" );
+        assertEquals( "<", instance.canonicalize("\\3c"));  // add strings to prevent null byte
+        assertEquals( "<", instance.canonicalize("\\03c"));
+        assertEquals( "<", instance.canonicalize("\\003c"));
+        assertEquals( "<", instance.canonicalize("\\0003c"));
+        assertEquals( "<", instance.canonicalize("\\00003c"));
+        assertEquals( "<", instance.canonicalize("\\3C"));
+        assertEquals( "<", instance.canonicalize("\\03C"));
+        assertEquals( "<", instance.canonicalize("\\003C"));
+        assertEquals( "<", instance.canonicalize("\\0003C"));
+        assertEquals( "<", instance.canonicalize("\\00003C"));
+	}
+
+	
+    /**
+     * Test of canonicalize method, of class org.owasp.esapi.Encoder.
+     * 
+     * @throws EncodingException
+     */
+    public void testDoubleEncodingCanonicalization() throws EncodingException {
+        System.out.println("doubleEncodingCanonicalization");
+        Encoder instance = ESAPI.encoder();
+
+        // note these examples use the strict=false flag on canonicalize to allow
+        // full decoding without throwing an IntrusionException. Generally, you
+        // should use strict mode as allowing double-encoding is an abomination.
+        
+        // double encoding examples
+        assertEquals( "<", instance.canonicalize("&#x26;lt&#59", false )); //double entity
+        assertEquals( "\\", instance.canonicalize("%255c", false)); //double percent
+        assertEquals( "%", instance.canonicalize("%2525", false)); //double percent
+        
+        // double encoding with multiple schemes example
+        assertEquals( "<", instance.canonicalize("%26lt%3b", false)); //first entity, then percent
+        assertEquals( "&", instance.canonicalize("&#x25;26", false)); //first percent, then entity
+
+		//enforce multiple and mixed encoding detection
+		try {
+			instance.canonicalize("%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B", true, true);
+			fail("Multiple and mixed encoding not detected");
+		} catch (IntrusionException ie) {}
+
+		//enforce multiple but not mixed encoding detection
+		try {
+			instance.canonicalize("%252525253C", true, false); 
+			fail("Multiple encoding not detected");
+		} catch (IntrusionException ie) {}
+
+		//enforce mixed but not multiple encoding detection
+		try {
+			instance.canonicalize("%25 %2526 %26#X3c;script&#x3e; %3Cscript%25252525253e", false, true); 
+			fail("Mixed encoding not detected");
+		} catch (IntrusionException ie) {}
+
+		//enforce niether mixed nor multiple encoding detection -should canonicalize but not throw an error
+		assertEquals( "< < < < < < <", instance.canonicalize("%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B", 
+															 false, false)); 
+
+        // nested encoding examples
+        assertEquals( "<", instance.canonicalize("%253c", false)); //nested encode % with percent
+        assertEquals( "<", instance.canonicalize("%%33%63", false)); //nested encode both nibbles with percent
+        assertEquals( "<", instance.canonicalize("%%33c", false)); // nested encode first nibble with percent
+        assertEquals( "<", instance.canonicalize("%3%63", false));  //nested encode second nibble with percent
+        assertEquals( "<", instance.canonicalize("&lt;", false)); //nested encode l with entity
+        assertEquals( "<", instance.canonicalize("%2&#x35;3c", false)); //triple percent, percent, 5 with entity
+        
+        // nested encoding with multiple schemes examples
+        assertEquals( "<", instance.canonicalize("&%6ct;", false)); // nested encode l with percent
+        assertEquals( "<", instance.canonicalize("%&#x33;c", false)); //nested encode 3 with entity            
+        
+        // multiple encoding tests
+        assertEquals( "% & <script> <script>", instance.canonicalize( "%25 %2526 %26#X3c;script&#x3e; %3Cscript%25252525253e", false ) );
+        assertEquals( "< < < < < < <", instance.canonicalize( "%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B", false ) );
+
+        // test strict mode with both mixed and multiple encoding
+        try {
+            assertEquals( "< < < < < < <", instance.canonicalize( "%26lt; %26lt; &#X25;3c &#x25;3c %2526lt%253B %2526lt%253B %2526lt%253B" ) );
+        } catch( IntrusionException e ) {
+            // expected
+        }
+        
+        try {
+            assertEquals( "<script", instance.canonicalize("%253Cscript" ) );
+        } catch( IntrusionException e ) {
+            // expected
+        }
+        try {
+            assertEquals( "<script", instance.canonicalize("%3Cscript" ) );
+        } catch( IntrusionException e ) {
+            // expected
+        }
+    }
+
+    /**
+	 * Test of encodeForHTML method, of class org.owasp.esapi.Encoder.
+     *
+     * @throws Exception
+     */
+    public void testEncodeForHTML() throws Exception {
+        System.out.println("encodeForHTML");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForHTML(null));
+        // test invalid characters are replaced with spaces
+        assertEquals("a&#xfffd;b&#xfffd;c&#xfffd;d&#xfffd;e&#xfffd;f&#x9;g", instance.encodeForHTML("a" + (char)0 + "b" + (char)4 + "c" + (char)128 + "d" + (char)150 + "e" +(char)159 + "f" + (char)9 + "g"));
+        
+        assertEquals("<script>", instance.encodeForHTML("<script>"));
+        assertEquals("&lt&#x3b;script&gt&#x3b;", instance.encodeForHTML("<script>"));
+        assertEquals("&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForHTML("!@$%()=+{}[]"));
+        assertEquals("&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForHTML(instance.canonicalize("!@$%()=+{}[]") ) );
+        assertEquals(",.-_ ", instance.encodeForHTML(",.-_ "));
+        assertEquals("dir&", instance.encodeForHTML("dir&"));
+        assertEquals("one&two", instance.encodeForHTML("one&two"));
+        assertEquals("" + (char)12345 + (char)65533 + (char)1244, "" + (char)12345 + (char)65533 + (char)1244 );
+    }
+    
+    /**
+	 * Test of encodeForHTMLAttribute method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForHTMLAttribute() {
+        System.out.println("encodeForHTMLAttribute");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForHTMLAttribute(null));
+        assertEquals("<script>", instance.encodeForHTMLAttribute("<script>"));
+        assertEquals(",.-_", instance.encodeForHTMLAttribute(",.-_"));
+        assertEquals("&#x20;&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForHTMLAttribute(" !@$%()=+{}[]"));
+    }
+    
+    
+    /**
+     *
+     */
+    public void testEncodeForCSS() {
+        System.out.println("encodeForCSS");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForCSS(null));
+        assertEquals("\\3c script\\3e ", instance.encodeForCSS("<script>"));
+        assertEquals("\\21 \\40 \\24 \\25 \\28 \\29 \\3d \\2b \\7b \\7d \\5b \\5d ", instance.encodeForCSS("!@$%()=+{}[]"));
+    }
+    
+
+    
+    /**
+	 * Test of encodeForJavaScript method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForJavascript() {
+        System.out.println("encodeForJavascript");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForJavaScript(null));
+        assertEquals("\\x3Cscript\\x3E", instance.encodeForJavaScript("<script>"));
+        assertEquals(",.\\x2D_\\x20", instance.encodeForJavaScript(",.-_ "));
+        assertEquals("\\x21\\x40\\x24\\x25\\x28\\x29\\x3D\\x2B\\x7B\\x7D\\x5B\\x5D", instance.encodeForJavaScript("!@$%()=+{}[]"));
+        // assertEquals( "\\0", instance.encodeForJavaScript("\0"));
+        // assertEquals( "\\b", instance.encodeForJavaScript("\b"));
+        // assertEquals( "\\t", instance.encodeForJavaScript("\t"));
+        // assertEquals( "\\n", instance.encodeForJavaScript("\n"));
+        // assertEquals( "\\v", instance.encodeForJavaScript("" + (char)0x0b));
+        // assertEquals( "\\f", instance.encodeForJavaScript("\f"));
+        // assertEquals( "\\r", instance.encodeForJavaScript("\r"));
+        // assertEquals( "\\'", instance.encodeForJavaScript("\'"));
+        // assertEquals( "\\\"", instance.encodeForJavaScript("\""));
+        // assertEquals( "\\\\", instance.encodeForJavaScript("\\"));
+    }
+        
+    /**
+     *
+     */
+    public void testEncodeForVBScript() {
+        System.out.println("encodeForVBScript");        
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForVBScript(null));
+        assertEquals( "chrw(60)&\"script\"&chrw(62)", instance.encodeForVBScript("<script>"));
+        assertEquals( "x\"&chrw(32)&chrw(33)&chrw(64)&chrw(36)&chrw(37)&chrw(40)&chrw(41)&chrw(61)&chrw(43)&chrw(123)&chrw(125)&chrw(91)&chrw(93)", instance.encodeForVBScript("x !@$%()=+{}[]"));
+        assertEquals( "alert\"&chrw(40)&chrw(39)&\"ESAPI\"&chrw(32)&\"test\"&chrw(33)&chrw(39)&chrw(41)", instance.encodeForVBScript("alert('ESAPI test!')" ));
+        assertEquals( "jeff.williams\"&chrw(64)&\"aspectsecurity.com", instance.encodeForVBScript("jeff.williams at aspectsecurity.com"));
+        assertEquals( "test\"&chrw(32)&chrw(60)&chrw(62)&chrw(32)&\"test", instance.encodeForVBScript("test <> test" ));
+    }
+
+        
+    /**
+	 * Test of encodeForXPath method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForXPath() {
+        System.out.println("encodeForXPath");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForXPath(null));
+        assertEquals("&#x27;or 1&#x3d;1", instance.encodeForXPath("'or 1=1"));
+    }
+    
+
+    
+    /**
+	 * Test of encodeForSQL method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForSQL() {
+        System.out.println("encodeForSQL");
+        Encoder instance = ESAPI.encoder();
+
+        Codec mySQL1 = new MySQLCodec( MySQLCodec.ANSI_MODE );
+        assertEquals("ANSI_MODE", null, instance.encodeForSQL(mySQL1, null));
+        assertEquals("ANSI_MODE", "Jeff'' or ''1''=''1", instance.encodeForSQL(mySQL1, "Jeff' or '1'='1"));
+        
+        Codec mySQL2 = new MySQLCodec( MySQLCodec.MYSQL_MODE );
+        assertEquals("MYSQL_MODE", null, instance.encodeForSQL(mySQL2, null));
+        assertEquals("MYSQL_MODE", "Jeff\\' or \\'1\\'\\=\\'1", instance.encodeForSQL(mySQL2, "Jeff' or '1'='1"));
+
+        Codec oracle = new OracleCodec();
+        assertEquals("Oracle", null, instance.encodeForSQL(oracle, null));
+        assertEquals("Oracle", "Jeff'' or ''1''=''1", instance.encodeForSQL(oracle, "Jeff' or '1'='1"));
+    }
+
+    public void testMySQLANSIModeQuoteInjection() {
+        Encoder instance = ESAPI.encoder();
+        Codec c = new MySQLCodec(MySQLCodec.Mode.ANSI);
+        assertEquals("MySQL Ansi Quote Injection Bug", " or 1=1 -- -", instance.encodeForSQL(c, "\" or 1=1 -- -"));
+    }
+
+    
+    /**
+	 * Test of encodeForLDAP method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForLDAP() {
+        System.out.println("encodeForLDAP");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForLDAP(null));
+        assertEquals("No special characters to escape", "Hi This is a test #��", instance.encodeForLDAP("Hi This is a test #��"));
+        assertEquals("Zeros", "Hi \\00", instance.encodeForLDAP("Hi \u0000"));
+        assertEquals("LDAP Christams Tree", "Hi \\28This\\29 = is \\2a a \\5c test # � � �", instance.encodeForLDAP("Hi (This) = is * a \\ test # � � �"));
+    }
+    
+    /**
+	 * Test of encodeForLDAP method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForDN() {
+        System.out.println("encodeForDN");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForDN(null));
+        assertEquals("No special characters to escape", "Hello�", instance.encodeForDN("Hello�"));
+        assertEquals("leading #", "\\# Hello�", instance.encodeForDN("# Hello�"));
+        assertEquals("leading space", "\\ Hello�", instance.encodeForDN(" Hello�"));
+        assertEquals("trailing space", "Hello�\\ ", instance.encodeForDN("Hello� "));
+        assertEquals("less than greater than", "Hello\\<\\>", instance.encodeForDN("Hello<>"));
+        assertEquals("only 3 spaces", "\\  \\ ", instance.encodeForDN("   "));
+        assertEquals("Christmas Tree DN", "\\ Hello\\\\ \\+ \\, \\\"World\\\" \\;\\ ", instance.encodeForDN(" Hello\\ + , \"World\" ; "));
+    }
+    
+    public void testEncodeForXMLNull() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForXML(null));
+    }
+
+    public void testEncodeForXMLSpace() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(" ", instance.encodeForXML(" "));
+    }
+
+    public void testEncodeForXMLScript() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#x3c;script&#x3e;", instance.encodeForXML("<script>"));
+    }
+
+    public void testEncodeForXMLImmune() {
+        System.out.println("encodeForXML");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(",.-_", instance.encodeForXML(",.-_"));
+    }
+    
+    public void testEncodeForXMLSymbol() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForXML("!@$%()=+{}[]"));
+    }
+
+    public void testEncodeForXMLPound() {
+        System.out.println("encodeForXML");
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#xa3;", instance.encodeForXML("\u00A3"));
+    }
+    
+    public void testEncodeForXMLAttributeNull() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForXMLAttribute(null));
+    }
+    
+    public void testEncodeForXMLAttributeSpace() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(" ", instance.encodeForXMLAttribute(" "));
+    }
+    
+    public void testEncodeForXMLAttributeScript() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#x3c;script&#x3e;", instance.encodeForXMLAttribute("<script>"));
+    }
+    
+    public void testEncodeForXMLAttributeImmune() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(",.-_", instance.encodeForXMLAttribute(",.-_"));
+    }
+    
+    public void testEncodeForXMLAttributeSymbol() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals(" &#x21;&#x40;&#x24;&#x25;&#x28;&#x29;&#x3d;&#x2b;&#x7b;&#x7d;&#x5b;&#x5d;", instance.encodeForXMLAttribute(" !@$%()=+{}[]"));
+    }
+    
+    public void testEncodeForXMLAttributePound() {
+        Encoder instance = ESAPI.encoder();
+        assertEquals("&#xa3;", instance.encodeForXMLAttribute("\u00A3"));
+    }
+    
+    /**
+	 * Test of encodeForURL method, of class org.owasp.esapi.Encoder.
+     *
+     * @throws Exception
+     */
+    public void testEncodeForURL() throws Exception {
+        System.out.println("encodeForURL");
+        Encoder instance = ESAPI.encoder();
+        assertEquals(null, instance.encodeForURL(null));
+        assertEquals("%3Cscript%3E", instance.encodeForURL("<script>"));
+    }
+    
+    /**
+	 * Test of decodeFromURL method, of class org.owasp.esapi.Encoder.
+     *
+     * @throws Exception
+     */
+    public void testDecodeFromURL() throws Exception {
+        System.out.println("decodeFromURL");
+        Encoder instance = ESAPI.encoder();
+        try {
+        	assertEquals(null, instance.decodeFromURL(null));
+            assertEquals("<script>", instance.decodeFromURL("%3Cscript%3E"));
+            assertEquals("     ", instance.decodeFromURL("+++++") );
+        } catch ( Exception e ) {
+            fail();
+        }
+        try {
+        	instance.decodeFromURL( "%3xridiculous" );
+        	fail();
+        } catch( Exception e ) {
+        	// expected
+        }
+    }
+    
+    /**
+	 * Test of encodeForBase64 method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testEncodeForBase64() {
+        System.out.println("encodeForBase64");
+        Encoder instance = ESAPI.encoder();
+        
+        try {
+        	assertEquals(null, instance.encodeForBase64(null, false));
+            assertEquals(null, instance.encodeForBase64(null, true));
+            assertEquals(null, instance.decodeFromBase64(null));
+            for ( int i=0; i < 100; i++ ) {
+                byte[] r = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS ).getBytes(PREFERRED_ENCODING);
+                String encoded = instance.encodeForBase64( r, ESAPI.randomizer().getRandomBoolean() );
+                byte[] decoded = instance.decodeFromBase64( encoded );
+                assertTrue( Arrays.equals( r, decoded ) );
+            }
+        } catch ( IOException e ) {
+            fail();
+        }
+    }
+    
+    /**
+	 * Test of decodeFromBase64 method, of class org.owasp.esapi.Encoder.
+	 */
+    public void testDecodeFromBase64() {
+        System.out.println("decodeFromBase64");
+        Encoder instance = ESAPI.encoder();
+        for ( int i=0; i < 100; i++ ) {
+            try {
+                byte[] r = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS ).getBytes(PREFERRED_ENCODING);
+                String encoded = instance.encodeForBase64( r, ESAPI.randomizer().getRandomBoolean() );
+                byte[] decoded = instance.decodeFromBase64( encoded );
+                assertTrue( Arrays.equals( r, decoded ) );
+            } catch ( IOException e ) {
+                fail();
+	        }
+        }
+        for ( int i=0; i < 100; i++ ) {
+            try {
+                byte[] r = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS ).getBytes(PREFERRED_ENCODING);
+                String encoded = ESAPI.randomizer().getRandomString(1, EncoderConstants.CHAR_ALPHANUMERICS) + instance.encodeForBase64( r, ESAPI.randomizer().getRandomBoolean() );
+	            byte[] decoded = instance.decodeFromBase64( encoded );
+	            assertFalse( Arrays.equals(r, decoded) );
+            } catch( UnsupportedEncodingException ex) {
+            	fail();
+            } catch ( IOException e ) {
+            	// expected
+            }
+        }
+    }
+    
+
+    /**
+	 * Test of WindowsCodec
+	 */
+    public void testWindowsCodec() {
+        System.out.println("WindowsCodec");
+        Encoder instance = ESAPI.encoder();
+
+        Codec win = new WindowsCodec();
+        char[] immune = new char[0];
+        assertEquals(null, instance.encodeForOS(win, null));
+        
+        PushbackString npbs = new PushbackString("n");
+        assertEquals(null, win.decodeCharacter(npbs));
+
+        PushbackString epbs = new PushbackString("");
+        assertEquals(null, win.decodeCharacter(epbs));
+        
+        Character c = Character.valueOf('<');
+        PushbackString cpbs = new PushbackString(win.encodeCharacter(immune, c));
+        Character decoded = win.decodeCharacter(cpbs);
+        assertEquals(c, decoded);
+        
+        String orig = "c:\\jeff";
+        String enc = win.encode(EncoderConstants.CHAR_ALPHANUMERICS, orig);
+        assertEquals(orig, win.decode(enc));
+        assertEquals(orig, win.decode(orig));
+        
+     // TODO: Check that these are acceptable for Windows
+        assertEquals("c^:^\\jeff", instance.encodeForOS(win, "c:\\jeff"));		
+        assertEquals("c^:^\\jeff", win.encode(immune, "c:\\jeff"));
+        assertEquals("dir^ ^&^ foo", instance.encodeForOS(win, "dir & foo"));
+        assertEquals("dir^ ^&^ foo", win.encode(immune, "dir & foo"));
+    }
+
+    /**
+	 * Test of UnixCodec
+	 */
+    public void testUnixCodec() {
+        System.out.println("UnixCodec");
+        Encoder instance = ESAPI.encoder();
+
+        Codec unix = new UnixCodec();
+        char[] immune = new char[0];
+        assertEquals(null, instance.encodeForOS(unix, null));
+        
+        PushbackString npbs = new PushbackString("n");
+        assertEquals(null, unix.decodeCharacter(npbs));
+
+        Character c = Character.valueOf('<');
+        PushbackString cpbs = new PushbackString(unix.encodeCharacter(immune, c));
+        Character decoded = unix.decodeCharacter(cpbs);
+        assertEquals(c, decoded);
+        
+        PushbackString epbs = new PushbackString("");
+        assertEquals(null, unix.decodeCharacter(epbs));
+
+        String orig = "/etc/passwd";
+        String enc = unix.encode(immune, orig);
+        assertEquals(orig, unix.decode(enc));
+        assertEquals(orig, unix.decode(orig));
+        
+     // TODO: Check that these are acceptable for Unix hosts
+        assertEquals("c\\:\\\\jeff", instance.encodeForOS(unix, "c:\\jeff"));
+        assertEquals("c\\:\\\\jeff", unix.encode(immune, "c:\\jeff"));
+        assertEquals("dir\\ \\&\\ foo", instance.encodeForOS(unix, "dir & foo"));
+        assertEquals("dir\\ \\&\\ foo", unix.encode(immune, "dir & foo"));
+
+        // Unix paths (that must be encoded safely)
+        // TODO: Check that these are acceptable for Unix
+        assertEquals("\\/etc\\/hosts", instance.encodeForOS(unix, "/etc/hosts"));
+        assertEquals("\\/etc\\/hosts\\;\\ ls\\ -l", instance.encodeForOS(unix, "/etc/hosts; ls -l"));
+    }
+    
+    public void testCanonicalizePerformance() throws Exception {
+        System.out.println("Canonicalization Performance");
+    	Encoder encoder = ESAPI.encoder();
+    	int iterations = 100;
+    	String normal = "The quick brown fox jumped over the lazy dog";
+    	
+    	long start = System.currentTimeMillis();
+    	String temp = null;		// Trade in 1/2 doz warnings in Eclipse for one (never read)
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = normal;
+        }
+    	long stop = System.currentTimeMillis();
+        System.out.println( "Normal: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = encoder.canonicalize( normal, false );
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Normal Loose: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = encoder.canonicalize( normal, true );
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Normal Strict: " + (stop-start) );
+
+    	String attack = "%2&#x35;2%3525&#x32;\\u0036lt;\r\n\r\n%&#x%%%3333\\u0033;&%23101;";
+    	
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = attack;
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Attack: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	temp = encoder.canonicalize( attack, false );
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Attack Loose: " + (stop-start) );
+        
+    	start = System.currentTimeMillis();
+        for ( int i=0; i< iterations; i++ ) {
+        	try {
+        		temp = encoder.canonicalize( attack, true );
+        	} catch( IntrusionException e ) { 
+        		// expected
+        	}
+        }
+    	stop = System.currentTimeMillis();
+        System.out.println( "Attack Strict: " + (stop-start) );
+    }
+    
+
+    public void testConcurrency() {
+        System.out.println("Encoder Concurrency");
+		for (int i = 0; i < 10; i++) {
+			new Thread( new EncoderConcurrencyMock( i )).start();
+		}
+	}
+
+    /**
+     *  A simple class that calls the Encoder to test thread safety
+     */
+    public class EncoderConcurrencyMock implements Runnable {
+    	public int num = 0;
+    	public EncoderConcurrencyMock( int num ) {
+    		this.num = num;
+    	}
+	    public void run() {
+			while( true ) {
+				String nonce = ESAPI.randomizer().getRandomString( 20, EncoderConstants.CHAR_SPECIALS );
+				String result = javaScriptEncode( nonce );
+				// randomize the threads
+				try {
+					Thread.sleep( ESAPI.randomizer().getRandomInteger( 100, 500 ) );
+				} catch (InterruptedException e) {
+					// just continue
+				}
+				assertTrue( result.equals ( javaScriptEncode( nonce ) ) );
+			}
+		}
+		
+	    public String javaScriptEncode(String str) {
+			Encoder encoder = DefaultEncoder.getInstance();
+			return encoder.encodeForJavaScript(str);
+		}
+    }
+    
+}
+
diff --git a/src/test/java/org/owasp/esapi/reference/ExecutorTest.java b/src/test/java/org/owasp/esapi/reference/ExecutorTest.java
new file mode 100644
index 0000000..f97a81c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/ExecutorTest.java
@@ -0,0 +1,269 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.ExecuteResult;
+import org.owasp.esapi.Executor;
+import org.owasp.esapi.SecurityConfiguration;
+import org.owasp.esapi.SecurityConfigurationWrapper;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.codecs.UnixCodec;
+import org.owasp.esapi.codecs.WindowsCodec;
+
+/**
+ * The Class ExecutorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ExecutorTest extends TestCase {
+
+	private SecurityConfiguration origConfig;
+
+	private static class Conf extends SecurityConfigurationWrapper
+	{
+		private final List allowedExes;
+		private final File workingDir;
+
+		Conf(SecurityConfiguration orig, List allowedExes, File workingDir)
+		{
+			super(orig);
+			this.allowedExes = allowedExes;
+			this.workingDir = workingDir;
+		}
+
+		@Override
+		public List getAllowedExecutables()
+		{
+			return allowedExes;
+		}
+
+		@Override
+		public File getWorkingDirectory()
+		{
+			return workingDir;
+		}
+	}
+
+	/**
+	 * Instantiates a new executor test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public ExecutorTest(String testName) {
+		super(testName);
+	}
+
+    @Override
+    protected void tearDown() throws Exception {
+        ESAPI.override(null);
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ExecutorTest.class);
+		return suite;
+	}
+
+	/**
+	 * Test of executeOSCommand method, of class org.owasp.esapi.Executor
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testExecuteWindowsSystemCommand() throws Exception {
+		System.out.println("executeWindowsSystemCommand");
+
+		if ( System.getProperty("os.name").indexOf("Windows") == -1 ) {
+			System.out.println("testExecuteWindowsSystemCommand - on non-Windows platform, exiting");
+			return;	// Not windows, not going to execute this path
+		}
+		File tmpDir = new File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
+		File sysRoot = new File(System.getenv("SystemRoot")).getCanonicalFile();
+		File sys32 = new File(sysRoot,"system32").getCanonicalFile();
+		File cmd = new File(sys32,"cmd.exe").getCanonicalFile();
+		ESAPI.override(
+			new Conf(
+				ESAPI.securityConfiguration(),
+				Collections.singletonList(cmd.getPath()),
+				tmpDir
+			)
+		);
+
+		Codec codec = new WindowsCodec();
+		System.out.println("executeSystemCommand");
+		Executor instance = ESAPI.executor();
+		List params = new ArrayList();
+		try {
+			params.add("/C");
+			params.add("dir");
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			assertTrue(result.getOutput().length() > 0);
+		} catch (Exception e) {
+			e.printStackTrace();
+			fail();
+		}
+		try {
+			File exec2 = new File( cmd.getPath() + ";inject.exe" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			File exec2 = new File( cmd.getPath() + "\\..\\cmd.exe" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			File workdir = new File( "c:\\ridiculous" );
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params), workdir, codec, false, false );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			params.add("&dir");
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+
+		try {
+			params.set( params.size()-1, "c:\\autoexec.bat" );
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+
+		try {
+			params.set( params.size()-1, "c:\\autoexec.bat c:\\config.sys" );
+			ExecuteResult result = instance.executeSystemCommand(cmd, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+	}
+
+	/**
+	 * Test of executeOSCommand method, of class org.owasp.esapi.Executor
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testExecuteUnixSystemCommand() throws Exception {
+		System.out.println("executeUnixSystemCommand");
+
+		if ( System.getProperty("os.name").indexOf("Windows") != -1 ) {
+			System.out.println("executeUnixSystemCommand - on Windows platform, exiting");
+			return;
+		}
+
+		// FIXME: need more test cases to use this codec
+		Codec codec = new UnixCodec();
+
+		// make sure we have what /bin/sh is pointing at in the allowed exes for the test
+		// and a usable working dir
+		File binSh = new File("/bin/sh").getCanonicalFile();
+		ESAPI.override(
+			new Conf(
+				ESAPI.securityConfiguration(),
+				Collections.singletonList(binSh.getPath()),
+				new File("/tmp")
+			)
+		);
+
+		Executor instance = ESAPI.executor();
+		File executable = binSh;
+		List params = new ArrayList();
+		try {
+			params.add("-c");
+			params.add("ls");
+			params.add("/");
+			ExecuteResult result = instance.executeSystemCommand(executable, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			assertTrue(result.getOutput().length() > 0);
+		} catch (Exception e) {
+			fail(e.getMessage());
+		}
+		try {
+			File exec2 = new File( executable.getPath() + ";./inject" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			File exec2 = new File( executable.getPath() + "/../bin/sh" );
+			ExecuteResult result = instance.executeSystemCommand(exec2, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+			fail();
+		} catch (Exception e) {
+			// expected
+		}
+		try {
+			params.add(";ls");
+			ExecuteResult result = instance.executeSystemCommand(executable, new ArrayList(params) );
+			System.out.println( "RESULT: " + result );
+		} catch (Exception e) {
+			fail();
+		}
+
+		try {
+			File cwd = new File(".");
+			File script = File.createTempFile("ESAPI-ExecutorTest", "sh", cwd);
+			script.deleteOnExit();
+			FileWriter output = new FileWriter(script);
+			try {
+				output.write("i=0\nwhile [ $i -lt 8192 ]\ndo\necho stdout data\necho stderr data >&2\ni=$((i+1))\ndone\n");
+			} finally {
+				output.close();
+			}
+			List deadlockParams = new ArrayList();
+			deadlockParams.add(script.getName());
+			ExecuteResult result = instance.executeSystemCommand(executable, deadlockParams, cwd, codec, true, false);
+			System.out.println( "RESULT: " + result.getExitValue() );
+			assertEquals(0, result.getExitValue());
+		} catch (Exception e) {
+			fail();
+		}
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java
new file mode 100644
index 0000000..40f9e21
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java
@@ -0,0 +1,507 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.HTTPUtilities;
+import org.owasp.esapi.User;
+import org.owasp.esapi.codecs.Hex;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+import org.owasp.esapi.http.MockHttpSession;
+import org.owasp.esapi.util.FileTestUtils;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * The Class HTTPUtilitiesTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class HTTPUtilitiesTest extends TestCase
+{
+	private static final Class<HTTPUtilitiesTest> CLASS = HTTPUtilitiesTest.class;
+	private static final String CLASS_NAME = CLASS.getName();
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		return new TestSuite(HTTPUtilitiesTest.class);
+	}
+
+	/**
+	 * Instantiates a new HTTP utilities test.
+	 * 
+	 * @param testName the test name
+	 */
+	public HTTPUtilitiesTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @throws Exception
+	 */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * @throws Exception
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	public void testCSRFToken() throws Exception {
+		System.out.println( "CSRFToken");
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user = ESAPI.authenticator().createUser(username, "addCSRFToken", "addCSRFToken");
+		ESAPI.authenticator().setCurrentUser( user );
+		String token = ESAPI.httpUtilities().getCSRFToken();
+		assertEquals( 8, token.length() );
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		try {
+			ESAPI.httpUtilities().verifyCSRFToken(request);
+			fail();
+		} catch( Exception e ) {
+			// expected
+		}
+		request.addParameter( DefaultHTTPUtilities.CSRF_TOKEN_NAME, token );
+		ESAPI.httpUtilities().verifyCSRFToken(request);
+	}
+
+	/**
+	 * Test of addCSRFToken method, of class org.owasp.esapi.HTTPUtilities.
+	 * @throws AuthenticationException 
+	 */
+	public void testAddCSRFToken() throws AuthenticationException {
+		Authenticator instance = ESAPI.authenticator();
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		User user = instance.createUser(username, "addCSRFToken", "addCSRFToken");
+		instance.setCurrentUser( user );
+
+		System.out.println("addCSRFToken");
+		String csrf1=ESAPI.httpUtilities().addCSRFToken("/test1");
+		System.out.println( "CSRF1:" + csrf1);
+		assertTrue(csrf1.indexOf("?") > -1);
+
+		String csrf2=ESAPI.httpUtilities().addCSRFToken("/test1?one=two");
+		System.out.println( "CSRF1:" + csrf1);
+		assertTrue(csrf2.indexOf("&") > -1);
+	}
+
+
+	/**
+	 * Test of assertSecureRequest method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testAssertSecureRequest() {
+		System.out.println("assertSecureRequest");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		try {
+			request.setRequestURL( "http://example.com");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( "ftp://example.com");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( "");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( null );
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			fail();
+		} catch( Exception e ) {
+			// pass
+		}
+		try {
+			request.setRequestURL( "https://example.com");
+			ESAPI.httpUtilities().assertSecureRequest( request );
+			// pass
+		} catch( Exception e ) {
+			fail();
+		}
+	}
+
+
+	/**
+	 * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities.
+	 * 
+	 * @throws EnterpriseSecurityException
+	 */
+	public void testChangeSessionIdentifier() throws EnterpriseSecurityException {
+		System.out.println("changeSessionIdentifier");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		MockHttpSession session = (MockHttpSession) request.getSession();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		session.setAttribute("one", "one");
+		session.setAttribute("two", "two");
+		session.setAttribute("three", "three");
+		String id1 = session.getId();
+		session = (MockHttpSession) ESAPI.httpUtilities().changeSessionIdentifier( request );
+		String id2 = session.getId();
+		assertTrue(!id1.equals(id2));
+		assertEquals("one", (String) session.getAttribute("one"));
+	}
+
+	/**
+	 * Test of formatHttpRequestForLog method, of class org.owasp.esapi.HTTPUtilities.
+	 * @throws IOException 
+	 */
+	public void testGetFileUploads() throws IOException {
+		File home = null;
+
+		try
+		{
+			home = FileTestUtils.createTmpDirectory(CLASS_NAME);
+			String content = "--ridiculous\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"testupload.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nThis is a test of the multipart broadcast system.\r\nThis is only a test.\r\nStop.\r\n\r\n--ridiculous\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit Query\r\n--ridiculous--\r\nEpilogue";
+
+			MockHttpServletResponse response = new MockHttpServletResponse();    
+			MockHttpServletRequest request1 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding()));
+			ESAPI.httpUtilities().setCurrentHTTP(request1, response);
+			try {
+				ESAPI.httpUtilities().getFileUploads(request1, home);
+				fail();
+			} catch( ValidationException e ) {
+				// expected
+			}
+
+			MockHttpServletRequest request2 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding()));
+			request2.setContentType( "multipart/form-data; boundary=ridiculous");
+			ESAPI.httpUtilities().setCurrentHTTP(request2, response);
+			try {
+				List list = ESAPI.httpUtilities().getFileUploads(request2, home);
+				Iterator i = list.iterator();
+				while ( i.hasNext() ) {
+					File f = (File)i.next();
+					System.out.println( "  " + f.getAbsolutePath() );
+				}
+				assertTrue( list.size() > 0 );
+			} catch (ValidationException e) {
+				fail();
+			}
+
+			MockHttpServletRequest request4 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding()));
+			request4.setContentType( "multipart/form-data; boundary=ridiculous");
+			ESAPI.httpUtilities().setCurrentHTTP(request4, response);
+			System.err.println("UPLOAD DIRECTORY: " + ESAPI.securityConfiguration().getUploadDirectory());
+			try {
+				List list = ESAPI.httpUtilities().getFileUploads(request4, home);
+				Iterator i = list.iterator();
+				while ( i.hasNext() ) {
+					File f = (File)i.next();
+					System.out.println( "  " + f.getAbsolutePath() );
+				}
+				assertTrue( list.size() > 0 );
+			} catch (ValidationException e) {
+				System.err.println("ERROR: " + e.toString());
+				fail();
+			}
+
+			MockHttpServletRequest request3 = new MockHttpServletRequest("/test", content.replaceAll("txt", "ridiculous").getBytes(response.getCharacterEncoding()));
+			request3.setContentType( "multipart/form-data; boundary=ridiculous");
+			ESAPI.httpUtilities().setCurrentHTTP(request3, response);
+			try {
+				ESAPI.httpUtilities().getFileUploads(request3, home);
+				fail();
+			} catch (ValidationException e) {
+				// expected
+			}
+		}
+		finally
+		{
+			FileTestUtils.deleteRecursively(home);
+		}
+
+	}
+
+
+
+	/**
+	 * Test of killAllCookies method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testKillAllCookies() {
+		System.out.println("killAllCookies");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		assertTrue(response.getCookies().isEmpty());
+		ArrayList list = new ArrayList();
+		list.add(new Cookie("test1", "1"));
+		list.add(new Cookie("test2", "2"));
+		list.add(new Cookie("test3", "3"));
+		request.setCookies(list);
+		ESAPI.httpUtilities().killAllCookies(request, response);
+		assertTrue(response.getCookies().size() == 3);
+	}
+
+	/**
+	 * Test of killCookie method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testKillCookie() {
+		System.out.println("killCookie");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		assertTrue(response.getCookies().isEmpty());
+		ArrayList list = new ArrayList();
+		list.add(new Cookie("test1", "1"));
+		list.add(new Cookie("test2", "2"));
+		list.add(new Cookie("test3", "3"));
+		request.setCookies(list);
+		ESAPI.httpUtilities().killCookie( request, response, "test1" );
+		assertTrue(response.getCookies().size() == 1);
+	}
+
+	/**
+	 * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities.
+	 * 
+	 * @throws ValidationException the validation exception
+	 * @throws IOException Signals that an I/O exception has occurred.
+	 */
+	public void testSendSafeRedirect() throws Exception {
+		System.out.println("sendSafeRedirect");
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		try {
+			ESAPI.httpUtilities().sendRedirect(response, "/test1/abcdefg");
+			ESAPI.httpUtilities().sendRedirect(response,"/test2/1234567");
+		} catch (IOException e) {
+			fail();
+		}
+		try {
+			ESAPI.httpUtilities().sendRedirect(response,"http://www.aspectsecurity.com");
+			fail();
+		} catch (IOException e) {
+			// expected
+		}
+		try {
+			ESAPI.httpUtilities().sendRedirect(response,"/ridiculous");
+			fail();
+		} catch (IOException e) {
+			// expected
+		}
+	}
+
+	/**
+	 * Test of setCookie method, of class org.owasp.esapi.HTTPUtilities.
+	 */
+	public void testSetCookie() {
+		System.out.println("setCookie");
+		HTTPUtilities instance = ESAPI.httpUtilities(); 
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		assertTrue(response.getHeaderNames().isEmpty());
+
+		instance.addCookie( response, new Cookie( "test1", "test1" ) );
+		assertTrue(response.getHeaderNames().size() == 1);
+
+		instance.addCookie( response, new Cookie( "test2", "test2" ) );
+		assertTrue(response.getHeaderNames().size() == 2);
+
+		// test illegal name
+		instance.addCookie( response, new Cookie( "tes<t3", "test3" ) );
+		assertTrue(response.getHeaderNames().size() == 2);
+
+		// test illegal value
+		instance.addCookie( response, new Cookie( "test3", "tes<t3" ) );
+		assertTrue(response.getHeaderNames().size() == 2);
+	}
+
+	/**
+	 *
+	 * @throws java.lang.Exception
+	 */
+	public void testGetStateFromEncryptedCookie() throws Exception {
+		System.out.println("getStateFromEncryptedCookie");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+
+		// test null cookie array
+		Map empty = ESAPI.httpUtilities().decryptStateFromCookie(request);
+		assertTrue( empty.isEmpty() );
+
+		HashMap map = new HashMap();
+		map.put( "one", "aspect" );
+		map.put( "two", "ridiculous" );
+		map.put( "test_hard", "&(@#*!^|;,." );
+		try {
+			ESAPI.httpUtilities().encryptStateInCookie(response, map);
+			String value = response.getHeader( "Set-Cookie" );
+			String encrypted = value.substring(value.indexOf("=")+1, value.indexOf(";"));
+			request.setCookie( DefaultHTTPUtilities.ESAPI_STATE, encrypted );
+			Map state = ESAPI.httpUtilities().decryptStateFromCookie(request);
+			Iterator i = map.entrySet().iterator();
+			while ( i.hasNext() ) {
+				Map.Entry entry = (Map.Entry)i.next();
+				String origname = (String)entry.getKey();
+				String origvalue = (String)entry.getValue();
+				if( !state.get( origname ).equals( origvalue ) ) {
+					fail();
+				}
+			}
+		} catch( EncryptionException e ) {
+			fail();
+		}
+	}
+
+	/**
+	 *
+	 */
+	public void testSaveStateInEncryptedCookie() {
+		System.out.println("saveStateInEncryptedCookie");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		HashMap map = new HashMap();
+		map.put( "one", "aspect" );
+		map.put( "two", "ridiculous" );
+		map.put( "test_hard", "&(@#*!^|;,." );
+		try {
+			ESAPI.httpUtilities().encryptStateInCookie(response,map);
+			String value = response.getHeader( "Set-Cookie" );
+			String encrypted = value.substring(value.indexOf("=")+1, value.indexOf(";"));
+			byte[] serializedCiphertext = Hex.decode(encrypted);
+	        CipherText restoredCipherText =
+	            CipherText.fromPortableSerializedBytes(serializedCiphertext);
+	        ESAPI.encryptor().decrypt(restoredCipherText);
+		} catch( EncryptionException e ) {
+			fail();
+		}
+	}
+
+
+	/**
+	 *
+	 */
+	public void testSaveTooLongStateInEncryptedCookieException() {
+		System.out.println("saveTooLongStateInEncryptedCookie");
+
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+
+		String foo = ESAPI.randomizer().getRandomString(4096, EncoderConstants.CHAR_ALPHANUMERICS);
+
+		HashMap map = new HashMap();
+		map.put("long", foo);
+		try {
+			ESAPI.httpUtilities().encryptStateInCookie(response, map);
+			fail("Should have thrown an exception");
+		}
+		catch (EncryptionException expected) {
+			//expected
+		}    	
+	}
+
+	/**
+	 * Test set no cache headers.
+	 */
+	public void testSetNoCacheHeaders() {
+		System.out.println("setNoCacheHeaders");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		assertTrue(response.getHeaderNames().isEmpty());
+		response.addHeader("test1", "1");
+		response.addHeader("test2", "2");
+		response.addHeader("test3", "3");
+		assertFalse(response.getHeaderNames().isEmpty());
+		ESAPI.httpUtilities().setNoCacheHeaders( response );
+		assertTrue(response.containsHeader("Cache-Control"));
+		assertTrue(response.containsHeader("Expires"));
+	}
+
+	/**
+	 *
+	 * @throws org.owasp.esapi.errors.AuthenticationException
+	 */
+	public void testSetRememberToken() throws AuthenticationException {
+		System.out.println("setRememberToken");
+		Authenticator instance = (Authenticator)ESAPI.authenticator();
+		String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = instance.generateStrongPassword();
+		User user = instance.createUser(accountName, password, password);
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		request.addParameter("username", accountName);
+		request.addParameter("password", password);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		instance.login( request, response);
+
+		int maxAge = ( 60 * 60 * 24 * 14 );
+		ESAPI.httpUtilities().setRememberToken( request, response, password, maxAge, "domain", "/" );
+		// Can't test this because we're using safeSetCookie, which sets a header, not a real cookie!
+		// String value = response.getCookie( Authenticator.REMEMBER_TOKEN_COOKIE_NAME ).getValue();
+		// assertEquals( user.getRememberToken(), value );
+	}
+
+	public void testGetSessionAttribute() throws Exception {
+		HttpServletRequest request = new MockHttpServletRequest();
+		HttpSession session = request.getSession();
+		session.setAttribute("testAttribute", 43f);
+
+		try {
+			Integer test1 = ESAPI.httpUtilities().getSessionAttribute( session, "testAttribute" );
+			fail();
+		} catch ( ClassCastException cce ) {}
+
+		Float test2 = ESAPI.httpUtilities().getSessionAttribute( session, "testAttribute" );
+		assertEquals( test2, 43f );
+	}
+
+	public void testGetRequestAttribute() throws Exception {
+		HttpServletRequest request = new MockHttpServletRequest();
+		request.setAttribute( "testAttribute", 43f );
+		try {
+			Integer test1 = ESAPI.httpUtilities().getRequestAttribute( request, "testAttribute" );
+			fail();
+		} catch ( ClassCastException cce ) {}
+
+		Float test2 = ESAPI.httpUtilities().getRequestAttribute( request, "testAttribute" );
+		assertEquals( test2, 43f );
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/IntegerAccessReferenceMapTest.java b/src/test/java/org/owasp/esapi/reference/IntegerAccessReferenceMapTest.java
new file mode 100644
index 0000000..c4afc8e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/IntegerAccessReferenceMapTest.java
@@ -0,0 +1,223 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+
+
+/**
+ * The Class AccessReferenceMapTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntegerAccessReferenceMapTest extends TestCase {
+    
+    /**
+	 * Instantiates a new access reference map test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public IntegerAccessReferenceMapTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(IntegerAccessReferenceMapTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test of update method, of class org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AuthenticationException
+     *             the authentication exception
+     * @throws EncryptionException
+	 */
+    public void testUpdate() throws AuthenticationException, EncryptionException {
+        System.out.println("update");
+    	IntegerAccessReferenceMap arm = new IntegerAccessReferenceMap();
+    	Authenticator auth = ESAPI.authenticator();
+    	
+    	String pass = auth.generateStrongPassword();
+    	User u = auth.createUser( "armUpdate", pass, pass );
+    	
+    	// test to make sure update returns something
+		arm.update(auth.getUserNames());
+		String indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect == null ) fail();
+		
+		// test to make sure update removes items that are no longer in the list
+		auth.removeUser( u.getAccountName() );
+		arm.update(auth.getUserNames());
+		indirect = arm.getIndirectReference( u.getAccountName() );
+		if ( indirect != null ) fail();
+		
+		// test to make sure old indirect reference is maintained after an update
+		arm.update(auth.getUserNames());
+		String newIndirect = arm.getIndirectReference( u.getAccountName() );
+		assertEquals(indirect, newIndirect);
+    }
+    
+    
+    /**
+	 * Test of iterator method, of class org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testIterator() {
+        System.out.println("iterator");
+    	IntegerAccessReferenceMap arm = new IntegerAccessReferenceMap();
+        Authenticator auth = ESAPI.authenticator();
+        
+		arm.update(auth.getUserNames());
+
+		Iterator i = arm.iterator();
+		while ( i.hasNext() ) {
+			String userName = (String)i.next();
+			User u = auth.getUser( userName );
+			if ( u == null ) fail();
+		}
+    }
+    
+    /**
+	 * Test of getIndirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 */
+    public void testGetIndirectReference() {
+        System.out.println("getIndirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String expResult = directReference;
+        String result = instance.getIndirectReference(directReference);
+        assertNotSame(expResult, result);        
+    }
+
+    /**
+	 * Test of getDirectReference method, of class
+	 * org.owasp.esapi.AccessReferenceMap.
+	 * 
+	 * @throws AccessControlException
+	 *             the access control exception
+	 */
+    public void testGetDirectReference() throws AccessControlException {
+        System.out.println("getDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String ind = instance.getIndirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+        try {
+        	instance.getDirectReference("invalid");
+        	fail();
+        } catch( AccessControlException e ) {
+        	// success
+        }
+    }
+    
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testAddDirectReference() throws AccessControlException {
+        System.out.println("addDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String newDirect = instance.addDirectReference("newDirect");
+        assertNotNull( newDirect );
+        String ind = instance.addDirectReference(directReference);
+        String dir = (String)instance.getDirectReference(ind);
+        assertEquals(directReference, dir);
+    	String newInd = instance.addDirectReference(directReference);
+    	assertEquals(ind, newInd);
+    }
+
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AccessControlException
+     */
+    public void testRemoveDirectReference() throws AccessControlException {
+        System.out.println("removeDirectReference");
+        
+        String directReference = "234";
+        Set list = new HashSet();
+        list.add( "123" );
+        list.add( directReference );
+        list.add( "345" );
+        IntegerAccessReferenceMap instance = new IntegerAccessReferenceMap( list );
+        
+        String indirect = instance.getIndirectReference(directReference);
+        assertNotNull(indirect);
+        String deleted = instance.removeDirectReference(directReference);
+        assertEquals(indirect,deleted);
+    	deleted = instance.removeDirectReference("ridiculous");
+    	assertNull(deleted);
+    }
+    
+    
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/IntrusionDetectorTest.java b/src/test/java/org/owasp/esapi/reference/IntrusionDetectorTest.java
new file mode 100644
index 0000000..e7f79d5
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/IntrusionDetectorTest.java
@@ -0,0 +1,132 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.errors.IntrusionException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class IntrusionDetectorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class IntrusionDetectorTest extends TestCase {
+
+	/**
+	 * Instantiates a new intrusion detector test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public IntrusionDetectorTest(String testName) {
+		super(testName);
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(IntrusionDetectorTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of addException method, of class org.owasp.esapi.IntrusionDetector.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testAddException() throws AuthenticationException {
+		System.out.println("addException");
+		ESAPI.intrusionDetector().addException( new RuntimeException("message") );
+		ESAPI.intrusionDetector().addException( new ValidationException("user message", "log message") );
+		ESAPI.intrusionDetector().addException( new IntrusionException("user message", "log message") );
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator auth = ESAPI.authenticator();
+		User user = auth.createUser(username, "addException", "addException");
+		user.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		user.loginWithPassword("addException");
+		
+		// Now generate some exceptions to disable account
+		for ( int i = 0; i < ESAPI.securityConfiguration().getQuota(IntegrityException.class.getName()).count; i++ ) {
+            // EnterpriseSecurityExceptions are added to IntrusionDetector automatically
+            new IntegrityException( "IntegrityException " + i, "IntegrityException " + i );
+		}
+        assertFalse( user.isLoggedIn() );
+	}
+
+    
+    /**
+     * Test of addEvent method, of class org.owasp.esapi.IntrusionDetector.
+     * 
+     * @throws AuthenticationException
+     *             the authentication exception
+     */
+    public void testAddEvent() throws AuthenticationException {
+        System.out.println("addEvent");
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        Authenticator auth = ESAPI.authenticator();
+		User user = auth.createUser(username, "addEvent", "addEvent");
+		user.enable();
+	    MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		user.loginWithPassword("addEvent");
+        
+        // Now generate some events to disable user account
+        for ( int i = 0; i < ESAPI.securityConfiguration().getQuota("event.test").count; i++ ) {
+            ESAPI.intrusionDetector().addEvent("test", "test message");
+        }
+        assertFalse( user.isEnabled() );
+    }
+    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/JavaLoggerTest.java b/src/test/java/org/owasp/esapi/reference/JavaLoggerTest.java
new file mode 100644
index 0000000..88d42bc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/JavaLoggerTest.java
@@ -0,0 +1,282 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class LoggerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class JavaLoggerTest extends TestCase {
+
+	private static int testCount = 0;
+	
+	private static Logger testLogger = null;
+
+	
+    /**
+	 * Instantiates a new logger test.
+	 * 
+	 * @param testName the test name
+	 */
+    public JavaLoggerTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+        UnitTestSecurityConfiguration tmpConfig = new UnitTestSecurityConfiguration((DefaultSecurityConfiguration) ESAPI.securityConfiguration());
+        tmpConfig.setLogImplementation( JavaLogFactory.class.getName() );
+        ESAPI.override(tmpConfig);
+    	//This ensures a clean logger between tests
+    	testLogger = ESAPI.getLogger( "test" + testCount++ );
+    	System.out.println("Test logger: " + testLogger);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	//this helps, with garbage collection
+    	testLogger = null;
+        ESAPI.override(null);
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(JavaLoggerTest.class);    
+        return suite;
+    }
+    
+    /**
+     * Test of logHTTPRequest method, of class org.owasp.esapi.Logger.
+     * 
+     * @throws ValidationException
+     *             the validation exception
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     * @throws AuthenticationException
+     *             the authentication exception
+     */
+    public void testLogHTTPRequest() throws ValidationException, IOException, AuthenticationException {
+        System.out.println("logHTTPRequest");
+        String[] ignore = {"password","ssn","ccn"};
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        Logger logger = ESAPI.getLogger("logger");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+        request.addParameter("one","one");
+        request.addParameter("two","two1");
+        request.addParameter("two","two2");
+        request.addParameter("password","jwilliams");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+    }    
+    
+    
+    /**
+     * Test of setLevel method of the inner class org.owasp.esapi.reference.JavaLogger that is defined in 
+     * org.owasp.esapi.reference.JavaLogFactory.
+     */
+    public void testSetLevel() {
+        System.out.println("setLevel");
+        
+        // The following tests that the default logging level is set to WARNING. Since the default might be changed
+        // in the ESAPI security configuration file, these are commented out.
+//       	assertTrue(testLogger.isWarningEnabled());
+//       	assertFalse(testLogger.isInfoEnabled());
+
+        // First, test all the different logging levels
+        testLogger.setLevel( Logger.ALL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.TRACE );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.DEBUG );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.INFO );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.WARNING );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.ERROR );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.FATAL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.OFF );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	//Now test to see if a change to the logging level in one log affects other logs
+       	Logger newLogger = ESAPI.getLogger( "test_num2" );
+       	testLogger.setLevel( Logger.OFF );
+       	newLogger.setLevel( Logger.INFO );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	assertTrue(newLogger.isFatalEnabled());
+       	assertTrue(newLogger.isErrorEnabled());
+       	assertTrue(newLogger.isWarningEnabled());
+       	assertTrue(newLogger.isInfoEnabled());
+       	assertFalse(newLogger.isDebugEnabled());
+       	assertFalse(newLogger.isTraceEnabled());
+    }
+
+    
+    /**
+	 * Test of info method, of class org.owasp.esapi.Logger.
+	 */
+    public void testInfo() {
+        System.out.println("info");
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message" );
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "%3escript%3f test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "<script> test message", null );
+    }
+
+    /**
+	 * Test of trace method, of class org.owasp.esapi.Logger.
+	 */
+    public void testTrace() {
+        System.out.println("trace");
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace" );
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace", null );
+    }
+
+    /**
+	 * Test of debug method, of class org.owasp.esapi.Logger.
+	 */
+    public void testDebug() {
+        System.out.println("debug");
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug" );
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug", null );
+    }
+
+    /**
+	 * Test of error method, of class org.owasp.esapi.Logger.
+	 */
+    public void testError() {
+        System.out.println("error");
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error" );
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error", null );
+    }
+
+    /**
+	 * Test of warning method, of class org.owasp.esapi.Logger.
+	 */
+    public void testWarning() {
+        System.out.println("warning");
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning" );
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning", null );
+    }
+
+    /**
+	 * Test of fatal method, of class org.owasp.esapi.Logger.
+	 */
+    public void testFatal() {
+        System.out.println("fatal");
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal" );
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal", null );
+    }
+    
+    /**
+     * Test of always method, of class org.owasp.esapi.Logger.
+     */
+    public void testAlways() {
+
+        System.out.println("always");
+        testLogger.always(Logger.SECURITY_SUCCESS, "test message always 1 (SECURITY_SUCCESS)" );
+        testLogger.always(Logger.SECURITY_AUDIT,   "test message always 2 (SECURITY_AUDIT)" );
+        testLogger.always(Logger.SECURITY_SUCCESS, "test message always 3 (SECURITY_SUCCESS)", null );
+        testLogger.always(Logger.SECURITY_AUDIT,   "test message always 4 (SECURITY_AUDIT)", null );
+        try {
+        	throw new RuntimeException("What? You call that a 'throw'? My grandmother throws " +
+        							   "better than that and she's been dead for more than 10 years!");
+        } catch(RuntimeException rtex) {
+            testLogger.always(Logger.SECURITY_AUDIT,   "test message always 5", rtex );
+        }
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java b/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java
new file mode 100644
index 0000000..cddf71e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java
@@ -0,0 +1,463 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Logger;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * The Class LoggerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author August Detlefsen (augustd at codemagi dot com) <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ */
+public class Log4JLoggerTest extends TestCase {
+	private static int testCount = 0;
+	
+	private static Logger testLogger = null;
+
+	//a logger for explicit tests of log4j logging methods
+	private static Log4JLogger log4JLogger = null;
+
+    /**
+	 * Instantiates a new logger test.
+	 * 
+	 * @param testName the test name
+	 */
+    public Log4JLoggerTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+		//override default log configuration in ESAPI.properties to use Log4JLogFactory
+        UnitTestSecurityConfiguration tmpConfig = new UnitTestSecurityConfiguration((DefaultSecurityConfiguration) ESAPI.securityConfiguration());
+        tmpConfig.setLogImplementation( Log4JLogFactory.class.getName() );
+        ESAPI.override(tmpConfig);
+
+    	//This ensures a clean logger between tests
+    	testLogger = ESAPI.getLogger( "test ExampleExtendedLog4JLogFactory: " + testCount++ );
+    	System.out.println("Test ExampleExtendedLog4JLogFactory logger: " + testLogger);
+
+		//declare this one as Log4JLogger to be able to use Log4J logging methods
+		log4JLogger = (Log4JLogger)ESAPI.getLogger( "test Log4JLogFactory: " + testCount);
+		System.out.println("Test Log4JLogFactory logger: " + log4JLogger);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	//this helps, with garbage collection
+    	testLogger = null;
+		log4JLogger = null;
+
+		ESAPI.override(null);
+	}
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(Log4JLoggerTest.class);    
+        return suite;
+    }
+    
+    /**
+     * Test of logHTTPRequest method, of class org.owasp.esapi.Logger.
+     * 
+     * @throws ValidationException
+     *             the validation exception
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     * @throws AuthenticationException
+     *             the authentication exception
+     */
+    public void testLogHTTPRequest() throws ValidationException, IOException, AuthenticationException {
+        System.out.println("logHTTPRequest");
+        String[] ignore = {"password","ssn","ccn"};
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        Logger logger = ESAPI.getLogger("logger");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+        request.addParameter("one","one");
+        request.addParameter("two","two1");
+        request.addParameter("two","two2");
+        request.addParameter("password","jwilliams");
+        ESAPI.httpUtilities().logHTTPRequest( request, logger, Arrays.asList(ignore) );
+    } 
+    
+    
+    /**
+     * Test of setLevel method of the inner class org.owasp.esapi.reference.JavaLogger that is defined in 
+     * org.owasp.esapi.reference.JavaLogFactory.
+     */
+    public void testSetLevel() {
+        System.out.println("setLevel");
+        
+        // The following tests that the default logging level is set to WARNING. Since the default might be changed
+        // in the ESAPI security configuration file, these are commented out.
+//       	assertTrue(testLogger.isWarningEnabled());
+//       	assertFalse(testLogger.isInfoEnabled());
+
+        // First, test all the different logging levels
+        testLogger.setLevel( Logger.ALL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.TRACE );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertTrue(testLogger.isTraceEnabled());
+
+       	testLogger.setLevel( Logger.DEBUG );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertTrue(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.INFO );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertTrue(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.WARNING );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertTrue(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.ERROR );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertTrue(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.FATAL );
+    	assertTrue(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	testLogger.setLevel( Logger.OFF );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	//Now test to see if a change to the logging level in one log affects other logs
+       	Logger newLogger = ESAPI.getLogger( "test_num2" );
+       	testLogger.setLevel( Logger.OFF );
+       	newLogger.setLevel( Logger.INFO );
+    	assertFalse(testLogger.isFatalEnabled());
+       	assertFalse(testLogger.isErrorEnabled());
+       	assertFalse(testLogger.isWarningEnabled());
+       	assertFalse(testLogger.isInfoEnabled());
+       	assertFalse(testLogger.isDebugEnabled());
+       	assertFalse(testLogger.isTraceEnabled());
+       	
+       	assertTrue(newLogger.isFatalEnabled());
+       	assertTrue(newLogger.isErrorEnabled());
+       	assertTrue(newLogger.isWarningEnabled());
+       	assertTrue(newLogger.isInfoEnabled());
+       	assertFalse(newLogger.isDebugEnabled());
+       	assertFalse(newLogger.isTraceEnabled());
+    }
+
+	/**
+	 * test of loggers without setting explicit log levels
+	 * (log levels set from log4j.xml configuration)
+	 */
+	public void testLogLevels() {
+
+		Logger traceLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestTrace");
+		Logger debugLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestDebug");
+		Logger infoLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestInfo");
+		Logger errorLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestError");
+		Logger warningLogger		= ESAPI.getLogger("org.owasp.esapi.reference.TestWarning");
+		Logger fatalLogger			= ESAPI.getLogger("org.owasp.esapi.reference.TestFatal");
+		Logger unspecifiedLogger	= ESAPI.getLogger("org.owasp.esapi.reference");  //should use package-wide log level configuration (info)
+
+
+		//traceLogger - all log levels should be enabled
+		assertTrue(traceLogger.isTraceEnabled());
+		assertTrue(traceLogger.isDebugEnabled());
+		assertTrue(traceLogger.isInfoEnabled());
+		assertTrue(traceLogger.isWarningEnabled());
+		assertTrue(traceLogger.isErrorEnabled());
+		assertTrue(traceLogger.isFatalEnabled());
+
+		//debugLogger - all log levels should be enabled EXCEPT trace
+		assertFalse(debugLogger.isTraceEnabled());
+		assertTrue(debugLogger.isDebugEnabled());
+		assertTrue(debugLogger.isInfoEnabled());
+		assertTrue(debugLogger.isWarningEnabled());
+		assertTrue(debugLogger.isErrorEnabled());
+		assertTrue(debugLogger.isFatalEnabled());
+
+		//infoLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(infoLogger.isTraceEnabled());
+		assertFalse(infoLogger.isDebugEnabled());
+		assertTrue(infoLogger.isInfoEnabled());
+		assertTrue(infoLogger.isWarningEnabled());
+		assertTrue(infoLogger.isErrorEnabled());
+		assertTrue(infoLogger.isFatalEnabled());
+
+		//warningLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(warningLogger.isTraceEnabled());
+		assertFalse(warningLogger.isDebugEnabled());
+		assertFalse(warningLogger.isInfoEnabled());
+		assertTrue(warningLogger.isWarningEnabled());
+		assertTrue(warningLogger.isErrorEnabled());
+		assertTrue(warningLogger.isFatalEnabled());
+
+		//errorLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(errorLogger.isTraceEnabled());
+		assertFalse(errorLogger.isDebugEnabled());
+		assertFalse(errorLogger.isInfoEnabled());
+		assertFalse(errorLogger.isWarningEnabled());
+		assertTrue(errorLogger.isErrorEnabled());
+		assertTrue(errorLogger.isFatalEnabled());
+
+		//fatalLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(fatalLogger.isTraceEnabled());
+		assertFalse(fatalLogger.isDebugEnabled());
+		assertFalse(fatalLogger.isInfoEnabled());
+		assertFalse(fatalLogger.isWarningEnabled());
+		assertFalse(fatalLogger.isErrorEnabled());
+		assertTrue(fatalLogger.isFatalEnabled());
+
+		//unspecifiedLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(unspecifiedLogger.isTraceEnabled());
+		assertFalse(unspecifiedLogger.isDebugEnabled());
+		assertTrue(unspecifiedLogger.isInfoEnabled());
+		assertTrue(unspecifiedLogger.isWarningEnabled());
+		assertTrue(unspecifiedLogger.isErrorEnabled());
+		assertTrue(unspecifiedLogger.isFatalEnabled());
+	}
+
+	/**
+	 * test of loggers without setting explicit log levels
+	 * (log levels set from log4j.xml configuration)
+	 */
+	public void testLogLevelsWithClass() {
+
+		Logger traceLogger			= ESAPI.getLogger(TestTrace.class);
+		Logger debugLogger			= ESAPI.getLogger(TestDebug.class);
+		Logger infoLogger			= ESAPI.getLogger(TestInfo.class);
+		Logger errorLogger			= ESAPI.getLogger(TestError.class);
+		Logger warningLogger		= ESAPI.getLogger(TestWarning.class);
+		Logger fatalLogger			= ESAPI.getLogger(TestFatal.class);
+		Logger unspecifiedLogger	= ESAPI.getLogger(TestUnspecified.class);  //should use package-wide log level configuration (info)
+
+		//traceLogger - all log levels should be enabled
+		assertTrue(traceLogger.isTraceEnabled());
+		assertTrue(traceLogger.isDebugEnabled());
+		assertTrue(traceLogger.isInfoEnabled());
+		assertTrue(traceLogger.isWarningEnabled());
+		assertTrue(traceLogger.isErrorEnabled());
+		assertTrue(traceLogger.isFatalEnabled());
+
+		//debugLogger - all log levels should be enabled EXCEPT trace
+		assertFalse(debugLogger.isTraceEnabled());
+		assertTrue(debugLogger.isDebugEnabled());
+		assertTrue(debugLogger.isInfoEnabled());
+		assertTrue(debugLogger.isWarningEnabled());
+		assertTrue(debugLogger.isErrorEnabled());
+		assertTrue(debugLogger.isFatalEnabled());
+
+		//infoLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(infoLogger.isTraceEnabled());
+		assertFalse(infoLogger.isDebugEnabled());
+		assertTrue(infoLogger.isInfoEnabled());
+		assertTrue(infoLogger.isWarningEnabled());
+		assertTrue(infoLogger.isErrorEnabled());
+		assertTrue(infoLogger.isFatalEnabled());
+
+		//warningLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(warningLogger.isTraceEnabled());
+		assertFalse(warningLogger.isDebugEnabled());
+		assertFalse(warningLogger.isInfoEnabled());
+		assertTrue(warningLogger.isWarningEnabled());
+		assertTrue(warningLogger.isErrorEnabled());
+		assertTrue(warningLogger.isFatalEnabled());
+
+		//errorLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(errorLogger.isTraceEnabled());
+		assertFalse(errorLogger.isDebugEnabled());
+		assertFalse(errorLogger.isInfoEnabled());
+		assertFalse(errorLogger.isWarningEnabled());
+		assertTrue(errorLogger.isErrorEnabled());
+		assertTrue(errorLogger.isFatalEnabled());
+
+		//fatalLogger - all log levels should be enabled EXCEPT etc.
+		assertFalse(fatalLogger.isTraceEnabled());
+		assertFalse(fatalLogger.isDebugEnabled());
+		assertFalse(fatalLogger.isInfoEnabled());
+		assertFalse(fatalLogger.isWarningEnabled());
+		assertFalse(fatalLogger.isErrorEnabled());
+		assertTrue(fatalLogger.isFatalEnabled());
+
+		//unspecifiedLogger - all log levels should be enabled EXCEPT trace and debug
+		assertFalse(unspecifiedLogger.isTraceEnabled());
+		assertFalse(unspecifiedLogger.isDebugEnabled());
+		assertTrue(unspecifiedLogger.isInfoEnabled());
+		assertTrue(unspecifiedLogger.isWarningEnabled());
+		assertTrue(unspecifiedLogger.isErrorEnabled());
+		assertTrue(unspecifiedLogger.isFatalEnabled());
+	}
+
+    /**
+	 * Test of info method, of class org.owasp.esapi.Logger.
+	 */
+    public void testInfo() {
+        System.out.println("info");
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message" );
+        testLogger.info(Logger.SECURITY_SUCCESS, "test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "%3escript%3f test message", null );
+        testLogger.info(Logger.SECURITY_SUCCESS, "<script> test message", null );
+
+        log4JLogger.info("test message" );
+        log4JLogger.info("test message", null );
+        log4JLogger.info("%3escript%3f test message", null );
+        log4JLogger.info("<script> test message", null );
+
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "test message" );
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "test message", null );
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "%3escript%3f test message", null );
+        log4JLogger.info(Logger.SECURITY_SUCCESS, "<script> test message", null );
+	}
+
+    /**
+	 * Test of trace method, of class org.owasp.esapi.Logger.
+	 */
+    public void testTrace() {
+        System.out.println("trace");
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace" );
+        testLogger.trace(Logger.SECURITY_SUCCESS, "test message trace", null );
+
+        log4JLogger.trace("test message trace" );
+        log4JLogger.trace("test message trace", null );
+	}
+
+    /**
+	 * Test of debug method, of class org.owasp.esapi.Logger.
+	 */
+    public void testDebug() {
+        System.out.println("debug");
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug" );
+        testLogger.debug(Logger.SECURITY_SUCCESS, "test message debug", null );
+
+	    log4JLogger.debug("test message debug" );
+		log4JLogger.debug("test message debug", null );
+	}
+
+    /**
+	 * Test of error method, of class org.owasp.esapi.Logger.
+	 */
+    public void testError() {
+        System.out.println("error");
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error" );
+        testLogger.error(Logger.SECURITY_SUCCESS, "test message error", null );
+
+	    log4JLogger.error("test message error" );
+		log4JLogger.error("test message error", null );
+	}
+
+    /**
+	 * Test of warning method, of class org.owasp.esapi.Logger.
+	 */
+    public void testWarning() {
+        System.out.println("warning");
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning" );
+        testLogger.warning(Logger.SECURITY_SUCCESS, "test message warning", null );
+
+	    log4JLogger.warn("test message warning" );
+		log4JLogger.warn("test message warning", null );
+    }
+
+    /**
+	 * Test of fatal method, of class org.owasp.esapi.Logger.
+	 */
+    public void testFatal() {
+        System.out.println("fatal");
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal" );
+        testLogger.fatal(Logger.SECURITY_SUCCESS, "test message fatal", null );
+
+	    log4JLogger.fatal("test message fatal" );
+		log4JLogger.fatal("test message fatal", null );    
+	}
+    
+    /**
+     * Test of always method, of class org.owasp.esapi.Logger.
+     */
+    public void testAlways() {
+        System.out.println("always");
+        testLogger.always(Logger.SECURITY_SUCCESS, "test message always 1 (SECURITY_SUCCESS)" );
+        testLogger.always(Logger.SECURITY_AUDIT, "test message always 2 (SECURITY_AUDIT)", null );
+
+	    log4JLogger.always("test message always 3" );
+		log4JLogger.always("test message always 4", null );
+
+        try {
+        	throw new RuntimeException("What? You call that a 'throw'??? You couldn't hit the " +
+        							   "broad side of a barn (assuming that barns wore bras).");
+        } catch(RuntimeException rtex) {
+            testLogger.always(Logger.SECURITY_AUDIT, "test message always 5", rtex );
+            log4JLogger.always("test message always 6", rtex);
+        }
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/RandomizerTest.java b/src/test/java/org/owasp/esapi/reference/RandomizerTest.java
new file mode 100644
index 0000000..2fc27f9
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/RandomizerTest.java
@@ -0,0 +1,144 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Randomizer;
+import org.owasp.esapi.codecs.Codec;
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * The Class RandomizerTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class RandomizerTest extends TestCase {
+    
+    /**
+	 * Instantiates a new randomizer test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public RandomizerTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(RandomizerTest.class);        
+        return suite;
+    }
+
+    /**
+	 * Test of getRandomString method, of class org.owasp.esapi.Randomizer.
+	 */
+    public void testGetRandomString() {
+        System.out.println("getRandomString");
+        int length = 20;
+        Randomizer instance = ESAPI.randomizer();
+        for ( int i = 0; i < 100; i++ ) {
+            String result = instance.getRandomString(length, EncoderConstants.CHAR_ALPHANUMERICS );
+            for ( int j=0;j<result.length();j++ ) {
+            	if ( !Codec.containsCharacter( result.charAt(j), EncoderConstants.CHAR_ALPHANUMERICS) ) {
+            		fail();
+            	}
+            }
+            assertEquals(length, result.length());
+        }
+    }
+
+    /**
+	 * Test of getRandomInteger method, of class org.owasp.esapi.Randomizer.
+	 */
+    public void testGetRandomInteger() {
+        System.out.println("getRandomInteger");        
+        int min = -20;
+        int max = 100;
+        Randomizer instance = ESAPI.randomizer();        
+        int minResult = ( max - min ) / 2;
+        int maxResult = ( max - min ) / 2;
+        for ( int i = 0; i < 100; i++ ) {
+            int result = instance.getRandomInteger(min, max);
+            if ( result < minResult ) minResult = result;
+            if ( result > maxResult ) maxResult = result;
+        }
+        assertEquals(true, (minResult >= min && maxResult < max) );
+    }
+
+    /**
+	 * Test of getRandomReal method, of class org.owasp.esapi.Randomizer.
+	 */
+    public void testGetRandomReal() {
+        System.out.println("getRandomReal");
+        float min = -20.5234F;
+        float max = 100.12124F;
+        Randomizer instance = ESAPI.randomizer();
+        float minResult = ( max - min ) / 2;
+        float maxResult = ( max - min ) / 2;
+        for ( int i = 0; i < 100; i++ ) {
+            float result = instance.getRandomReal(min, max);
+            if ( result < minResult ) minResult = result;
+            if ( result > maxResult ) maxResult = result;
+        }
+        assertEquals(true, (minResult >= min && maxResult < max));
+    }
+    
+    
+    /**
+     * Test of getRandomGUID method, of class org.owasp.esapi.Randomizer.
+     * @throws EncryptionException
+     */
+    public void testGetRandomGUID() throws EncryptionException {
+        System.out.println("getRandomGUID");
+        Randomizer instance = ESAPI.randomizer();
+        ArrayList list = new ArrayList();
+        for ( int i = 0; i < 100; i++ ) {
+            String guid = instance.getRandomGUID();
+            if ( list.contains( guid ) ) fail();
+            list.add( guid );
+        }
+    }
+
+     
+}
diff --git a/src/test/java/org/owasp/esapi/reference/SafeFileTest.java b/src/test/java/org/owasp/esapi/reference/SafeFileTest.java
new file mode 100644
index 0000000..ef24f44
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/SafeFileTest.java
@@ -0,0 +1,279 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.util.Iterator;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.SafeFile;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.util.FileTestUtils;
+import org.owasp.esapi.util.CollectionsUtil;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class SafeFileTest extends TestCase
+{
+	private static final Class CLASS = SafeFileTest.class;
+	private static final String CLASS_NAME = CLASS.getName();
+	/** Name of the file in the temporary directory */
+	private static final String TEST_FILE_NAME = "test.file";
+	private static final Set GOOD_FILE_CHARS = CollectionsUtil.strToUnmodifiableSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" /* + "." */);
+	private static final Set BAD_FILE_CHARS = CollectionsUtil.strToUnmodifiableSet("\u0000" + /*(File.separatorChar == '/' ? '\\' : '/') +*/ "*|<>?:" /*+ "~!@#$%^&(){}[],`;"*/);
+
+	private File testDir = null;
+	private File testFile = null;
+
+	String pathWithNullByte = "/temp/file.txt" + (char)0;
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception
+	{
+		// create a file to test with
+		testDir = FileTestUtils.createTmpDirectory(CLASS_NAME).getCanonicalFile();
+		testFile = new File(testDir, TEST_FILE_NAME);
+		testFile.createNewFile();
+		testFile = testFile.getCanonicalFile();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception
+	{
+		FileTestUtils.deleteRecursively(testDir);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SafeFileTest.class);
+		return suite;
+	}
+
+	public void testEscapeCharactersInFilename() {
+		System.out.println("testEscapeCharactersInFilenameInjection");
+		File tf = testFile;
+		if ( tf.exists() ) {
+			System.out.println( "File is there: " + tf );
+		}
+
+		File sf = new File(testDir, "test^.file" );
+		if ( sf.exists() ) {
+			System.out.println( "  Injection allowed "+ sf.getAbsolutePath() );
+		} else {
+			System.out.println( "  Injection didn't work "+ sf.getAbsolutePath() );
+		}
+	}
+
+	public void testEscapeCharacterInDirectoryInjection() {
+		System.out.println("testEscapeCharacterInDirectoryInjection");
+		File sf = new File(testDir, "test\\^.^.\\file");
+		if ( sf.exists() ) {
+			System.out.println( "  Injection allowed "+ sf.getAbsolutePath() );
+		} else {
+			System.out.println( "  Injection didn't work "+ sf.getAbsolutePath() );
+		}
+	}
+
+	public void testJavaFileInjectionGood() throws ValidationException
+	{
+		for(Iterator i=GOOD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+			sf = new SafeFile(testDir, TEST_FILE_NAME + ch + "test");
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+		}		
+	}
+
+	public void testJavaFileInjectionBad()
+	{
+		for(Iterator i=BAD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch  + "test");
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+		}		
+	}
+
+	public void testMultipleJavaFileInjectionGood() throws ValidationException
+	{
+		for(Iterator i=GOOD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			ch = ch + ch + ch;
+			File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+			sf = new SafeFile(testDir, TEST_FILE_NAME + ch + "test");
+			assertFalse("File \"" + TEST_FILE_NAME + ch + "\" should not exist ((int)ch=" + (int)ch.charAt(0) + ").", sf.exists());
+		}		
+	}
+
+	public void testMultipleJavaFileInjectionBad()
+	{
+		for(Iterator i=BAD_FILE_CHARS.iterator();i.hasNext();)
+		{
+			String ch = i.next().toString();	// avoids generic issues in 1.4&1.5
+			ch = ch + ch + ch;
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch);
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+			try
+			{
+				File sf = new SafeFile(testDir, TEST_FILE_NAME + ch  + "test");
+				fail("Able to create SafeFile \"" + TEST_FILE_NAME + ch + "\" ((int)ch=" + (int)ch.charAt(0) + ").");
+			}
+			catch(ValidationException expected)
+			{
+			}
+		}		
+	}
+
+	public void testAlternateDataStream() {
+		try
+		{
+			File sf = new SafeFile(testDir, TEST_FILE_NAME + ":secret.txt");
+			fail("Able to construct SafeFile for alternate data stream: " + sf.getPath());
+		}
+		catch(ValidationException expected)
+		{
+		}
+	}
+
+	static public String toHex(final byte b) {
+		final char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+		final char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
+		return new String(array);
+	}	
+
+	public void testCreatePath() throws Exception
+	{
+		SafeFile sf = new SafeFile(testFile.getPath());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateParentPathName() throws Exception
+	{
+		SafeFile sf = new SafeFile(testDir, testFile.getName());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateParentFileName() throws Exception
+	{
+		SafeFile sf = new SafeFile(testFile.getParentFile(), testFile.getName());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateURI() throws Exception
+	{
+		SafeFile sf = new SafeFile(testFile.toURI());
+		assertTrue(sf.exists());
+	}
+
+	public void testCreateFileNamePercentNull()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testDir + File.separator + "file%00.txt");
+			fail("no exception thrown for file name with percent encoded null");
+		}
+		catch(ValidationException expected)
+		{
+		}
+	}
+
+	public void testCreateFileNameQuestion()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file?.txt");
+			fail("no exception thrown for file name with question mark in it");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+	public void testCreateFileNameNull()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file" + ((char)0) + ".txt");
+			fail("no exception thrown for file name with null in it");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+	public void testCreateFileHighByte()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file" + ((char)160) + ".txt");
+			fail("no exception thrown for file name with high byte in it");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+	public void testCreateParentPercentNull()
+	{
+		try
+		{
+			SafeFile sf = new SafeFile(testFile.getParent() + File.separator + "file%00.txt");
+			fail("no exception thrown for file name with percent encoded null");
+		}
+		catch(ValidationException e)
+		{
+			// expected
+		}
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/TestDebug.java b/src/test/java/org/owasp/esapi/reference/TestDebug.java
new file mode 100644
index 0000000..7b456e4
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/TestDebug.java
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestDebug extends TestCase {
+
+	/** 
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/TestError.java b/src/test/java/org/owasp/esapi/reference/TestError.java
new file mode 100644
index 0000000..98d9845
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/TestError.java
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestError extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/TestFatal.java b/src/test/java/org/owasp/esapi/reference/TestFatal.java
new file mode 100644
index 0000000..5caa11c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/TestFatal.java
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestFatal extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/TestInfo.java b/src/test/java/org/owasp/esapi/reference/TestInfo.java
new file mode 100644
index 0000000..17cd3df
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/TestInfo.java
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestInfo extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/TestTrace.java b/src/test/java/org/owasp/esapi/reference/TestTrace.java
new file mode 100644
index 0000000..b38e65b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/TestTrace.java
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestTrace extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/TestUnspecified.java b/src/test/java/org/owasp/esapi/reference/TestUnspecified.java
new file mode 100644
index 0000000..dc35da3
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/TestUnspecified.java
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestUnspecified extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/TestWarning.java b/src/test/java/org/owasp/esapi/reference/TestWarning.java
new file mode 100644
index 0000000..16c14e3
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/TestWarning.java
@@ -0,0 +1,22 @@
+package org.owasp.esapi.reference;
+
+import junit.framework.TestCase;
+
+/**
+ * Class for testing log levels
+ *
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 15, 2010
+ * @see org.owasp.esapi.reference.Log4JLoggerTest
+ */
+public class TestWarning extends TestCase {
+
+	/**
+	 * Dummy method so that JUnit won't complain
+	 */
+	public void testLogging() {
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/UnitTestSecurityConfiguration.java b/src/test/java/org/owasp/esapi/reference/UnitTestSecurityConfiguration.java
new file mode 100644
index 0000000..82b8427
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/UnitTestSecurityConfiguration.java
@@ -0,0 +1,87 @@
+package org.owasp.esapi.reference;
+
+import java.util.Properties;
+
+public class UnitTestSecurityConfiguration extends DefaultSecurityConfiguration {
+    public UnitTestSecurityConfiguration(DefaultSecurityConfiguration cfg) {
+        super(cfg.getESAPIProperties());
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setApplicationName(String v) {
+    	getESAPIProperties().setProperty(APPLICATION_NAME, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setLogImplementation(String v) {
+    	getESAPIProperties().setProperty(LOG_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setAuthenticationImplementation(String v) {
+    	getESAPIProperties().setProperty(AUTHENTICATION_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setEncoderImplementation(String v) {
+    	getESAPIProperties().setProperty(ENCODER_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setAccessControlImplementation(String v) {
+    	getESAPIProperties().setProperty(ACCESS_CONTROL_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setEncryptionImplementation(String v) {
+    	getESAPIProperties().setProperty(ENCRYPTION_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setIntrusionDetectionImplementation(String v) {
+    	getESAPIProperties().setProperty(INTRUSION_DETECTION_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setRandomizerImplementation(String v) {
+    	getESAPIProperties().setProperty(RANDOMIZER_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setExecutorImplementation(String v) {
+    	getESAPIProperties().setProperty(EXECUTOR_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setHTTPUtilitiesImplementation(String v) {
+    	getESAPIProperties().setProperty(HTTP_UTILITIES_IMPLEMENTATION, v);
+    }
+
+    /**
+	 * {@inheritDoc}
+	 */
+    public void setValidationImplementation(String v) {
+    	getESAPIProperties().setProperty(VALIDATOR_IMPLEMENTATION, v);
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/UserTest.java b/src/test/java/org/owasp/esapi/reference/UserTest.java
new file mode 100644
index 0000000..630976e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/UserTest.java
@@ -0,0 +1,739 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.servlet.http.HttpSession;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.User;
+import org.owasp.esapi.errors.AuthenticationException;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+import org.owasp.esapi.http.MockHttpSession;
+
+/**
+ * The Class UserTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class UserTest extends TestCase {
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UserTest.class);
+		return suite;
+	}
+	
+	/**
+	 * Instantiates a new user test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public UserTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * Creates the test user.
+	 * 
+	 * @param password
+	 *            the password
+	 * 
+	 * @return the user
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	private DefaultUser createTestUser(String password) throws AuthenticationException {
+		String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		Exception e = new Exception();
+		System.out.println("Creating user " + username + " for " + e.getStackTrace()[1].getMethodName());
+		DefaultUser user = (DefaultUser) ESAPI.authenticator().createUser(username, password, password);
+		return user;
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Test of testAddRole method, of class org.owasp.esapi.User.
+	 * 
+	 * @exception Exception
+	 * 				any Exception thrown by testing addRole()
+	 */
+	public void testAddRole() throws Exception {
+		System.out.println("addRole");
+		Authenticator instance = ESAPI.authenticator();
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = ESAPI.authenticator().generateStrongPassword();
+		String role = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_LOWERS);
+		User user = instance.createUser(accountName, password, password);
+		user.addRole(role);
+		assertTrue(user.isInRole(role));
+		assertFalse(user.isInRole("ridiculous"));
+	}
+
+	/**
+	 * Test of addRoles method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testAddRoles() throws AuthenticationException {
+		System.out.println("addRoles");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		Set set = new HashSet();
+		set.add("rolea");
+		set.add("roleb");
+		user.addRoles(set);
+		assertTrue(user.isInRole("rolea"));
+		assertTrue(user.isInRole("roleb"));
+		assertFalse(user.isInRole("ridiculous"));
+	}
+
+	/**
+	 * Test of changePassword method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testChangePassword() throws Exception {
+		System.out.println("changePassword");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = "Password12!@";
+		DefaultUser user = createTestUser(oldPassword);
+		System.out.println("Hash of " + oldPassword + " = " + ((FileBasedAuthenticator)instance).getHashedPassword(user));
+		String password1 = "SomethingElse34#$";
+		user.changePassword(oldPassword, password1, password1);
+		System.out.println("Hash of " + password1 + " = " + ((FileBasedAuthenticator)instance).getHashedPassword(user));
+		assertTrue(user.verifyPassword(password1));
+		String password2 = "YetAnother56%^";
+		user.changePassword(password1, password2, password2);
+		System.out.println("Hash of " + password2 + " = " + ((FileBasedAuthenticator)instance).getHashedPassword(user));
+		try {
+			user.changePassword(password2, password1, password1);
+			fail("Shouldn't be able to reuse a password");
+		} catch( AuthenticationException e ) {
+			// expected
+		}
+		assertTrue(user.verifyPassword(password2));
+		assertFalse(user.verifyPassword("badpass"));
+	}
+
+	/**
+	 * Test of disable method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testDisable() throws AuthenticationException {
+		System.out.println("disable");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.enable();
+		assertTrue(user.isEnabled());
+		user.disable();
+		assertFalse(user.isEnabled());
+	}
+
+	/**
+	 * Test of enable method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testEnable() throws AuthenticationException {
+		System.out.println("enable");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.enable();
+		assertTrue(user.isEnabled());
+		user.disable();
+		assertFalse(user.isEnabled());
+	}
+
+	/**
+	 * Test of failedLoginCount lockout, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 * @throws EncryptionException
+	 *             any EncryptionExceptions thrown by testing failedLoginLockout()
+	 */
+	public void testFailedLoginLockout() throws AuthenticationException, EncryptionException {
+		System.out.println("failedLoginLockout");
+		DefaultUser user = createTestUser("failedLoginLockout");
+		user.enable();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        
+		user.loginWithPassword("failedLoginLockout");
+		
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+ 		System.out.println("FAILED: " + user.getFailedLoginCount());
+		assertFalse(user.isLocked());
+
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		System.out.println("FAILED: " + user.getFailedLoginCount());
+		assertFalse(user.isLocked());
+
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		System.out.println("FAILED: " + user.getFailedLoginCount());
+		assertTrue(user.isLocked());
+	}
+
+	/**
+	 * Test of getAccountName method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetAccountName() throws AuthenticationException {
+		System.out.println("getAccountName");
+		DefaultUser user = createTestUser("getAccountName");
+		String accountName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setAccountName(accountName);
+		assertEquals(accountName.toLowerCase(), user.getAccountName());
+		assertFalse("ridiculous".equals(user.getAccountName()));
+	}
+
+	/**
+	 * Test get last failed login time.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testGetLastFailedLoginTime() throws Exception {
+		System.out.println("getLastLoginTime");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		Date llt1 = user.getLastFailedLoginTime();
+		Thread.sleep(100); // need a short delay to separate attempts
+		try {
+    		user.loginWithPassword("ridiculous");
+		} catch( AuthenticationException e ) { 
+    		// expected
+    	}
+		Date llt2 = user.getLastFailedLoginTime();
+		assertTrue(llt1.before(llt2));
+	}
+
+	/**
+	 * Test get last login time.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testGetLastLoginTime() throws Exception {
+		System.out.println("getLastLoginTime");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.verifyPassword(oldPassword);
+		Date llt1 = user.getLastLoginTime();
+		Thread.sleep(10); // need a short delay to separate attempts
+		user.verifyPassword(oldPassword);
+		Date llt2 = user.getLastLoginTime();
+		assertTrue(llt1.before(llt2));
+	}
+
+	/**
+	 * Test getLastPasswordChangeTime method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws Exception
+	 *             the exception
+	 */
+	public void testGetLastPasswordChangeTime() throws Exception {
+		System.out.println("getLastPasswordChangeTime");
+		DefaultUser user = createTestUser("getLastPasswordChangeTime");
+		Date t1 = user.getLastPasswordChangeTime();
+		Thread.sleep(10); // need a short delay to separate attempts
+		String newPassword = ESAPI.authenticator().generateStrongPassword(user, "getLastPasswordChangeTime");
+		user.changePassword("getLastPasswordChangeTime", newPassword, newPassword);
+		Date t2 = user.getLastPasswordChangeTime();
+		assertTrue(t2.after(t1));
+	}
+
+	/**
+	 * Test of getRoles method, of class org.owasp.esapi.User.
+     *
+     * @throws Exception
+     */
+	public void testGetRoles() throws Exception {
+		System.out.println("getRoles");
+		Authenticator instance = ESAPI.authenticator();
+		String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		String password = ESAPI.authenticator().generateStrongPassword();
+		String role = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_LOWERS);
+		User user = instance.createUser(accountName, password, password);
+		user.addRole(role);
+		Set roles = user.getRoles();
+		assertTrue(roles.size() > 0);
+	}
+
+	/**
+	 * Test of getScreenName method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testGetScreenName() throws AuthenticationException {
+		System.out.println("getScreenName");
+		DefaultUser user = createTestUser("getScreenName");
+		String screenName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setScreenName(screenName);
+		assertEquals(screenName, user.getScreenName());
+		assertFalse("ridiculous".equals(user.getScreenName()));
+	}
+
+    /**
+     *
+     * @throws org.owasp.esapi.errors.AuthenticationException
+     */
+    public void testGetSessions() throws AuthenticationException {
+        System.out.println("getSessions");
+        Authenticator instance = ESAPI.authenticator();
+        String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+        String password = ESAPI.authenticator().generateStrongPassword();
+        User user = instance.createUser(accountName, password, password);
+        HttpSession session1 = new MockHttpSession();
+        user.addSession( session1 );
+        HttpSession session2 = new MockHttpSession();
+        user.addSession( session2 );
+        HttpSession session3 = new MockHttpSession();
+        user.addSession( session3 );
+        Set sessions = user.getSessions();
+        Iterator i = sessions.iterator();
+        while ( i.hasNext() ) {
+            HttpSession s = (HttpSession)i.next();
+            System.out.println( ">>>" + s.getId() );
+        }
+        assertTrue(sessions.size() == 3);
+	}
+	
+	
+    /**
+     *
+     */
+    public void testAddSession() {
+	    // TODO
+	}
+	
+    /**
+     *
+     */
+    public void testRemoveSession() {
+	    // TODO
+	}
+	
+	/**
+	 * Test of incrementFailedLoginCount method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIncrementFailedLoginCount() throws AuthenticationException {
+		System.out.println("incrementFailedLoginCount");
+		DefaultUser user = createTestUser("incrementFailedLoginCount");
+		user.enable();
+		assertEquals(0, user.getFailedLoginCount());
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertEquals(1, user.getFailedLoginCount());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertEquals(2, user.getFailedLoginCount());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertEquals(3, user.getFailedLoginCount());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertTrue(user.isLocked());
+	}
+
+	/**
+	 * Test of isEnabled method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsEnabled() throws AuthenticationException {
+		System.out.println("isEnabled");
+		DefaultUser user = createTestUser("isEnabled");
+		user.disable();
+		assertFalse(user.isEnabled());
+		user.enable();
+		assertTrue(user.isEnabled());
+	}
+
+    
+    
+	/**
+	 * Test of isInRole method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsInRole() throws AuthenticationException {
+		System.out.println("isInRole");
+		DefaultUser user = createTestUser("isInRole");
+		String role = "TestRole";
+		assertFalse(user.isInRole(role));
+		user.addRole(role);
+		assertTrue(user.isInRole(role));
+		assertFalse(user.isInRole("Ridiculous"));
+	}
+
+	/**
+	 * Test of isLocked method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsLocked() throws AuthenticationException {
+		System.out.println("isLocked");
+		DefaultUser user = createTestUser("isLocked");
+		user.lock();
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertFalse(user.isLocked());
+	}
+
+	/**
+	 * Test of isSessionAbsoluteTimeout method, of class
+	 * org.owasp.esapi.IntrusionDetector.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsSessionAbsoluteTimeout() throws AuthenticationException {
+		System.out.println("isSessionAbsoluteTimeout");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		long now = System.currentTimeMillis();
+		// setup request and response
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		MockHttpSession session = (MockHttpSession)request.getSession();
+				
+		// set session creation -3 hours (default is 2 hour timeout)		
+		session.setCreationTime( now - (1000 * 60 * 60 * 3) );
+		assertTrue(user.isSessionAbsoluteTimeout());
+		
+		// set session creation -1 hour (default is 2 hour timeout)
+		session.setCreationTime( now - (1000 * 60 * 60 * 1) );
+		assertFalse(user.isSessionAbsoluteTimeout());
+	}
+
+	/**
+	 * Test of isSessionTimeout method, of class
+	 * org.owasp.esapi.IntrusionDetector.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testIsSessionTimeout() throws AuthenticationException {
+		System.out.println("isSessionTimeout");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		long now = System.currentTimeMillis();
+		// setup request and response
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		MockHttpSession session = (MockHttpSession)request.getSession();
+		
+		// set creation -30 mins (default is 20 min timeout)
+		session.setAccessedTime( now - 1000 * 60 * 30 );
+		assertTrue(user.isSessionTimeout());
+		
+		// set creation -1 hour (default is 20 min timeout)
+		session.setAccessedTime( now - 1000 * 60 * 10 );
+		assertFalse(user.isSessionTimeout());
+	}
+
+	/**
+	 * Test of lockAccount method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLock() throws AuthenticationException {
+		System.out.println("lock");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.lock();
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertFalse(user.isLocked());
+	}
+
+	/**
+	 * Test of loginWithPassword method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLoginWithPassword() throws AuthenticationException {
+		System.out.println("loginWithPassword");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpSession session = (MockHttpSession) request.getSession();
+		assertFalse(session.getInvalidated());
+		DefaultUser user = createTestUser("loginWithPassword");
+		user.enable();
+		user.loginWithPassword("loginWithPassword");
+		assertTrue(user.isLoggedIn());
+		user.logout();
+		assertFalse(user.isLoggedIn());
+		assertFalse(user.isLocked());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertFalse(user.isLoggedIn());
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		try {
+			user.loginWithPassword("ridiculous");
+		} catch (AuthenticationException e) {
+			// expected
+		}
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertTrue(user.getFailedLoginCount() == 0 );
+	}
+
+
+	/**
+	 * Test of logout method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testLogout() throws AuthenticationException {
+		System.out.println("logout");
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		MockHttpSession session = (MockHttpSession) request.getSession();
+		assertFalse(session.getInvalidated());
+		Authenticator instance = ESAPI.authenticator();
+		ESAPI.httpUtilities().setCurrentHTTP(request, response);
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.enable();
+		System.out.println(user.getLastLoginTime());
+		user.loginWithPassword(oldPassword);
+		assertTrue(user.isLoggedIn());
+		// get new session after user logs in
+		session = (MockHttpSession) request.getSession();
+		assertFalse(session.getInvalidated());
+		user.logout();
+		assertFalse(user.isLoggedIn());
+		assertTrue(session.getInvalidated());
+	}
+
+	/**
+	 * Test of testRemoveRole method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testRemoveRole() throws AuthenticationException {
+		System.out.println("removeRole");
+		String role = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_LOWERS);
+		DefaultUser user = createTestUser("removeRole");
+		user.addRole(role);
+		assertTrue(user.isInRole(role));
+		user.removeRole(role);
+		assertFalse(user.isInRole(role));
+	}
+
+	/**
+	 * Test of testResetCSRFToken method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testResetCSRFToken() throws AuthenticationException {
+		System.out.println("resetCSRFToken");
+		DefaultUser user = createTestUser("resetCSRFToken");
+        String token1 = user.resetCSRFToken();
+        String token2 = user.resetCSRFToken();
+        assertFalse( token1.equals( token2 ) );
+	}
+	
+	/**
+	 * Test of setAccountName method, of class org.owasp.esapi.User.
+     *
+     * @throws AuthenticationException
+     */
+	public void testSetAccountName() throws AuthenticationException {
+		System.out.println("setAccountName");
+		DefaultUser user = createTestUser("setAccountName");
+		String accountName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setAccountName(accountName);
+		assertEquals(accountName.toLowerCase(), user.getAccountName());
+		assertFalse("ridiculous".equals(user.getAccountName()));
+	}
+
+	/**
+	 * Test of setExpirationTime method, of class org.owasp.esapi.User.
+     *
+     * @throws Exception
+     */
+	public void testSetExpirationTime() throws Exception {
+		Date longAgo = new Date(0);
+		Date now = new Date();
+		assertTrue("new Date(0) returned " + longAgo + " which is considered before new Date() " + now + ". Please report this output to the email list or as a issue", longAgo.before(now));
+		String password=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+		DefaultUser user = createTestUser(password);
+		user.setExpirationTime(longAgo);
+		assertTrue( user.isExpired() );
+	}
+
+	
+	/**
+	 * Test of setRoles method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetRoles() throws AuthenticationException {
+		System.out.println("setRoles");
+		DefaultUser user = createTestUser("setRoles");
+		user.addRole("user");
+		assertTrue(user.isInRole("user"));
+		Set set = new HashSet();
+		set.add("rolea");
+		set.add("roleb");
+		user.setRoles(set);
+		assertFalse(user.isInRole("user"));
+		assertTrue(user.isInRole("rolea"));
+		assertTrue(user.isInRole("roleb"));
+		assertFalse(user.isInRole("ridiculous"));
+	}
+
+	/**
+	 * Test of setScreenName method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testSetScreenName() throws AuthenticationException {
+		System.out.println("setScreenName");
+		DefaultUser user = createTestUser("setScreenName");
+		String screenName = ESAPI.randomizer().getRandomString(7, EncoderConstants.CHAR_ALPHANUMERICS);
+		user.setScreenName(screenName);
+		assertEquals(screenName, user.getScreenName());
+		assertFalse("ridiculous".equals(user.getScreenName()));
+	}
+
+	/**
+	 * Test of unlockAccount method, of class org.owasp.esapi.User.
+	 * 
+	 * @throws AuthenticationException
+	 *             the authentication exception
+	 */
+	public void testUnlock() throws AuthenticationException {
+		System.out.println("unlockAccount");
+		Authenticator instance = ESAPI.authenticator();
+		String oldPassword = instance.generateStrongPassword();
+		DefaultUser user = createTestUser(oldPassword);
+		user.lock();
+		assertTrue(user.isLocked());
+		user.unlock();
+		assertFalse(user.isLocked());
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java
new file mode 100644
index 0000000..9402630
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java
@@ -0,0 +1,1150 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.*;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.filters.SecurityWrapperRequest;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+import org.owasp.esapi.reference.validation.HTMLValidationRule;
+import org.owasp.esapi.reference.validation.StringValidationRule;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * The Class ValidatorTest.
+ *
+ * @author Mike Fauzy (mike.fauzy at aspectsecurity.com)
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class ValidatorTest extends TestCase {
+
+    private static final String PREFERRED_ENCODING = "UTF-8";
+
+    public static Test suite() {
+        return new TestSuite(ValidatorTest.class);
+    }
+
+    /**
+     * Instantiates a new HTTP utilities test.
+     *
+     * @param testName the test name
+     */
+    public ValidatorTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+        // none
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+        // none
+    }
+
+    public void testAddRule() {
+        Validator validator = ESAPI.validator();
+        ValidationRule rule = new StringValidationRule("ridiculous");
+        validator.addRule(rule);
+        assertEquals(rule, validator.getRule("ridiculous"));
+    }
+
+    public void testAssertValidFileUpload() {
+        //		assertValidFileUpload(String, String, String, byte[], int, boolean, ValidationErrorList)
+    }
+
+    public void testGetPrintable1() {
+        //		getValidPrintable(String, char[], int, boolean, ValidationErrorList)
+    }
+
+    public void testGetPrintable2() {
+        //		getValidPrintable(String, String, int, boolean, ValidationErrorList)
+    }
+
+    public void testGetRule() {
+        Validator validator = ESAPI.validator();
+        ValidationRule rule = new StringValidationRule("rule");
+        validator.addRule(rule);
+        assertEquals(rule, validator.getRule("rule"));
+        assertFalse(rule == validator.getRule("ridiculous"));
+    }
+
+    public void testGetValidCreditCard() {
+        System.out.println("getValidCreditCard");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+
+        assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false));
+        assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false));
+        assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false));
+        assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false));
+
+        instance.getValidCreditCard("cctest5", "1234 9876 0000 0008", false, errors);
+        assertEquals(0, errors.size());
+        instance.getValidCreditCard("cctest6", "1234987600000008", false, errors);
+        assertEquals(0, errors.size());
+        instance.getValidCreditCard("cctest7", "12349876000000081", false, errors);
+        assertEquals(1, errors.size());
+        instance.getValidCreditCard("cctest8", "4417 1234 5678 9112", false, errors);
+        assertEquals(2, errors.size());
+
+        assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false, errors));
+        assertTrue(errors.size()==2);
+        assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false, errors));
+        assertTrue(errors.size()==3);
+        assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false, errors));
+        assertTrue(errors.size()==4);
+    }
+
+    public void testGetValidDate() throws Exception {
+    	System.out.println("getValidDate");
+    	Validator instance = ESAPI.validator();
+    	ValidationErrorList errors = new ValidationErrorList();
+    	assertTrue(instance.getValidDate("datetest1", "June 23, 1967", DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US), false) != null);
+    	instance.getValidDate("datetest2", "freakshow", DateFormat.getDateInstance(), false, errors);
+    	assertEquals(1, errors.size());
+
+    	// TODO: This test case fails due to an apparent bug in SimpleDateFormat
+    	// Note: This seems to be fixed in JDK 6. Will leave it commented out since
+    	//		 we only require JDK 5. -kww
+    	instance.getValidDate("test", "June 32, 2008", DateFormat.getDateInstance(), false, errors);
+    	// assertEquals( 2, errors.size() );
+    }
+    
+    // FIXME: Should probably use SecurityConfigurationWrapper and force
+    //		  Validator.AcceptLenientDates to be false.
+    public void testLenientDate() {
+    	System.out.println("testLenientDate");
+    	boolean acceptLenientDates = ESAPI.securityConfiguration().getLenientDatesAccepted();
+    	if ( acceptLenientDates ) {
+    		assertTrue("Lenient date test skipped because Validator.AcceptLenientDates set to true", true);
+    		return;
+    	}
+
+    	Date lenientDateTest = null;
+    	try {
+    		// lenientDateTest will be null when Validator.AcceptLenientDates
+    		// is set to false (the default).
+    		Validator instance = ESAPI.validator();
+    		lenientDateTest = instance.getValidDate("datatest3-lenient", "15/2/2009 11:83:00",
+    				                                DateFormat.getDateInstance(DateFormat.SHORT, Locale.US),
+    				                                false);
+    		fail("Failed to throw expected ValidationException when Validator.AcceptLenientDates set to false.");
+    	} catch (ValidationException ve) {
+    		assertNull( lenientDateTest );
+    		Throwable cause = ve.getCause();
+    		assertTrue( cause.getClass().getName().equals("java.text.ParseException") );
+    	} catch (Exception e) {
+    		fail("Caught unexpected exception: " + e.getClass().getName() + "; msg: " + e);
+    	}
+    }
+
+    public void testGetValidDirectoryPath() throws Exception {
+        System.out.println("getValidDirectoryPath");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // find a directory that exists
+        File parent = new File("/");
+        String path = ESAPI.securityConfiguration().getResourceFile("ESAPI.properties").getParentFile().getCanonicalPath();
+        instance.getValidDirectoryPath("dirtest1", path, parent, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidDirectoryPath("dirtest2", null, parent, false, errors);
+        assertEquals(1, errors.size());
+        instance.getValidDirectoryPath("dirtest3", "ridicul%00ous", parent, false, errors);
+        assertEquals(2, errors.size());
+    }
+
+    public void testGetValidDouble() {
+        System.out.println("getValidDouble");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        instance.getValidDouble("dtest1", "1.0", 0, 20, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidDouble("dtest2", null, 0, 20, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidDouble("dtest3", null, 0, 20, false, errors);
+        assertEquals(1, errors.size());
+        instance.getValidDouble("dtest4", "ridiculous", 0, 20, true, errors);
+        assertEquals(2, errors.size());
+        instance.getValidDouble("dtest5", "" + (Double.MAX_VALUE), 0, 20, true, errors);
+        assertEquals(3, errors.size());
+        instance.getValidDouble("dtest6", "" + (Double.MAX_VALUE + .00001), 0, 20, true, errors);
+        assertEquals(4, errors.size());
+    }
+
+    public void testGetValidFileContent() {
+        System.out.println("getValidFileContent");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        byte[] bytes = null;
+        try {
+            bytes = "12345".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!");
+        }
+        instance.getValidFileContent("test", bytes, 5, true, errors);
+        assertEquals(0, errors.size());
+        instance.getValidFileContent("test", bytes, 4, true, errors);
+        assertEquals(1, errors.size());
+    }
+
+    public void testGetValidFileName() throws Exception {
+        System.out.println("getValidFileName");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        String testName = "aspe%20ct.jar";
+        assertEquals("Percent encoding is not changed", testName, instance.getValidFileName("test", testName, ESAPI.securityConfiguration().getAllowedFileExtensions(), false, errors));
+    }
+
+    public void testGetValidInput() {
+        System.out.println("getValidInput");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidInput(String, String, String, int, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidInteger() {
+        System.out.println("getValidInteger");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidInteger(String, String, int, int, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidListItem() {
+        System.out.println("getValidListItem");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidListItem(String, String, List, ValidationErrorList)
+    }
+
+    public void testGetValidNumber() {
+        System.out.println("getValidNumber");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidNumber(String, String, long, long, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidRedirectLocation() {
+        System.out.println("getValidRedirectLocation");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        // instance.getValidRedirectLocation(String, String, boolean, ValidationErrorList)
+    }
+
+    public void testGetValidSafeHTML() throws Exception {
+        System.out.println("getValidSafeHTML");
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+
+        // new school test case setup
+        HTMLValidationRule rule = new HTMLValidationRule("test");
+        ESAPI.validator().addRule(rule);
+
+        assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. <script>alert(document.cookie)</script>"));
+
+        String test1 = "<b>Jeff</b>";
+        String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors);
+        assertEquals(test1, result1);
+
+        String test2 = "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>";
+        String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors);
+        assertEquals(test2, result2);
+
+        String test3 = "Test. <script>alert(document.cookie)</script>";
+        assertEquals("Test.", rule.getSafe("test", test3));
+
+        assertEquals("Test. <<div>load=alert()</div>", rule.getSafe("test", "Test. <<div on<script></script>load=alert()"));
+        assertEquals("Test. <div>b</div>", rule.getSafe("test", "Test. <div style={xss:expression(xss)}>b</div>"));
+        assertEquals("Test.", rule.getSafe("test", "Test. <s%00cript>alert(document.cookie)</script>"));
+        assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. <s\tcript>alert(document.cookie)</script>"));
+        assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. <s\tcript>alert(document.cookie)</script>"));
+        // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts
+        // This would be nice to catch, but just looks like text to AntiSamy
+        // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" "));
+        // String result4 = instance.getValidSafeHTML("test", test4);
+        // assertEquals("", result4);
+    }
+
+    public void testIsInvalidFilename() {
+        System.out.println("testIsInvalidFilename");
+        Validator instance = ESAPI.validator();
+        char invalidChars[] = "/\\:*?\"<>|".toCharArray();
+        for (int i = 0; i < invalidChars.length; i++) {
+            assertFalse(invalidChars[i] + " is an invalid character for a filename",
+                    instance.isValidFileName("test", "as" + invalidChars[i] + "pect.jar", false));
+        }
+        assertFalse("Files must have an extension", instance.isValidFileName("test", "", false));
+        assertFalse("Files must have a valid extension", instance.isValidFileName("test.invalidExtension", "", false));
+        assertFalse("Filennames cannot be the empty string", instance.isValidFileName("test", "", false));
+    }
+
+    public void testIsValidDate() {
+        System.out.println("isValidDate");
+        Validator instance = ESAPI.validator();
+        DateFormat format = SimpleDateFormat.getDateInstance();
+        assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true));
+        assertFalse(instance.isValidDate("datetest2", null, format, false));
+        assertFalse(instance.isValidDate("datetest3", "", format, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidDate("datetest2", null, format, false, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidDate("datetest3", "", format, false, errors));
+        assertTrue(errors.size()==2);
+
+    }
+
+    public void testIsValidDirectoryPath() throws IOException {
+        System.out.println("isValidDirectoryPath");
+
+        // get an encoder with a special list of codecs and make a validator out of it
+        List list = new ArrayList();
+        list.add("HTMLEntityCodec");
+        Encoder encoder = new DefaultEncoder(list);
+        Validator instance = new DefaultValidator(encoder);
+
+        boolean isWindows = (System.getProperty("os.name").indexOf("Windows") != -1) ? true : false;
+        File parent = new File("/");
+
+        ValidationErrorList errors = new ValidationErrorList();
+
+        if (isWindows) {
+            String sysRoot = new File(System.getenv("SystemRoot")).getCanonicalPath();
+            // Windows paths that don't exist and thus should fail
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\jeff", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false));
+
+            // Windows paths
+            assertTrue(instance.isValidDirectoryPath("test", "C:\\", parent, false));                        // Windows root directory
+            assertTrue(instance.isValidDirectoryPath("test", sysRoot, parent, false));                  // Windows always exist directory
+            assertFalse(instance.isValidDirectoryPath("test", sysRoot + "\\System32\\cmd.exe", parent, false));      // Windows command shell
+
+            // Unix specific paths should not pass
+            assertFalse(instance.isValidDirectoryPath("test", "/tmp", parent, false));      // Unix Temporary directory
+            assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false));   // Unix Standard shell
+            assertFalse(instance.isValidDirectoryPath("test", "/etc/config", parent, false));
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false));
+
+            assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors));
+            assertTrue(errors.size()==1);
+            assertFalse(instance.isValidDirectoryPath("test2", "c:\\jeff", parent, false, errors));
+            assertTrue(errors.size()==2);
+            assertFalse(instance.isValidDirectoryPath("test3", "c:\\temp\\..\\etc", parent, false, errors));
+            assertTrue(errors.size()==3);
+
+            // Windows paths
+            assertTrue(instance.isValidDirectoryPath("test4", "C:\\", parent, false, errors));                        // Windows root directory
+            assertTrue(errors.size()==3);
+            assertTrue(instance.isValidDirectoryPath("test5", sysRoot, parent, false, errors));                  // Windows always exist directory
+            assertTrue(errors.size()==3);
+            assertFalse(instance.isValidDirectoryPath("test6", sysRoot + "\\System32\\cmd.exe", parent, false, errors));      // Windows command shell
+            assertTrue(errors.size()==4);
+
+            // Unix specific paths should not pass
+            assertFalse(instance.isValidDirectoryPath("test7", "/tmp", parent, false, errors));      // Unix Temporary directory
+            assertTrue(errors.size()==5);
+            assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors));   // Unix Standard shell
+            assertTrue(errors.size()==6);
+            assertFalse(instance.isValidDirectoryPath("test9", "/etc/config", parent, false, errors));
+            assertTrue(errors.size()==7);
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test10", "/etc/ridiculous", parent, false, errors));
+            assertTrue(errors.size()==8);
+            assertFalse(instance.isValidDirectoryPath("test11", "/tmp/../etc", parent, false, errors));
+            assertTrue(errors.size()==9);
+
+        } else {
+            // Windows paths should fail
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false));
+
+            // Standard Windows locations should fail
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\", parent, false));                        // Windows root directory
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\temp", parent, false));               // Windows temporary directory
+            assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\System32\\cmd.exe", parent, false));   // Windows command shell
+
+            // Unix specific paths should pass
+            assertTrue(instance.isValidDirectoryPath("test", "/", parent, false));         // Root directory
+            assertTrue(instance.isValidDirectoryPath("test", "/bin", parent, false));      // Always exist directory
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false));   // Standard shell, not dir
+            assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false));
+            assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false));
+
+            // Windows paths should fail
+            assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors));
+            assertTrue(errors.size()==1);
+            assertFalse(instance.isValidDirectoryPath("test2", "c:\\temp\\..\\etc", parent, false, errors));
+            assertTrue(errors.size()==2);
+
+            // Standard Windows locations should fail
+            assertFalse(instance.isValidDirectoryPath("test3", "c:\\", parent, false, errors));                        // Windows root directory
+            assertTrue(errors.size()==3);
+            assertFalse(instance.isValidDirectoryPath("test4", "c:\\Windows\\temp", parent, false, errors));               // Windows temporary directory
+            assertTrue(errors.size()==4);
+            assertFalse(instance.isValidDirectoryPath("test5", "c:\\Windows\\System32\\cmd.exe", parent, false, errors));   // Windows command shell
+            assertTrue(errors.size()==5);
+
+            // Unix specific paths should pass
+            assertTrue(instance.isValidDirectoryPath("test6", "/", parent, false, errors));         // Root directory
+            assertTrue(errors.size()==5);
+            assertTrue(instance.isValidDirectoryPath("test7", "/bin", parent, false, errors));      // Always exist directory
+            assertTrue(errors.size()==5);
+
+            // Unix specific paths that should not exist or work
+            assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors));   // Standard shell, not dir
+            assertTrue(errors.size()==6);
+            assertFalse(instance.isValidDirectoryPath("test9", "/etc/ridiculous", parent, false, errors));
+            assertTrue(errors.size()==7);
+            assertFalse(instance.isValidDirectoryPath("test10", "/tmp/../etc", parent, false, errors));
+            assertTrue(errors.size()==8);
+        }
+    }
+
+    public void TestIsValidDirectoryPath() {
+        // isValidDirectoryPath(String, String, boolean)
+    }
+
+    public void testIsValidDouble() {
+        // isValidDouble(String, String, double, double, boolean)
+    	Validator instance = ESAPI.validator();
+    	ValidationErrorList errors = new ValidationErrorList();
+    	//testing negative range
+        assertFalse(instance.isValidDouble("test1", "-4", 1, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        assertTrue(instance.isValidDouble("test2", "-4", -10, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        //testing null value
+        assertTrue(instance.isValidDouble("test3", null, -10, 10, true, errors));
+        assertTrue(errors.size() == 1);
+        assertFalse(instance.isValidDouble("test4", null, -10, 10, false, errors));
+        assertTrue(errors.size() == 2);
+        //testing empty string
+        assertTrue(instance.isValidDouble("test5", "", -10, 10, true, errors));
+        assertTrue(errors.size() == 2);
+        assertFalse(instance.isValidDouble("test6", "", -10, 10, false, errors));
+        assertTrue(errors.size() == 3);
+        //testing improper range
+        assertFalse(instance.isValidDouble("test7", "50.0", 10, -10, false, errors));
+        assertTrue(errors.size() == 4);
+        //testing non-integers
+        assertTrue(instance.isValidDouble("test8", "4.3214", -10, 10, true, errors));
+        assertTrue(errors.size() == 4);
+        assertTrue(instance.isValidDouble("test9", "-1.65", -10, 10, true, errors));
+        assertTrue(errors.size() == 4);
+        //other testing
+        assertTrue(instance.isValidDouble("test10", "4", 1, 10, false, errors));
+        assertTrue(errors.size() == 4);
+        assertTrue(instance.isValidDouble("test11", "400", 1, 10000, false, errors));
+        assertTrue(errors.size() == 4);
+        assertTrue(instance.isValidDouble("test12", "400000000", 1, 400000000, false, errors));
+        assertTrue(errors.size() == 4);
+        assertFalse(instance.isValidDouble("test13", "4000000000000", 1, 10000, false, errors));
+        assertTrue(errors.size() == 5);
+        assertFalse(instance.isValidDouble("test14", "alsdkf", 10, 10000, false, errors));
+        assertTrue(errors.size() == 6);
+        assertFalse(instance.isValidDouble("test15", "--10", 10, 10000, false, errors));
+        assertTrue(errors.size() == 7);
+        assertFalse(instance.isValidDouble("test16", "14.1414234x", 10, 10000, false, errors));
+        assertTrue(errors.size() == 8);
+        assertFalse(instance.isValidDouble("test17", "Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 9);
+        assertFalse(instance.isValidDouble("test18", "-Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 10);
+        assertFalse(instance.isValidDouble("test19", "NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 11);
+        assertFalse(instance.isValidDouble("test20", "-NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 12);
+        assertFalse(instance.isValidDouble("test21", "+NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 13);
+        assertTrue(instance.isValidDouble("test22", "1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 13);
+        assertTrue(instance.isValidDouble("test23", "-1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 13);
+    }
+
+    public void testIsValidFileContent() {
+        System.out.println("isValidFileContent");
+        byte[] content = null;
+        try {
+            content = "This is some file content".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidFileContent("test", content, 100, false));
+    }
+
+    public void testIsValidFileName() {
+        System.out.println("isValidFileName");
+        Validator instance = ESAPI.validator();
+        assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false));
+        assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false));
+        assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false, errors));
+        assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false, errors));
+        assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false, errors));
+        assertTrue(errors.size() == 0);
+    }
+
+    public void testIsValidFileUpload() throws IOException {
+        System.out.println("isValidFileUpload");
+        String filepath = new File(System.getProperty("user.dir")).getCanonicalPath();
+        String filename = "aspect.jar";
+        File parent = new File("/").getCanonicalFile();
+        ValidationErrorList errors = new ValidationErrorList();
+        byte[] content = null;
+        try {
+            content = "This is some file content".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false));
+        assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors));
+        assertTrue(errors.size() == 0);
+
+        filepath = "/ridiculous";
+        filename = "aspect.jar";
+        try {
+            content = "This is some file content".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false));
+        assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors));
+        assertTrue(errors.size() == 1);
+    }
+
+    public void testIsValidHTTPRequestParameterSet() {
+        //		isValidHTTPRequestParameterSet(String, Set, Set)
+    }
+
+    public void testisValidInput() {
+        System.out.println("isValidInput");
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidInput("test", "jeff.williams at aspectsecurity.com", "Email", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff.williams@@aspectsecurity.com", "Email", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff.williams at aspectsecurity", "Email", 100, false));
+        assertTrue(instance.isValidInput("test", "jeff.wil'liams at aspectsecurity.com", "Email", 100, false));
+        assertTrue(instance.isValidInput("test", "jeff.wil''liams at aspectsecurity.com", "Email", 100, false));
+        assertTrue(instance.isValidInput("test", "123.168.100.234", "IPAddress", 100, false));
+        assertTrue(instance.isValidInput("test", "192.168.1.234", "IPAddress", 100, false));
+        assertFalse(instance.isValidInput("test", "..168.1.234", "IPAddress", 100, false));
+        assertFalse(instance.isValidInput("test", "10.x.1.234", "IPAddress", 100, false));
+        assertTrue(instance.isValidInput("test", "http://www.aspectsecurity.com", "URL", 100, false));
+        assertFalse(instance.isValidInput("test", "http:///www.aspectsecurity.com", "URL", 100, false));
+        assertFalse(instance.isValidInput("test", "http://www.aspect security.com", "URL", 100, false));
+        assertTrue(instance.isValidInput("test", "078-05-1120", "SSN", 100, false));
+        assertTrue(instance.isValidInput("test", "078 05 1120", "SSN", 100, false));
+        assertTrue(instance.isValidInput("test", "078051120", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "987-65-4320", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "000-00-0000", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "(555) 555-5555", "SSN", 100, false));
+        assertFalse(instance.isValidInput("test", "test", "SSN", 100, false));
+        assertTrue(instance.isValidInput("test", "jeffWILLIAMS123", "HTTPParameterValue", 100, false));
+        assertTrue(instance.isValidInput("test", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false));
+        // Removed per Issue 116 - The '*' character is valid as a parameter character
+//        assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff^WILLIAMS", "HTTPParameterValue", 100, false));
+        assertFalse(instance.isValidInput("test", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false));
+
+        assertTrue(instance.isValidInput("test", null, "Email", 100, true));
+        assertFalse(instance.isValidInput("test", null, "Email", 100, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+
+        assertTrue(instance.isValidInput("test1", "jeff.williams at aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidInput("test2", "jeff.williams@@aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidInput("test3", "jeff.williams at aspectsecurity", "Email", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test4", "jeff.wil'liams at aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test5", "jeff.wil''liams at aspectsecurity.com", "Email", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test6", "123.168.100.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertTrue(instance.isValidInput("test7", "192.168.1.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==2);
+        assertFalse(instance.isValidInput("test8", "..168.1.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==3);
+        assertFalse(instance.isValidInput("test9", "10.x.1.234", "IPAddress", 100, false, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidInput("test10", "http://www.aspectsecurity.com", "URL", 100, false, errors));
+        assertTrue(errors.size()==4);
+        assertFalse(instance.isValidInput("test11", "http:///www.aspectsecurity.com", "URL", 100, false, errors));
+        assertTrue(errors.size()==5);
+        assertFalse(instance.isValidInput("test12", "http://www.aspect security.com", "URL", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertTrue(instance.isValidInput("test13", "078-05-1120", "SSN", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertTrue(instance.isValidInput("test14", "078 05 1120", "SSN", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertTrue(instance.isValidInput("test15", "078051120", "SSN", 100, false, errors));
+        assertTrue(errors.size()==6);
+        assertFalse(instance.isValidInput("test16", "987-65-4320", "SSN", 100, false, errors));
+        assertTrue(errors.size()==7);
+        assertFalse(instance.isValidInput("test17", "000-00-0000", "SSN", 100, false, errors));
+        assertTrue(errors.size()==8);
+        assertFalse(instance.isValidInput("test18", "(555) 555-5555", "SSN", 100, false, errors));
+        assertTrue(errors.size()==9);
+        assertFalse(instance.isValidInput("test19", "test", "SSN", 100, false, errors));
+        assertTrue(errors.size()==10);
+        assertTrue(instance.isValidInput("test20", "jeffWILLIAMS123", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==10);
+        assertTrue(instance.isValidInput("test21", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==10);
+        // Removed per Issue 116 - The '*' character is valid as a parameter character
+//        assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false));
+        assertFalse(instance.isValidInput("test22", "jeff^WILLIAMS", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==11);
+        assertFalse(instance.isValidInput("test23", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false, errors));
+        assertTrue(errors.size()==12);
+
+        assertTrue(instance.isValidInput("test", null, "Email", 100, true, errors));
+        assertFalse(instance.isValidInput("test", null, "Email", 100, false, errors));
+    }
+
+    public void testIsValidInteger() {
+        System.out.println("isValidInteger");
+        Validator instance = ESAPI.validator();
+        //testing negative range
+        assertFalse(instance.isValidInteger("test", "-4", 1, 10, false));
+        assertTrue(instance.isValidInteger("test", "-4", -10, 10, false));
+        //testing null value
+        assertTrue(instance.isValidInteger("test", null, -10, 10, true));
+        assertFalse(instance.isValidInteger("test", null, -10, 10, false));
+        //testing empty string
+        assertTrue(instance.isValidInteger("test", "", -10, 10, true));
+        assertFalse(instance.isValidInteger("test", "", -10, 10, false));
+        //testing improper range
+        assertFalse(instance.isValidInteger("test", "50", 10, -10, false));
+        //testing non-integers
+        assertFalse(instance.isValidInteger("test", "4.3214", -10, 10, true));
+        assertFalse(instance.isValidInteger("test", "-1.65", -10, 10, true));
+        //other testing
+        assertTrue(instance.isValidInteger("test", "4", 1, 10, false));
+        assertTrue(instance.isValidInteger("test", "400", 1, 10000, false));
+        assertTrue(instance.isValidInteger("test", "400000000", 1, 400000000, false));
+        assertFalse(instance.isValidInteger("test", "4000000000000", 1, 10000, false));
+        assertFalse(instance.isValidInteger("test", "alsdkf", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "--10", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "14.1414234x", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "Infinity", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "-Infinity", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "NaN", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "-NaN", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "+NaN", 10, 10000, false));
+        assertFalse(instance.isValidInteger("test", "1e-6", -999999999, 999999999, false));
+        assertFalse(instance.isValidInteger("test", "-1e-6", -999999999, 999999999, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        //testing negative range
+        assertFalse(instance.isValidInteger("test1", "-4", 1, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        assertTrue(instance.isValidInteger("test2", "-4", -10, 10, false, errors));
+        assertTrue(errors.size() == 1);
+        //testing null value
+        assertTrue(instance.isValidInteger("test3", null, -10, 10, true, errors));
+        assertTrue(errors.size() == 1);
+        assertFalse(instance.isValidInteger("test4", null, -10, 10, false, errors));
+        assertTrue(errors.size() == 2);
+        //testing empty string
+        assertTrue(instance.isValidInteger("test5", "", -10, 10, true, errors));
+        assertTrue(errors.size() == 2);
+        assertFalse(instance.isValidInteger("test6", "", -10, 10, false, errors));
+        assertTrue(errors.size() == 3);
+        //testing improper range
+        assertFalse(instance.isValidInteger("test7", "50", 10, -10, false, errors));
+        assertTrue(errors.size() == 4);
+        //testing non-integers
+        assertFalse(instance.isValidInteger("test8", "4.3214", -10, 10, true, errors));
+        assertTrue(errors.size() == 5);
+        assertFalse(instance.isValidInteger("test9", "-1.65", -10, 10, true, errors));
+        assertTrue(errors.size() == 6);
+        //other testing
+        assertTrue(instance.isValidInteger("test10", "4", 1, 10, false, errors));
+        assertTrue(errors.size() == 6);
+        assertTrue(instance.isValidInteger("test11", "400", 1, 10000, false, errors));
+        assertTrue(errors.size() == 6);
+        assertTrue(instance.isValidInteger("test12", "400000000", 1, 400000000, false, errors));
+        assertTrue(errors.size() == 6);
+        assertFalse(instance.isValidInteger("test13", "4000000000000", 1, 10000, false, errors));
+        assertTrue(errors.size() == 7);
+        assertFalse(instance.isValidInteger("test14", "alsdkf", 10, 10000, false, errors));
+        assertTrue(errors.size() == 8);
+        assertFalse(instance.isValidInteger("test15", "--10", 10, 10000, false, errors));
+        assertTrue(errors.size() == 9);
+        assertFalse(instance.isValidInteger("test16", "14.1414234x", 10, 10000, false, errors));
+        assertTrue(errors.size() == 10);
+        assertFalse(instance.isValidInteger("test17", "Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 11);
+        assertFalse(instance.isValidInteger("test18", "-Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size() == 12);
+        assertFalse(instance.isValidInteger("test19", "NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 13);
+        assertFalse(instance.isValidInteger("test20", "-NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 14);
+        assertFalse(instance.isValidInteger("test21", "+NaN", 10, 10000, false, errors));
+        assertTrue(errors.size() == 15);
+        assertFalse(instance.isValidInteger("test22", "1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 16);
+        assertFalse(instance.isValidInteger("test23", "-1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size() == 17);
+
+    }
+
+    public void testIsValidListItem() {
+        System.out.println("isValidListItem");
+        Validator instance = ESAPI.validator();
+        List list = new ArrayList();
+        list.add("one");
+        list.add("two");
+        assertTrue(instance.isValidListItem("test", "one", list));
+        assertFalse(instance.isValidListItem("test", "three", list));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidListItem("test1", "one", list, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidListItem("test2", "three", list, errors));
+        assertTrue(errors.size()==1);
+    }
+
+    public void testIsValidNumber() {
+        System.out.println("isValidNumber");
+        Validator instance = ESAPI.validator();
+        //testing negative range
+        assertFalse(instance.isValidNumber("test", "-4", 1, 10, false));
+        assertTrue(instance.isValidNumber("test", "-4", -10, 10, false));
+        //testing null value
+        assertTrue(instance.isValidNumber("test", null, -10, 10, true));
+        assertFalse(instance.isValidNumber("test", null, -10, 10, false));
+        //testing empty string
+        assertTrue(instance.isValidNumber("test", "", -10, 10, true));
+        assertFalse(instance.isValidNumber("test", "", -10, 10, false));
+        //testing improper range
+        assertFalse(instance.isValidNumber("test", "5", 10, -10, false));
+        //testing non-integers
+        assertTrue(instance.isValidNumber("test", "4.3214", -10, 10, true));
+        assertTrue(instance.isValidNumber("test", "-1.65", -10, 10, true));
+        //other testing
+        assertTrue(instance.isValidNumber("test", "4", 1, 10, false));
+        assertTrue(instance.isValidNumber("test", "400", 1, 10000, false));
+        assertTrue(instance.isValidNumber("test", "400000000", 1, 400000000, false));
+        assertFalse(instance.isValidNumber("test", "4000000000000", 1, 10000, false));
+        assertFalse(instance.isValidNumber("test", "alsdkf", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "--10", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "14.1414234x", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "Infinity", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "-Infinity", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "NaN", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "-NaN", 10, 10000, false));
+        assertFalse(instance.isValidNumber("test", "+NaN", 10, 10000, false));
+        assertTrue(instance.isValidNumber("test", "1e-6", -999999999, 999999999, false));
+        assertTrue(instance.isValidNumber("test", "-1e-6", -999999999, 999999999, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+      //testing negative range
+        assertFalse(instance.isValidNumber("test1", "-4", 1, 10, false, errors));
+        assertTrue(errors.size()==1);
+        assertTrue(instance.isValidNumber("test2", "-4", -10, 10, false, errors));
+        assertTrue(errors.size()==1);
+        //testing null value
+        assertTrue(instance.isValidNumber("test3", null, -10, 10, true, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidNumber("test4", null, -10, 10, false, errors));
+        assertTrue(errors.size()==2);
+        //testing empty string
+        assertTrue(instance.isValidNumber("test5", "", -10, 10, true, errors));
+        assertTrue(errors.size()==2);
+        assertFalse(instance.isValidNumber("test6", "", -10, 10, false, errors));
+        assertTrue(errors.size()==3);
+        //testing improper range
+        assertFalse(instance.isValidNumber("test7", "5", 10, -10, false, errors));
+        assertTrue(errors.size()==4);
+        //testing non-integers
+        assertTrue(instance.isValidNumber("test8", "4.3214", -10, 10, true, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidNumber("test9", "-1.65", -10, 10, true, errors));
+        assertTrue(errors.size()==4);
+        //other testing
+        assertTrue(instance.isValidNumber("test10", "4", 1, 10, false, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidNumber("test11", "400", 1, 10000, false, errors));
+        assertTrue(errors.size()==4);
+        assertTrue(instance.isValidNumber("test12", "400000000", 1, 400000000, false, errors));
+        assertTrue(errors.size()==4);
+        assertFalse(instance.isValidNumber("test13", "4000000000000", 1, 10000, false, errors));
+        assertTrue(errors.size()==5);
+        assertFalse(instance.isValidNumber("test14", "alsdkf", 10, 10000, false, errors));
+        assertTrue(errors.size()==6);
+        assertFalse(instance.isValidNumber("test15", "--10", 10, 10000, false, errors));
+        assertTrue(errors.size()==7);
+        assertFalse(instance.isValidNumber("test16", "14.1414234x", 10, 10000, false, errors));
+        assertTrue(errors.size()==8);
+        assertFalse(instance.isValidNumber("test17", "Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size()==9);
+        assertFalse(instance.isValidNumber("test18", "-Infinity", 10, 10000, false, errors));
+        assertTrue(errors.size()==10);
+        assertFalse(instance.isValidNumber("test19", "NaN", 10, 10000, false, errors));
+        assertTrue(errors.size()==11);
+        assertFalse(instance.isValidNumber("test20", "-NaN", 10, 10000, false, errors));
+        assertTrue(errors.size()==12);
+        assertFalse(instance.isValidNumber("test21", "+NaN", 10, 10000, false, errors));
+        assertTrue(errors.size()==13);
+        assertTrue(instance.isValidNumber("test22", "1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size()==13);
+        assertTrue(instance.isValidNumber("test23", "-1e-6", -999999999, 999999999, false, errors));
+        assertTrue(errors.size()==13);
+    }
+
+    public void testIsValidParameterSet() {
+        System.out.println("isValidParameterSet");
+        Set requiredNames = new HashSet();
+        requiredNames.add("p1");
+        requiredNames.add("p2");
+        requiredNames.add("p3");
+        Set optionalNames = new HashSet();
+        optionalNames.add("p4");
+        optionalNames.add("p5");
+        optionalNames.add("p6");
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        request.addParameter("p1", "value");
+        request.addParameter("p2", "value");
+        request.addParameter("p3", "value");
+        ESAPI.httpUtilities().setCurrentHTTP(request, response);
+        Validator instance = ESAPI.validator();
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames));
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames,errors));
+        assertTrue(errors.size()==0);
+        request.addParameter("p4", "value");
+        request.addParameter("p5", "value");
+        request.addParameter("p6", "value");
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames));
+        assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors));
+        assertTrue(errors.size()==0);
+        request.removeParameter("p1");
+        assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames));
+        assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors));
+        assertTrue(errors.size() ==1);
+    }
+
+    public void testIsValidPrintable() {
+        System.out.println("isValidPrintable");
+        Validator instance = ESAPI.validator();
+        assertTrue(instance.isValidPrintable("name", "abcDEF", 100, false));
+        assertTrue(instance.isValidPrintable("name", "!@#R()*$;><()", 100, false));
+        char[] chars = {0x60, (char) 0xFF, 0x10, 0x25};
+        assertFalse(instance.isValidPrintable("name", chars, 100, false));
+        assertFalse(instance.isValidPrintable("name", "%08", 100, false));
+
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidPrintable("name1", "abcDEF", 100, false, errors));
+        assertTrue(errors.size()==0);
+        assertTrue(instance.isValidPrintable("name2", "!@#R()*$;><()", 100, false, errors));
+        assertTrue(errors.size()==0);
+        assertFalse(instance.isValidPrintable("name3", chars, 100, false, errors));
+        assertTrue(errors.size()==1);
+        assertFalse(instance.isValidPrintable("name4", "%08", 100, false, errors));
+        assertTrue(errors.size()==2);
+
+    }
+
+    public void testIsValidRedirectLocation() {
+        //		isValidRedirectLocation(String, String, boolean)
+    }
+
+    public void testIsValidSafeHTML() {
+        System.out.println("isValidSafeHTML");
+        Validator instance = ESAPI.validator();
+
+        assertTrue(instance.isValidSafeHTML("test", "<b>Jeff</b>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <script>alert(document.cookie)</script>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <div style={xss:expression(xss)}>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <s%00cript>alert(document.cookie)</script>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <s\tcript>alert(document.cookie)</script>", 100, false));
+        assertTrue(instance.isValidSafeHTML("test", "Test. <s\r\n\0cript>alert(document.cookie)</script>", 100, false));
+
+        // TODO: waiting for a way to validate text headed for an attribute for scripts
+        // This would be nice to catch, but just looks like text to AntiSamy
+        // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" "));
+        ValidationErrorList errors = new ValidationErrorList();
+        assertTrue(instance.isValidSafeHTML("test1", "<b>Jeff</b>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test2", "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test3", "Test. <script>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test4", "Test. <div style={xss:expression(xss)}>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test5", "Test. <s%00cript>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test6", "Test. <s\tcript>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(instance.isValidSafeHTML("test7", "Test. <s\r\n\0cript>alert(document.cookie)</script>", 100, false, errors));
+        assertTrue(errors.size() == 0);
+
+    }
+
+    public void testSafeReadLine() {
+        System.out.println("safeReadLine");
+
+        byte[] bytes = null;
+        try {
+            bytes = "testString".getBytes(PREFERRED_ENCODING);
+        }
+        catch (UnsupportedEncodingException e1) {
+            fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!");
+        }
+        ByteArrayInputStream s = new ByteArrayInputStream(bytes);
+        Validator instance = ESAPI.validator();
+        try {
+            instance.safeReadLine(s, -1);
+            fail();
+        }
+        catch (ValidationException e) {
+            // Expected
+        }
+        s.reset();
+        try {
+            instance.safeReadLine(s, 4);
+            fail();
+        }
+        catch (ValidationException e) {
+            // Expected
+        }
+        s.reset();
+        try {
+            String u = instance.safeReadLine(s, 20);
+            assertEquals("testString", u);
+        }
+        catch (ValidationException e) {
+            fail();
+        }
+
+        // This sub-test attempts to validate that BufferedReader.readLine() and safeReadLine() are similar in operation
+        // for the nominal case
+        try {
+            s.reset();
+            InputStreamReader isr = new InputStreamReader(s);
+            BufferedReader br = new BufferedReader(isr);
+            String u = br.readLine();
+            s.reset();
+            String v = instance.safeReadLine(s, 20);
+            assertEquals(u, v);
+        }
+        catch (IOException e) {
+            fail();
+        }
+        catch (ValidationException e) {
+            fail();
+        }
+    }
+
+    public void testIssue82_SafeString_Bad_Regex() {
+        Validator instance = ESAPI.validator();
+        try {
+            instance.getValidInput("address", "55 main st. pasadena ak", "SafeString", 512, false);
+        }
+        catch (ValidationException e) {
+            fail(e.getLogMessage());
+        }
+    }
+
+    public void testGetParameterMap() {
+//testing Validator.HTTPParameterName and Validator.HTTPParameterValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+//an example of a parameter from displaytag, should pass
+        request.addParameter("d-49653-p", "pass");
+        request.addParameter("<img ", "fail");
+        request.addParameter(generateStringOfLength(32), "pass");
+        request.addParameter(generateStringOfLength(33), "fail");
+        assertEquals(safeRequest.getParameterMap().size(), 2);
+        assertNull(safeRequest.getParameterMap().get("<img"));
+        assertNull(safeRequest.getParameterMap().get(generateStringOfLength(33)));
+    }
+
+    public void testGetParameterNames() {
+//testing Validator.HTTPParameterName
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+//an example of a parameter from displaytag, should pass
+        request.addParameter("d-49653-p", "pass");
+        request.addParameter("<img ", "fail");
+        request.addParameter(generateStringOfLength(32), "pass");
+        request.addParameter(generateStringOfLength(33), "fail");
+        assertEquals(Collections.list(safeRequest.getParameterNames()).size(), 2);
+    }
+
+    public void testGetParameter() {
+//testing Validator.HTTPParameterValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.addParameter("p1", "Alice");
+        request.addParameter("p2", "bob at alice.com");//mail-address from a submit-form
+        request.addParameter("p3", ESAPI.authenticator().generateStrongPassword());
+        request.addParameter("p4", new String(EncoderConstants.CHAR_PASSWORD_SPECIALS));
+        //TODO - I think this should fair request.addParameter("p5", "�������?����"); //some special characters from european languages;
+        request.addParameter("f1", "<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>");
+        request.addParameter("f2", "<IMG SRC=javascript:alert('XSS')>");
+        request.addParameter("f3", "<IMG SRC=javascript:alert('XSS')>");
+        for (int i = 1; i <= 4; i++) {
+            assertTrue(safeRequest.getParameter("p" + i).equals(request.getParameter("p" + i)));
+        }
+        for (int i = 1; i <= 2; i++) {
+        	boolean testResult = false;
+        	try {
+        		testResult = safeRequest.getParameter("f" + i).equals(request.getParameter("f" + i));
+        	} catch (NullPointerException npe) {
+        		//the test is this block SHOULD fail. a NPE is an acceptable failure state
+        		testResult = false; //redundant, just being descriptive here
+        	}
+        	assertFalse(testResult);
+        }
+        assertNull(safeRequest.getParameter("e1"));
+
+        //This is revealing problems with Jeff's original SafeRequest
+        //mishandling of the AllowNull parameter. I'm adding a new Google code
+        //bug to track this.
+        //
+        //assertNotNull(safeRequest.getParameter("e1", false));
+    }
+
+    public void testGetCookies() {
+//testing Validator.HTTPCookieName and Validator.HTTPCookieValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+//should support a base64-encode value
+        request.setCookie("p1", "34=VJhjv7jiDu7tsdLrQQ2KcUwpfWUM2_mBae6UA8ttk4wBHdxxQ-1IBxyCOn3LWE08SDhpnBcJ7N5Vze48F2t8a1R_hXt7PX1BvgTM0pn-T4JkqGTm_tlmV4RmU3GT-dgn");
+        request.setCookie("f1", "<A HREF=\"http://66.102.7.147/\">XSS</A>");
+        request.setCookie("load-balancing", "pass");
+        request.setCookie("'bypass", "fail");
+        Cookie[] cookies = safeRequest.getCookies();
+        assertEquals(cookies[0].getValue(), request.getCookies()[0].getValue());
+        assertEquals(cookies[1].getName(), request.getCookies()[2].getName());
+        assertTrue(cookies.length == 2);
+    }
+
+    public void testGetHeader() {
+//testing Validator.HTTPHeaderValue
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.addHeader("p1", "login");
+        request.addHeader("f1", "<A HREF=\"http://0x42.0x0000066.0x7.0x93/\">XSS</A>");
+        request.addHeader("p2", generateStringOfLength(150));
+        request.addHeader("f2", generateStringOfLength(151));
+        assertEquals(safeRequest.getHeader("p1"), request.getHeader("p1"));
+        assertEquals(safeRequest.getHeader("p2"), request.getHeader("p2"));
+        assertFalse(safeRequest.getHeader("f1").equals(request.getHeader("f1")));
+        assertFalse(safeRequest.getHeader("f2").equals(request.getHeader("f2")));
+        assertNull(safeRequest.getHeader("p3"));
+    }
+
+    public void testGetHeaderNames() {
+//testing Validator.HTTPHeaderName
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.addHeader("d-49653-p", "pass");
+        request.addHeader("<img ", "fail");
+        request.addHeader(generateStringOfLength(32), "pass");
+        request.addHeader(generateStringOfLength(33), "fail");
+        assertEquals(Collections.list(safeRequest.getHeaderNames()).size(), 2);
+    }
+
+    public void testGetQueryString() {
+//testing Validator.HTTPQueryString
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        request.setQueryString("mail=bob at alice.com&passwd=" + new String(EncoderConstants.CHAR_PASSWORD_SPECIALS));// TODO, fix this + "&special=�����");
+        assertEquals(safeRequest.getQueryString(), request.getQueryString());
+        request.setQueryString("mail=<IMG SRC=\"jav\tascript:alert('XSS');\">");
+        assertFalse(safeRequest.getQueryString().equals(request.getQueryString()));
+        request.setQueryString("mail=bob at alice.com-passwd=johny");
+        assertTrue(safeRequest.getQueryString().equals(request.getQueryString()));
+        request.setQueryString("mail=bob at alice.com-passwd=johny&special"); //= is missing!
+        assertFalse(safeRequest.getQueryString().equals(request.getQueryString()));
+    }
+
+    public void testGetRequestURI() {
+//testing Validator.HTTPURI
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request);
+        try {
+            request.setRequestURI("/app/page.jsp");
+        } catch (UnsupportedEncodingException ignored) {
+        }
+        assertEquals(safeRequest.getRequestURI(), request.getRequestURI());
+    }
+
+    private String generateStringOfLength(int length) {
+        StringBuilder longString = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            longString.append("a");
+        }
+        return longString.toString();
+    }
+
+    public void testGetContextPath() {
+        // Root Context Path ("")
+        assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "", "HTTPContextPath", 512, true));
+        // Deployed Context Path ("/context")
+        assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "/context", "HTTPContextPath", 512, true));
+        // Fail-case - URL Splitting
+        assertFalse(ESAPI.validator().isValidInput("HTTPContextPath", "/\\nGET http://evil.com", "HTTPContextPath", 512, true));
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/all-wcprops b/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/all-wcprops
new file mode 100644
index 0000000..c574100
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/accesscontrol
+END
+AccessControllerTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/accesscontrol/AccessControllerTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/entries b/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/entries
new file mode 100644
index 0000000..5530de1
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/entries
@@ -0,0 +1,65 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/accesscontrol
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-11-05T04:21:56.553937Z
+1646
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+AccessControllerTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.469956Z
+5b38ac74786ac7efc5c1e83c410f135b
+2009-07-10T04:27:08.810558Z
+564
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5564
+

+policyloader
+dir
+

diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AccessControllerTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AccessControllerTest.java.svn-base
new file mode 100644
index 0000000..1f3cc89
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/.svn/text-base/AccessControllerTest.java.svn-base
@@ -0,0 +1,128 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR;
+import org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR;
+import org.owasp.esapi.reference.accesscontrol.ExperimentalAccessController;
+
+/**
+ * Answers the question: is the AccessController itself working properly?
+ * @author Mike H. Fauzy
+ *
+ */
+public class AccessControllerTest {
+	
+	protected AccessController accessController;
+		
+	@Before
+	public void setup() {
+		Map accessControlRules = new HashMap(3);
+		accessControlRules.put("AlwaysTrue", new AlwaysTrueACR());
+		accessControlRules.put("AlwaysFalse", new AlwaysFalseACR());		
+		accessControlRules.put("EchoRuntimeParameter", new EchoRuntimeParameterACR());
+		accessController = new ExperimentalAccessController(accessControlRules);
+	}
+	
+	@Test 
+	public void isAuthorized() {
+		assertEquals("Rule Not Found: null", accessController.isAuthorized(null, null), false);
+		assertEquals("Rule Not Found: Invalid Key", accessController.isAuthorized("A key that does not map to a rule", null), false);		
+
+		assertEquals("AlwaysTrue", accessController.isAuthorized("AlwaysTrue", null), true);
+		assertEquals("AlwaysFalse", accessController.isAuthorized("AlwaysFalse", null), false);
+		
+		assertEquals("EchoRuntimeParameter: True", accessController.isAuthorized("EchoRuntimeParameter", Boolean.TRUE), true );
+		assertEquals("EchoRuntimeParameter: False", accessController.isAuthorized("EchoRuntimeParameter", Boolean.FALSE), false);
+		assertEquals("EchoRuntimeParameter: ClassCastException", accessController.isAuthorized("EchoRuntimeParameter", "This is not a boolean"), false);
+		assertEquals("EchoRuntimeParameter: null Runtime Parameter", accessController.isAuthorized("EchoRuntimeParameter", null), false);
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationRuleNotFoundNullKey() throws Exception {		
+		accessController.assertAuthorized(null, null);
+	}
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationRuleAKeyThatDoesNotMapToARule() throws Exception {		
+		accessController.assertAuthorized("A key that does not map to a rule", null);
+	}
+	
+	
+	@Test  
+	//Should not throw an exception
+	public void enforceAuthorizationAlwaysTrue() throws Exception {		
+		accessController.assertAuthorized("AlwaysTrue", null);
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationAlwaysFalse() throws Exception {		
+		accessController.assertAuthorized("AlwaysFalse", null);
+	}
+	
+	/**
+	 * Ensure that isAuthorized does nothing if enforceAuthorization 
+	 * is called and isAuthorized returns true
+	 */
+	@Test 
+	//Should not throw an exception
+	public void enforceAuthorizationEchoRuntimeParameterTrue() throws Exception {
+		accessController.assertAuthorized("EchoRuntimeParameter", Boolean.TRUE);
+	}
+	
+	/**
+	 * Ensure that isAuthorized translates into an exception if enforceAuthorization 
+	 * is called and isAuthorized returns false
+	 */
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationEchoRuntimeParameterFalse() throws Exception {		
+		accessController.assertAuthorized("EchoRuntimeParameter", Boolean.FALSE);
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationEchoRuntimeParameterClassCastException() throws Exception {	
+		accessController.assertAuthorized("EchoRuntimeParameter", "This is not a boolean");
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationEchoRuntimeParameterNullRuntimeParameter() throws Exception {		
+		accessController.assertAuthorized("EchoRuntimeParameter", null);
+	}
+	
+	@org.junit.Test
+	public void delegatingACR() throws Exception {
+		DelegatingACR delegatingACR = new DelegatingACR();
+		DynaBeanACRParameter policyParameter = new DynaBeanACRParameter();
+
+		delegatingACR = new DelegatingACR();
+		policyParameter = new DynaBeanACRParameter();
+		policyParameter.set("delegateClass", "java.lang.Object");
+		policyParameter.set("delegateMethod", "equals");
+		policyParameter.set("parameterClasses", new String[] {"java.lang.Object"});
+		delegatingACR.setPolicyParameters(policyParameter);
+		org.junit.Assert.assertFalse(delegatingACR.isAuthorized(new Object[] {new Object()}));
+		org.junit.Assert.assertFalse(delegatingACR.isAuthorized(new Object[] {delegatingACR}));
+
+		
+		policyParameter.set("delegateClass", "org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR");
+		policyParameter.set("delegateMethod", "isAuthorized");
+		policyParameter.set("parameterClasses", new String[] {"java.lang.Object"});
+		delegatingACR.setPolicyParameters(policyParameter);
+		org.junit.Assert.assertTrue(delegatingACR.isAuthorized(new Object[] {null}));
+		
+		delegatingACR = new DelegatingACR();
+		policyParameter = new DynaBeanACRParameter();
+		policyParameter.set("delegateClass", "org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR");
+		policyParameter.set("delegateMethod", "isAuthorized");
+		policyParameter.set("parameterClasses", new String[] {"java.lang.Object"});
+		delegatingACR.setPolicyParameters(policyParameter);
+		org.junit.Assert.assertFalse(delegatingACR.isAuthorized(new Object[] {null}));
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/AccessControllerTest.java b/src/test/java/org/owasp/esapi/reference/accesscontrol/AccessControllerTest.java
new file mode 100644
index 0000000..1f3cc89
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/AccessControllerTest.java
@@ -0,0 +1,128 @@
+package org.owasp.esapi.reference.accesscontrol;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.errors.AccessControlException;
+import org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR;
+import org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR;
+import org.owasp.esapi.reference.accesscontrol.ExperimentalAccessController;
+
+/**
+ * Answers the question: is the AccessController itself working properly?
+ * @author Mike H. Fauzy
+ *
+ */
+public class AccessControllerTest {
+	
+	protected AccessController accessController;
+		
+	@Before
+	public void setup() {
+		Map accessControlRules = new HashMap(3);
+		accessControlRules.put("AlwaysTrue", new AlwaysTrueACR());
+		accessControlRules.put("AlwaysFalse", new AlwaysFalseACR());		
+		accessControlRules.put("EchoRuntimeParameter", new EchoRuntimeParameterACR());
+		accessController = new ExperimentalAccessController(accessControlRules);
+	}
+	
+	@Test 
+	public void isAuthorized() {
+		assertEquals("Rule Not Found: null", accessController.isAuthorized(null, null), false);
+		assertEquals("Rule Not Found: Invalid Key", accessController.isAuthorized("A key that does not map to a rule", null), false);		
+
+		assertEquals("AlwaysTrue", accessController.isAuthorized("AlwaysTrue", null), true);
+		assertEquals("AlwaysFalse", accessController.isAuthorized("AlwaysFalse", null), false);
+		
+		assertEquals("EchoRuntimeParameter: True", accessController.isAuthorized("EchoRuntimeParameter", Boolean.TRUE), true );
+		assertEquals("EchoRuntimeParameter: False", accessController.isAuthorized("EchoRuntimeParameter", Boolean.FALSE), false);
+		assertEquals("EchoRuntimeParameter: ClassCastException", accessController.isAuthorized("EchoRuntimeParameter", "This is not a boolean"), false);
+		assertEquals("EchoRuntimeParameter: null Runtime Parameter", accessController.isAuthorized("EchoRuntimeParameter", null), false);
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationRuleNotFoundNullKey() throws Exception {		
+		accessController.assertAuthorized(null, null);
+	}
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationRuleAKeyThatDoesNotMapToARule() throws Exception {		
+		accessController.assertAuthorized("A key that does not map to a rule", null);
+	}
+	
+	
+	@Test  
+	//Should not throw an exception
+	public void enforceAuthorizationAlwaysTrue() throws Exception {		
+		accessController.assertAuthorized("AlwaysTrue", null);
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationAlwaysFalse() throws Exception {		
+		accessController.assertAuthorized("AlwaysFalse", null);
+	}
+	
+	/**
+	 * Ensure that isAuthorized does nothing if enforceAuthorization 
+	 * is called and isAuthorized returns true
+	 */
+	@Test 
+	//Should not throw an exception
+	public void enforceAuthorizationEchoRuntimeParameterTrue() throws Exception {
+		accessController.assertAuthorized("EchoRuntimeParameter", Boolean.TRUE);
+	}
+	
+	/**
+	 * Ensure that isAuthorized translates into an exception if enforceAuthorization 
+	 * is called and isAuthorized returns false
+	 */
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationEchoRuntimeParameterFalse() throws Exception {		
+		accessController.assertAuthorized("EchoRuntimeParameter", Boolean.FALSE);
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationEchoRuntimeParameterClassCastException() throws Exception {	
+		accessController.assertAuthorized("EchoRuntimeParameter", "This is not a boolean");
+	}
+	
+	@Test (expected = AccessControlException.class)	 
+	public void enforceAuthorizationEchoRuntimeParameterNullRuntimeParameter() throws Exception {		
+		accessController.assertAuthorized("EchoRuntimeParameter", null);
+	}
+	
+	@org.junit.Test
+	public void delegatingACR() throws Exception {
+		DelegatingACR delegatingACR = new DelegatingACR();
+		DynaBeanACRParameter policyParameter = new DynaBeanACRParameter();
+
+		delegatingACR = new DelegatingACR();
+		policyParameter = new DynaBeanACRParameter();
+		policyParameter.set("delegateClass", "java.lang.Object");
+		policyParameter.set("delegateMethod", "equals");
+		policyParameter.set("parameterClasses", new String[] {"java.lang.Object"});
+		delegatingACR.setPolicyParameters(policyParameter);
+		org.junit.Assert.assertFalse(delegatingACR.isAuthorized(new Object[] {new Object()}));
+		org.junit.Assert.assertFalse(delegatingACR.isAuthorized(new Object[] {delegatingACR}));
+
+		
+		policyParameter.set("delegateClass", "org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR");
+		policyParameter.set("delegateMethod", "isAuthorized");
+		policyParameter.set("parameterClasses", new String[] {"java.lang.Object"});
+		delegatingACR.setPolicyParameters(policyParameter);
+		org.junit.Assert.assertTrue(delegatingACR.isAuthorized(new Object[] {null}));
+		
+		delegatingACR = new DelegatingACR();
+		policyParameter = new DynaBeanACRParameter();
+		policyParameter.set("delegateClass", "org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR");
+		policyParameter.set("delegateMethod", "isAuthorized");
+		policyParameter.set("parameterClasses", new String[] {"java.lang.Object"});
+		delegatingACR.setPolicyParameters(policyParameter);
+		org.junit.Assert.assertFalse(delegatingACR.isAuthorized(new Object[] {null}));
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/all-wcprops b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/all-wcprops
new file mode 100644
index 0000000..89dbfc3
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader
+END
+ACRPolicyFileLoaderTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 131
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoaderTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/entries b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/entries
new file mode 100644
index 0000000..6aaed5f
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-11-05T04:21:56.553937Z
+1646
+manico.james
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+ACRPolicyFileLoaderTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.469956Z
+4ed5ee5bc4fbabed70714daa5cb29708
+2009-11-13T16:38:16.637322Z
+817
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2576
+

diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRPolicyFileLoaderTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRPolicyFileLoaderTest.java.svn-base
new file mode 100644
index 0000000..1dcc86b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/.svn/text-base/ACRPolicyFileLoaderTest.java.svn-base
@@ -0,0 +1,70 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.AccessControlException;
+/**
+ * Answers the question: Is the policy file being loaded properly?
+ * @author Mike H. Fauzy
+ */
+public class ACRPolicyFileLoaderTest {
+
+	protected AccessController accessController;
+
+	@Before
+	public void setUp() throws Exception {
+		accessController = ESAPI.accessController();
+	}
+
+	@Test
+	public void testSetup() throws AccessControlException {
+		/**
+		 * This tests the policy file
+		 */
+		ACRPolicyFileLoader policyDescriptor = new ACRPolicyFileLoader();
+		PolicyDTO policyDTO = policyDescriptor.load();
+		Map accessControlRules = policyDTO.getAccessControlRules();
+		assertTrue("Some AccessControlRules are loaded", !accessControlRules
+				.isEmpty());
+		assertTrue("Access Control Map Contains AlwaysTrue", accessControlRules
+				.containsKey("AlwaysTrue"));
+		assertTrue("Access Control Map Contains AlwaysFalse",
+				accessControlRules.containsKey("AlwaysFalse"));
+		assertTrue("Access Control Map Contains EchoRuntimeParameter",
+				accessControlRules.containsKey("EchoRuntimeParameter"));
+		assertTrue("Access Control Map Contains EchoPolicyParameter",
+				accessControlRules.containsKey("EchoPolicyParameter"));
+	}
+
+	@Test
+	public void isAuthorizedEchoPolicyParameter() {
+		assertEquals("EchoPolicyParameter", accessController
+				.isAuthorized("EchoPolicyParameter", null), true);
+		assertEquals("EchoRuntimeParameterClassCastException", accessController
+				.isAuthorized("EchoRuntimeParameterClassCastException", null),
+				false);
+		// Policy parameter value null, empty or missing. (TODO add more fail
+		// state tests
+		// assertEquals("EchoRuntimeParameterValueNull",
+		// accessController.isAuthorized("EchoRuntimeParameterValueNull", null),
+		// false);
+		// assertEquals("EchoRuntimeParameterValueEmpty",
+		// accessController.isAuthorized("EchoRuntimeParameterValueEmpty",
+		// null), false);
+		// assertEquals("EchoRuntimeParameterValueMissing",
+		// accessController.isAuthorized("EchoRuntimeParameterValueMissing",
+		// null), false);
+	}
+	
+	@Test(expected = AccessControlException.class)
+	public void enforceAuthorizationRuleNotFoundNullKey() throws AccessControlException {
+		accessController.assertAuthorized(null, null);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoaderTest.java b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoaderTest.java
new file mode 100644
index 0000000..1dcc86b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/accesscontrol/policyloader/ACRPolicyFileLoaderTest.java
@@ -0,0 +1,70 @@
+package org.owasp.esapi.reference.accesscontrol.policyloader;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.owasp.esapi.AccessController;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.AccessControlException;
+/**
+ * Answers the question: Is the policy file being loaded properly?
+ * @author Mike H. Fauzy
+ */
+public class ACRPolicyFileLoaderTest {
+
+	protected AccessController accessController;
+
+	@Before
+	public void setUp() throws Exception {
+		accessController = ESAPI.accessController();
+	}
+
+	@Test
+	public void testSetup() throws AccessControlException {
+		/**
+		 * This tests the policy file
+		 */
+		ACRPolicyFileLoader policyDescriptor = new ACRPolicyFileLoader();
+		PolicyDTO policyDTO = policyDescriptor.load();
+		Map accessControlRules = policyDTO.getAccessControlRules();
+		assertTrue("Some AccessControlRules are loaded", !accessControlRules
+				.isEmpty());
+		assertTrue("Access Control Map Contains AlwaysTrue", accessControlRules
+				.containsKey("AlwaysTrue"));
+		assertTrue("Access Control Map Contains AlwaysFalse",
+				accessControlRules.containsKey("AlwaysFalse"));
+		assertTrue("Access Control Map Contains EchoRuntimeParameter",
+				accessControlRules.containsKey("EchoRuntimeParameter"));
+		assertTrue("Access Control Map Contains EchoPolicyParameter",
+				accessControlRules.containsKey("EchoPolicyParameter"));
+	}
+
+	@Test
+	public void isAuthorizedEchoPolicyParameter() {
+		assertEquals("EchoPolicyParameter", accessController
+				.isAuthorized("EchoPolicyParameter", null), true);
+		assertEquals("EchoRuntimeParameterClassCastException", accessController
+				.isAuthorized("EchoRuntimeParameterClassCastException", null),
+				false);
+		// Policy parameter value null, empty or missing. (TODO add more fail
+		// state tests
+		// assertEquals("EchoRuntimeParameterValueNull",
+		// accessController.isAuthorized("EchoRuntimeParameterValueNull", null),
+		// false);
+		// assertEquals("EchoRuntimeParameterValueEmpty",
+		// accessController.isAuthorized("EchoRuntimeParameterValueEmpty",
+		// null), false);
+		// assertEquals("EchoRuntimeParameterValueMissing",
+		// accessController.isAuthorized("EchoRuntimeParameterValueMissing",
+		// null), false);
+	}
+	
+	@Test(expected = AccessControlException.class)
+	public void enforceAuthorizationRuleNotFoundNullKey() throws AccessControlException {
+		accessController.assertAuthorized(null, null);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/all-wcprops b/src/test/java/org/owasp/esapi/reference/crypto/.svn/all-wcprops
new file mode 100644
index 0000000..550db8d
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/crypto
+END
+ReferenceEncryptedPropertiesTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedPropertiesTest.java
+END
+EncryptedPropertiesTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 111
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesTest.java
+END
+EncryptedPropertiesUtilsTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtilsTest.java
+END
+CryptoPolicy.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/crypto/CryptoPolicy.java
+END
+EncryptorTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/entries b/src/test/java/org/owasp/esapi/reference/crypto/.svn/entries
new file mode 100644
index 0000000..93244f9
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/entries
@@ -0,0 +1,198 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/crypto
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:37:45.146375Z
+1890
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+CryptoPolicy.java
+file
+
+
+
+
+2014-02-18T16:19:52.381955Z
+946d3e612e43e87f3dd6767675f9007d
+2013-08-31T22:37:45.146375Z
+1890
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4655
+

+EncryptorTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.381955Z
+e1176c41c2519bbb79873cb88012f725
+2013-08-31T22:37:45.146375Z
+1890
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+23471
+

+ReferenceEncryptedPropertiesTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.381955Z
+339edf6b4e01bb1a863e940781746402
+2010-10-15T17:36:21.822622Z
+1574
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+17161
+

+EncryptedPropertiesTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.381955Z
+cc724562a8010c31fc50460faf29d932
+2010-07-26T05:14:58.233988Z
+1456
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7102
+

+EncryptedPropertiesUtilsTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.381955Z
+c58615bd3dbd3497e588896cea4457c7
+2010-10-15T17:36:21.822622Z
+1574
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6560
+

diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/prop-base/CryptoPolicy.java.svn-base b/src/test/java/org/owasp/esapi/reference/crypto/.svn/prop-base/CryptoPolicy.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/prop-base/CryptoPolicy.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/CryptoPolicy.java.svn-base b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/CryptoPolicy.java.svn-base
new file mode 100644
index 0000000..fe3a8e9
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/CryptoPolicy.java.svn-base
@@ -0,0 +1,105 @@
+package org.owasp.esapi.reference.crypto;
+
+import java.security.*;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+/**
+ * Helper class to see if unlimited strength crypto is available. If it is
+ * not, then symmetric encryption algorithms are restricted to 128-bit
+ * key size or the encryption must provide key weakening or key escrow.
+ * <p>
+ * This program attempts to generate a 256-bit AES key and use it to do
+ * to a simple encryption. If the encryption succeeds, the assumption is
+ * that the JVM being used has the "unlimited" strength JCE jurisdiction
+ * policy files installed.
+ * </p><p>
+ * We use this for JUnit tests. If unlimited strength crypto is not available,
+ * we simply skip certain JUnit tests that would require it.
+ * </p><p>
+ * The reason for not adding this class to ESAPI proper is because its mostly
+ * pointless to find out at runtime that you don't have the unlimited strength
+ * JCE jurisdiction policy files installed. If you don't, you're SOL until you
+ * install them and even if you could do that from a running JVM, chances are
+ * slim to none that one could easily get your JCE provider to work with them.
+ * (Well, one <i>might</i> be able to unload the JCE classes, but you hopefully
+ * are not running your JVM process as 'root' or other privileged account
+ * anyway, so you probably can't install these policy files from your JVM in
+ * the first place.)
+ * </p>
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class CryptoPolicy {
+
+    private static boolean checked = false;
+    private static boolean unlimited = false;
+
+    /**
+     * Check to see if unlimited strength crypto is available.
+     * There is an implicit assumption that the JCE jurisdiction policy
+     * files are not going to be changing while this given JVM is running.
+     *
+     * @return True if we can provide keys longer than 128 bits.
+     */
+    public synchronized static boolean isUnlimitedStrengthCryptoAvailable()
+    {
+        if ( checked == false ) {
+            unlimited = checkCrypto();
+            checked = true;
+        }
+        return unlimited;
+    }
+
+    private static boolean checkCrypto()
+    {
+        try {
+            KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+            keyGen.init(256);   // Max sym key size is 128 unless unlimited
+                                // strength jurisdiction policy files installed.
+            SecretKey skey = keyGen.generateKey();
+            byte[] raw = skey.getEncoded();
+            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
+            
+                // This usually will throw InvalidKeyException unless the
+                // unlimited jurisdiction policy files are installed. However,
+                // it can succeed even if it's not a provider chooses to use
+                // an exemption mechanism such as key escrow, key recovery, or
+                // key weakening for this cipher instead.
+            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
+            
+                // Try the encryption on dummy string to make sure it works.
+                // Not using padding so # bytes must be multiple of AES cipher
+                // block size which is 16 bytes. Also, OK not to use UTF-8 here.
+            byte[] encrypted = cipher.doFinal("1234567890123456".getBytes());
+            assert encrypted != null : "Encryption of test string failed!";
+            ExemptionMechanism em = cipher.getExemptionMechanism();
+            if ( em != null ) {
+                System.out.println("Cipher uses exemption mechanism " + em.getName());
+                return false;   // This is actually an indeterminate case, but
+                                // we can't bank on it at least for this
+                                // (default) provider.
+            }
+        } catch( InvalidKeyException ikex ) {
+            System.out.println("CryptoPolicy: 256 bits is " +
+            		"invalid key size ==> unlimited strength crypto NOT installed!");
+            return false;
+        } catch( Exception ex ) {
+            System.out.println("Caught unexpected exception: " + ex);
+            ex.printStackTrace(System.out);
+            return false;
+        }
+        return true;
+    }
+
+    public static void main(String[] args)
+    {
+        if ( isUnlimitedStrengthCryptoAvailable() ) {
+            System.out.println("Unlimited strength crypto IS available.");
+        } else {
+            System.out.println("Unlimited strength crypto is NOT available.");
+        }
+        System.exit( isUnlimitedStrengthCryptoAvailable() ? 0 : 1 );
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesTest.java.svn-base
new file mode 100644
index 0000000..d097137
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesTest.java.svn-base
@@ -0,0 +1,232 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
+import java.util.Iterator;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.crypto.DefaultEncryptedProperties;
+
+/**
+ * The Class EncryptedPropertiesTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncryptedPropertiesTest extends TestCase {
+
+	/**
+	 * Instantiates a new encrypted properties test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public EncryptedPropertiesTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(EncryptedPropertiesTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of getProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testGetProperty() throws EncryptionException {
+		System.out.println("getProperty");
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		assertNull(instance.getProperty("ridiculous"));
+	}
+
+	/**
+	 * Test of setProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testSetProperty() throws EncryptionException {
+		System.out.println("setProperty");
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		
+        instance.setProperty(name, "");
+        result = instance.getProperty(name);
+        assertEquals(result, "");
+        
+        try {
+            instance.setProperty(null, value);
+            fail("testSetProperty(): Null property name did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionException );
+        }
+        try {
+            instance.setProperty(name, null);
+            fail("testSetProperty(): Null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionException );
+        }
+		try {
+			instance.setProperty(null, null);			
+			fail("testSetProperty(): Null property name and valud did not result in expected exception.");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof EncryptionException );
+		}
+	}
+
+	/**
+	 * Test the behavior when the requested key does not exist.
+	 */
+	public void testNonExistantKeyValue() throws Exception
+	{
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		assertNull(instance.getProperty("not.there"));
+	}
+
+	/**
+	 * Test of keySet method, of class org.owasp.esapi.EncryptedProperties.
+	 */
+	public void testKeySet() throws Exception
+	{
+		boolean sawTwo = false;
+		boolean sawOne = false;
+
+		System.out.println("keySet");
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		instance.setProperty("one", "two");
+		instance.setProperty("two", "three");
+		Iterator i = instance.keySet().iterator();
+		while(i.hasNext())
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+					sawOne = true;
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+					sawTwo = true;
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test storing and loading of encrypted properties.
+	 */
+	public void testStoreLoad() throws Exception
+	{
+		DefaultEncryptedProperties toLoad = new DefaultEncryptedProperties();
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		ByteArrayInputStream bais;
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+	    DefaultEncryptedProperties toStore = new DefaultEncryptedProperties();
+		toStore.setProperty("one", "two");
+		toStore.setProperty("two", "three");
+		toStore.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+		toStore.store(baos, "testStore");
+
+		bais = new ByteArrayInputStream(baos.toByteArray());
+		toLoad.load(bais);
+
+		for(Iterator i=toLoad.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", toLoad.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", toLoad.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 toStore.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesUtilsTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesUtilsTest.java.svn-base
new file mode 100644
index 0000000..8619dbc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptedPropertiesUtilsTest.java.svn-base
@@ -0,0 +1,196 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * The Class EncryptedPropertiesTest.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class EncryptedPropertiesUtilsTest extends TestCase {
+
+	private static final String KEY1	= "quick";
+	private static final String VALUE1	= "brown fox";
+	private static final String KEY2	= "jumps";
+	private static final String VALUE2	= "lazy dog";
+	private static final String KEY3	= "joe bob";
+	private static final String VALUE3	= "jim bob";
+	private static final String KEY4	= "sally sue";
+	private static final String VALUE4	= "betty mae";
+
+	private static final String PLAINTEXT_FILENAME		= "plaintext.properties";
+	private static final String ENCRYPTED_FILENAME_1	= "encrypted.properties";
+	private static final String ENCRYPTED_FILENAME_2	= "encrypted.2.properties";
+
+	/**
+	 * Instantiates a new encrypted properties test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public EncryptedPropertiesUtilsTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception {
+		//write an initial plaintext properties file
+		Properties props = new Properties();
+		props.setProperty(KEY3, VALUE3);
+		props.setProperty(KEY4, VALUE4);
+
+		props.store(new FileOutputStream(PLAINTEXT_FILENAME), "Plaintext test file created by EncryptedPropertiesUtilsTest");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception {
+		File[] delFiles = new File[] { new File(PLAINTEXT_FILENAME), new File(ENCRYPTED_FILENAME_1), new File(ENCRYPTED_FILENAME_2) };
+        for ( File f : delFiles ) {
+            f.deleteOnExit();
+        }
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(EncryptedPropertiesUtilsTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of creating and storing a new EncryptedProperties from scratch,
+	 * as if calling:
+	 * <code>
+	 *     EncryptedPropertiesUtils --out encrypted.properties
+	 * </code>
+	 *
+	 * @throws Exception Any exception that occurs
+	 */
+	public void testCreateNew() throws Exception {
+
+		//create a new properties with no input
+		Properties props = EncryptedPropertiesUtils.loadProperties(null, null);
+		
+		//add some properties
+		Object prop1 = EncryptedPropertiesUtils.addProperty(props, KEY1, VALUE1);
+		assertNull("Expected null but returned: " + prop1, prop1);
+
+		Object prop2 = EncryptedPropertiesUtils.addProperty(props, KEY2, VALUE2);
+		assertNull("Expected null but returned: " + prop2, prop2);
+
+		//store the file 
+		EncryptedPropertiesUtils.storeProperties(ENCRYPTED_FILENAME_1, props, "Encrypted Properties File generated by EncryptedPropertiesUtilsTest");
+
+		//try reading in the resulting file
+		ReferenceEncryptedProperties loadedProps = new ReferenceEncryptedProperties();
+		loadedProps.load(new FileReader(ENCRYPTED_FILENAME_1));
+		
+		assertEquals(VALUE1, loadedProps.getProperty(KEY1));
+		assertEquals(VALUE2, loadedProps.getProperty(KEY2));
+	}
+
+	/**
+	 * Test of loading a plaintext file and storing it as an encrypted properties file,
+	 * as if calling:
+	 * <code>
+	 *     EncryptedPropertiesUtils --in plaintext.properties --out encrypted.properties --in-encrypted false
+	 * </code>
+	 *
+	 * @throws Exception Any exception that occurs
+	 */
+	public void testLoadPlaintextAndEncrypt() throws Exception {
+
+		//load the plaintext properties file
+		Properties props = EncryptedPropertiesUtils.loadProperties(PLAINTEXT_FILENAME, false);
+
+		//test some properties using getProperty
+		assertEquals(VALUE3, props.getProperty(KEY3));
+		assertEquals(VALUE4, props.getProperty(KEY4));
+
+		//store the file
+		EncryptedPropertiesUtils.storeProperties(ENCRYPTED_FILENAME_1, props, "Encrypted Properties File generated by EncryptedPropertiesUtilsTest");
+
+		//try reading in the resulting file
+		ReferenceEncryptedProperties loadedProps = new ReferenceEncryptedProperties();
+		loadedProps.load(new FileReader(ENCRYPTED_FILENAME_1));
+
+		assertEquals(VALUE3, loadedProps.getProperty(KEY3));
+		assertEquals(VALUE4, loadedProps.getProperty(KEY4));
+	}
+
+	/**
+	 * Test of loading an encrypted file, adding new properties and storing it as an encrypted properties file,
+	 * as if calling:
+	 * <code>
+	 *     EncryptedPropertiesUtils --in encrypted.properties --out encrypted.2.properties
+	 * </code>
+	 *
+	 * @throws Exception Any exception that occurs
+	 */
+	public void testLoadEncryptedAndAdd() throws Exception {
+
+		//load the plaintext properties file
+		Properties props = EncryptedPropertiesUtils.loadProperties(ENCRYPTED_FILENAME_1, true);
+
+		//test some properties
+		assertEquals(VALUE3, props.getProperty(KEY3));
+		assertEquals(VALUE4, props.getProperty(KEY4));
+
+		//add some new properties
+		EncryptedPropertiesUtils.addProperty(props, KEY1, VALUE1);
+		EncryptedPropertiesUtils.addProperty(props, KEY2, VALUE2);
+
+		//test the newly added properties
+		assertEquals(VALUE1, props.getProperty(KEY1));
+		assertEquals(VALUE2, props.getProperty(KEY2));
+
+		//store the file
+		EncryptedPropertiesUtils.storeProperties(ENCRYPTED_FILENAME_2, props, "Encrypted Properties File generated by EncryptedPropertiesUtilsTest");
+
+		//try reading in the resulting file
+		ReferenceEncryptedProperties loadedProps = new ReferenceEncryptedProperties();
+		loadedProps.load(new FileReader(ENCRYPTED_FILENAME_2));
+
+		//test the values read in
+		assertEquals(VALUE1, loadedProps.getProperty(KEY1));
+		assertEquals(VALUE2, loadedProps.getProperty(KEY2));
+		assertEquals(VALUE3, loadedProps.getProperty(KEY3));
+		assertEquals(VALUE4, loadedProps.getProperty(KEY4));
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptorTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptorTest.java.svn-base
new file mode 100644
index 0000000..35709a2
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/EncryptorTest.java.svn-base
@@ -0,0 +1,527 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.crypto.SecretKey;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.reference.crypto.JavaEncryptor;
+
+/**
+ * The Class EncryptorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author kevin.w.wall at gmail.com
+ */
+public class EncryptorTest extends TestCase {
+    
+    /**
+	 * Instantiates a new encryptor test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public EncryptorTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    @SuppressWarnings("deprecation")
+	protected void setUp() throws Exception {
+        // This is only mechanism to change this for now. Will do this with
+        // a soon to be CryptoControls class or equivalent mechanism in a
+    	// future release.
+        ESAPI.securityConfiguration().setCipherTransformation("AES/CBC/PKCS5Padding");
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+     * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(EncryptorTest.class);
+        
+        return suite;
+    }
+
+    /**
+	 * Test of hash method, of class org.owasp.esapi.Encryptor.
+     *
+     * @throws EncryptionException
+     */
+    public void testHash() throws EncryptionException {
+        System.out.println("testHash()");
+        Encryptor instance = ESAPI.encryptor();
+        String hash1 = instance.hash("test1", "salt");
+        String hash2 = instance.hash("test2", "salt");
+        assertFalse(hash1.equals(hash2));
+        String hash3 = instance.hash("test", "salt1");
+        String hash4 = instance.hash("test", "salt2");
+        assertFalse(hash3.equals(hash4));
+    }
+
+    /**
+	 * Test of new encrypt / decrypt method for Strings whose length is
+	 * not a multiple of the cipher block size (16 bytes for AES).
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+    public void testEncryptDecrypt1() throws EncryptionException {
+        System.out.println("testEncryptDecrypt2()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = "test1234test1234tes"; // Not a multiple of block size (16 bytes)
+        try {
+            CipherText ct = instance.encrypt(new PlainText(plaintext));
+            PlainText pt = instance.decrypt(ct);
+            assertTrue( pt.toString().equals(plaintext) );
+        }
+        catch( EncryptionException e ) {
+        	fail("testEncryptDecrypt2(): Caught exception: " + e);
+        }
+    }
+
+    /**
+	 * Test of new encrypt / decrypt method for Strings whose length is
+	 * same as cipher block size (16 bytes for AES).
+	 */
+    public void testEncryptDecrypt2() {
+        System.out.println("testEncryptDecrypt2()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = "test1234test1234";
+        try {
+            CipherText ct = instance.encrypt(new PlainText(plaintext));
+            PlainText pt = instance.decrypt(ct);
+            assertTrue( pt.toString().equals(plaintext) );
+        }
+        catch( EncryptionException e ) {
+        	fail("testEncryptDecrypt2(): Caught exception: " + e);
+        }
+    }
+
+    /**
+     * Test of encrypt methods for empty String.
+     */
+    public void testEncryptEmptyStrings() {
+        System.out.println("testEncryptEmptyStrings()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = "";
+        try {
+            // System.out.println("New encryption methods");
+            CipherText ct = instance.encrypt(new PlainText(plaintext));
+            PlainText pt = instance.decrypt(ct);
+            assertTrue( pt.toString().equals("") );
+        } catch(Exception e) {
+            fail("testEncryptEmptyStrings() -- Caught exception: " + e);
+        }
+    }
+    
+    /**
+     * Test encryption method for null.
+     */
+    public void testEncryptNull() {
+        System.out.println("testEncryptNull()");
+        Encryptor instance = ESAPI.encryptor();
+        try {
+			CipherText ct = instance.encrypt( null );  // Should throw NPE or AssertionError
+            fail("New encrypt(PlainText) method did not throw. Result was: " + ct.toString());
+        } catch(Throwable t) {
+            // It should be one of these, depending on whether or not assertions are enabled.
+            assertTrue( t instanceof IllegalArgumentException || t instanceof AssertionError);
+        }
+    }
+
+    /**
+     * Test decryption method for null.
+     */
+    public void testDecryptNull() {
+        System.out.println("testDecryptNull()");
+        Encryptor instance = ESAPI.encryptor();
+        try {
+			PlainText pt = instance.decrypt( null );  // Should throw IllegalArgumentException or AssertionError
+            fail("New decrypt(PlainText) method did not throw. Result was: " + pt.toString());
+        } catch(Throwable t) {
+            // It should be one of these, depending on whether or not assertions are enabled.
+            assertTrue( t instanceof IllegalArgumentException || t instanceof AssertionError);
+        }
+    }
+    
+    /**
+     * Test of new encrypt / decrypt methods added in ESAPI 2.0.
+     */
+    public void testNewEncryptDecrypt() {
+    	System.out.println("testNewEncryptDecrypt()");
+    	try {
+
+    	    // Let's try it with a 2-key version of 3DES. This should work for all
+    	    // installations, whereas the 3-key Triple DES will only work for those
+    	    // who have the Unlimited Strength Jurisdiction Policy files installed.
+			runNewEncryptDecryptTestCase("DESede/CBC/PKCS5Padding", 112, "1234567890".getBytes("UTF-8"));
+			runNewEncryptDecryptTestCase("DESede/CBC/NoPadding", 112, "12345678".getBytes("UTF-8"));
+			
+			runNewEncryptDecryptTestCase("DES/ECB/NoPadding", 56, "test1234".getBytes("UTF-8"));
+			
+	        runNewEncryptDecryptTestCase("AES/CBC/PKCS5Padding", 128, "Encrypt the world!".getBytes("UTF-8"));
+	        
+	        // These tests are only valid (and run) if one has the JCE Unlimited
+	        // Strength Jurisdiction Policy files installed for this Java VM.
+	            // 256-bit AES
+            runNewEncryptDecryptTestCase("AES/ECB/NoPadding", 256, "test1234test1234".getBytes("UTF-8"));
+                // 168-bit (aka, 3-key) Triple DES
+            runNewEncryptDecryptTestCase("DESede/CBC/PKCS5Padding", 168, "Groucho's secret word".getBytes("UTF-8"));
+		} catch (UnsupportedEncodingException e) {
+			fail("OK, who stole UTF-8 encoding from the Java rt.jar ???");
+		}
+    	
+    }
+    
+    /**
+     * Helper method to test new encryption / decryption.
+     * @param cipherXform	Cipher transformation
+     * @param keySize	Size of key, in bits.
+     * @param plaintextBytes Byte array of plaintext.
+     * @return The base64-encoded IV+ciphertext (or just ciphertext if no IV) or
+     *         null if {@code keysize} is greater than 128 bits and unlimited
+     *         strength crypto is not available for this Java VM.
+     */
+    private String runNewEncryptDecryptTestCase(String cipherXform, int keySize, byte[] plaintextBytes) {
+    	System.out.println("New encrypt / decrypt: " + cipherXform);
+    	
+    	if ( keySize > 128 && !CryptoPolicy.isUnlimitedStrengthCryptoAvailable() ) {
+    	    System.out.println("Skipping test for cipher transformation " +
+    	                       cipherXform + " with key size of " + keySize +
+    	                       " bits because this requires JCE Unlimited Strength" +
+    	                       " Jurisdiction Policy files to be installed and they" +
+    	                       " are not.");
+    	    return null;
+    	}
+
+    	try {
+    		// Generate an appropriate random secret key
+			SecretKey skey = CryptoHelper.generateSecretKey(cipherXform, keySize);	
+			assertTrue( skey.getAlgorithm().equals(cipherXform.split("/")[0]) );
+			String cipherAlg = cipherXform.split("/")[0];
+			
+			// Adjust key size for DES and DESede specific oddities.
+			// NOTE: Key size that encrypt() method is using is 192 bits!!!
+    		//        which is 3 times 64 bits, but DES key size is only 56 bits.
+    		// See 'IMPORTANT NOTE', in JavaEncryptor, near line 376. It's a "feature"!!!
+			if ( cipherAlg.equals( "DESede" ) ) {
+				keySize = 192;
+			} else if ( cipherAlg.equals( "DES" ) ) {
+				keySize = 64;
+			} // Else... use specified keySize.
+			assertTrue( (keySize / 8) == skey.getEncoded().length );
+//			System.out.println("testNewEncryptDecrypt(): Skey length (bits) = " + 8 * skey.getEncoded().length);
+
+			// Change to a possibly different cipher. This is kludgey at best. Am thinking about an
+			// alternate way to do this using a new 'CryptoControls' class. Maybe not until release 2.1.
+			// Change the cipher transform from whatever it currently is to the specified cipherXform.
+	    	@SuppressWarnings("deprecation")
+			String oldCipherXform = ESAPI.securityConfiguration().setCipherTransformation(cipherXform);
+	    	if ( ! cipherXform.equals(oldCipherXform) ) {
+	    		System.out.println("Cipher xform changed from \"" + oldCipherXform + "\" to \"" + cipherXform + "\"");
+	    	}
+	    	
+	    	// Get an Encryptor instance with the specified, possibly new, cipher transformation.
+	    	Encryptor instance = ESAPI.encryptor();
+	    	PlainText plaintext = new PlainText(plaintextBytes);
+	    	PlainText origPlainText = new PlainText( plaintext.toString() ); // Make _copy_ of original for comparison.
+	    	
+	    	// Do the encryption with the new encrypt() method and get back the CipherText.
+	    	CipherText ciphertext = instance.encrypt(skey, plaintext);	// The new encrypt() method.
+	    	System.out.println("DEBUG: Encrypt(): CipherText object is -- " + ciphertext);
+	    	assertTrue( ciphertext != null );
+//	    	System.out.println("DEBUG: After encryption: base64-encoded IV+ciphertext: " + ciphertext.getEncodedIVCipherText());
+//	    	System.out.println("\t\tOr... " + ESAPI.encoder().decodeFromBase64(ciphertext.getEncodedIVCipherText()) );
+//	    	System.out.println("DEBUG: After encryption: base64-encoded raw ciphertext: " + ciphertext.getBase64EncodedRawCipherText());
+//	    	System.out.println("\t\tOr... " + ESAPI.encoder().decodeFromBase64(ciphertext.getBase64EncodedRawCipherText()) );
+
+	    	// If we are supposed to have overwritten the plaintext, check this to see
+	    	// if origPlainText was indeed overwritten.
+			boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText();
+			if ( overwritePlaintext ) {
+				assertTrue( isPlaintextOverwritten(plaintext) );
+			}
+	    	
+	    	// Take the resulting ciphertext and decrypt w/ new decryption method.
+	    	PlainText decryptedPlaintext  = instance.decrypt(skey, ciphertext);		// The new decrypt() method.
+	    	
+	    	// Make sure we got back the same thing we started with.
+	    	System.out.println("\tOriginal plaintext: " + origPlainText);
+	    	System.out.println("\tResult after decryption: " + decryptedPlaintext);
+			assertTrue( "Failed to decrypt properly.", origPlainText.toString().equals( decryptedPlaintext.toString() ) );
+	    	
+	    	// Restore the previous cipher transformation. For now, this is only way to do this.
+	    	@SuppressWarnings("deprecation")
+			String previousCipherXform = ESAPI.securityConfiguration().setCipherTransformation(null);
+	    	assertTrue( previousCipherXform.equals( cipherXform ) );
+	    	String defaultCipherXform = ESAPI.securityConfiguration().getCipherTransformation();
+	    	assertTrue( defaultCipherXform.equals( oldCipherXform ) );
+	    	
+	    	return ciphertext.getEncodedIVCipherText();
+		} catch (Exception e) {
+			// OK if not counted toward code coverage.
+			System.out.println("testNewEncryptDecrypt(): Caught unexpected exception: " + e.getClass().getName());
+			e.printStackTrace(System.out);
+			fail("Caught unexpected exception; msg was: " + e);
+		}
+		return null;
+    }
+    
+    private static boolean isPlaintextOverwritten(PlainText plaintext) {
+    	// Note: An assumption here that the original plaintext did not consist
+    	// entirely of all '*' characters.
+    	byte[] ptBytes = plaintext.asBytes();
+    	
+    	for ( int i = 0; i < ptBytes.length; i++ ) {
+    		if ( ptBytes[i] != '*' ) {
+    			return false;
+    		}
+    	}
+    	return true;
+    }
+    
+    // TODO - Because none of the encryption / decryption tests persists
+    //		  encrypted data across runs, that means everything is run
+    //		  under same JVM at same time thus always with the same
+    //		  _native_ byte encoding.
+    //
+    //		  Need test(s) such that data is persisted across JVM runs
+    //		  so we can test a run on (say) a Windows Intel box can decrypt
+    //		  encrypted data produced by the reference Encryptor on
+    //		  (say) a Solaris SPARC box. I.e., test that the change to
+    //		  JavaEncryptor to use UTF-8 encoding throughout works as
+    //		  desired.
+    //
+    //		  Files saved across tests need to be added to SVN (under
+    //		  resources or where) and they should be named so we know
+    //		  where and how they were created. E.g., WinOS-AES-ECB.dat,
+    //		  Sparc-Solaris-AEC-CBC-PKCS5Padding.dat, etc., but they should be
+    //		  able to be decrypted from any platform. May wish to place that
+    //		  under a separate JUnit test.
+    //
+    // TODO - Need to test rainy day paths of new encrypt / decrypt so can
+    //		  verify that exception handling working OK, etc. Maybe also in
+    //		  a separate JUnit test, since everything here seems to be sunny
+    //		  day path. (Note: Some of this no in new test case,
+    //		  org.owasp.esapi.crypto.ESAPICryptoMACByPassTest.)
+    //
+    //				-kevin wall
+    
+
+    /**
+	 * Test of sign method, of class org.owasp.esapi.Encryptor.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+    public void testSign() throws EncryptionException {
+        System.out.println("testSign()");        
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = ESAPI.randomizer().getRandomString( 32, EncoderConstants.CHAR_ALPHANUMERICS );
+        String signature = instance.sign(plaintext);
+        assertTrue( instance.verifySignature( signature, plaintext ) );
+        assertFalse( instance.verifySignature( signature, "ridiculous" ) );
+        assertFalse( instance.verifySignature( "ridiculous", plaintext ) );
+    }
+
+    /**
+	 * Test of verifySignature method, of class org.owasp.esapi.Encryptor.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+    public void testVerifySignature() throws EncryptionException {
+        System.out.println("testVerifySignature()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = ESAPI.randomizer().getRandomString( 32, EncoderConstants.CHAR_ALPHANUMERICS );
+        String signature = instance.sign(plaintext);
+        assertTrue( instance.verifySignature( signature, plaintext ) );
+    }
+    
+ 
+    /**
+	 * Test of seal method, of class org.owasp.esapi.Encryptor.
+	 * 
+     * @throws IntegrityException
+	 */
+    public void testSeal() throws IntegrityException {
+        System.out.println("testSeal()");
+        Encryptor instance = ESAPI.encryptor(); 
+        String plaintext = ESAPI.randomizer().getRandomString( 32, EncoderConstants.CHAR_ALPHANUMERICS );
+        String seal = instance.seal( plaintext, instance.getTimeStamp() + 1000*60 );
+        instance.verifySeal( seal );
+        
+        int progressMark = 1;
+        boolean caughtExpectedEx = false;
+        try {
+            seal = instance.seal("", instance.getTimeStamp() + 1000*60);
+            progressMark++;
+            instance.verifySeal(seal);
+            progressMark++;
+        } catch(Exception e) {
+            fail("Failed empty string test: " + e + "; progress mark = " + progressMark);
+        }
+        try {
+            seal = instance.seal(null, instance.getTimeStamp() + 1000*60);
+            fail("Did not throw expected IllegalArgumentException");
+        } catch(IllegalArgumentException e) {
+            caughtExpectedEx = true;
+        } catch(Exception e) {
+            fail("Failed null string test; did not get expected IllegalArgumentException: " + e);
+        }
+        assertTrue(caughtExpectedEx);
+        
+        try {
+            seal = instance.seal("test", 0);
+            progressMark++;
+            // instance.verifySeal(seal);
+            progressMark++;
+        } catch(Exception e) {
+            fail("Fail test with 0 timestamp: " + e + "; progress mark = " + progressMark);
+        }
+        try {
+            seal = instance.seal("test", -1);
+            progressMark++;
+            // instance.verifySeal(seal);
+            progressMark++;
+        } catch(Exception e) {
+            fail("Fail test with -1 timestamp: " + e + "; progress mark = " + progressMark);
+        }
+    }
+
+    /**
+	 * Test of verifySeal method, of class org.owasp.esapi.Encryptor.
+	 * 
+     * @throws EnterpriseSecurityException
+	 */
+    public void testVerifySeal() throws EnterpriseSecurityException {
+        final int NSEC = 5;
+        System.out.println("testVerifySeal()");
+        Encryptor instance = ESAPI.encryptor(); 
+        String plaintext = "ridiculous:with:delimiters";    // Should now work w/ : (issue #28)
+        String seal = instance.seal( plaintext, instance.getRelativeTimeStamp( 1000 * NSEC ) );
+        try {
+        	assertNotNull("Encryptor.seal() returned null", seal );
+        	assertTrue("Failed to verify seal", instance.verifySeal( seal ) );
+        } catch ( Exception e ) {
+        	fail();
+        }
+        int progressMark = 1;
+        try {
+            // NOTE: I regrouped these all into a single try / catch since they
+            //       all test the same thing. Hence if one fails, they all should.
+            //       Also changed these tests so they no longer depend on the
+            //       deprecated encrypt() methods. IMO, *all these multiple
+            //       similar tests are not really required*, as they all are more
+            //       or less testing the same thing.
+            //                                              -kevin wall
+            // ================================================================
+            // Try to validate some invalid seals.
+            //
+            // All these should return false and log a warning with an Exception stack
+            // trace caused by an EncryptionException indicating "Invalid seal".
+        	assertFalse( instance.verifySeal( plaintext ) );
+        	progressMark++;      	
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;            
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(100 + ":" + plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":" + plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":random:" + plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":random:" + plaintext+ ":badsig")  ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":random:" + plaintext + ":"+ instance.sign( Long.MAX_VALUE + ":random:" + plaintext) ) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+        } catch ( Exception e ) {
+        	// fail("Failed invalid seal test # " + progressMark + " to verify seal.");
+            System.err.println("Failed seal verification at step # " + progressMark);
+            System.err.println("Exception was: " + e);
+            e.printStackTrace(System.err);
+        }
+        
+        try {
+            Thread.sleep(1000 * (NSEC + 1) );
+                // Seal now past expiration date.
+            assertFalse( instance.verifySeal( seal ) );
+        } catch ( Exception e ) {
+            fail("Failed expired seal test. Seal should be expired.");
+        }
+    }
+        
+
+    @SuppressWarnings("deprecation")
+    public void testEncryptionSerialization() throws EncryptionException {
+        String secretMsg = "Secret Message";
+        ESAPI.securityConfiguration().setCipherTransformation("AES/CBC/PKCS5Padding");
+        CipherText ct = ESAPI.encryptor().encrypt(new PlainText(secretMsg));
+        
+        byte[] serializedCipherText = ct.asPortableSerializedByteArray();
+        
+        PlainText plainText = ESAPI.encryptor().decrypt(
+                                CipherText.fromPortableSerializedBytes(serializedCipherText) );
+        
+        assertTrue( secretMsg.equals( plainText.toString() ) );
+    }
+    
+    /**
+     * Test of main method, of class org.owasp.esapi.Encryptor. Must be done by
+     * visual inspection for now. (Needs improvement.)
+     * @throws Exception
+     */
+    public void testMain() throws Exception {
+        System.out.println("testMain(): Encryptor Main with '-print' argument.");
+        String[] args = {};
+        JavaEncryptor.main( args );        
+        String[] args1 = {"-print"};
+        // TODO:
+        // It probably would be a better if System.out were changed to be
+        // a file or a byte stream so that the output could be slurped up
+        // and checked against (at least some of) the expected output.
+        // Left as an exercise to some future, ambitious ESAPI developer. ;-)
+        JavaEncryptor.main( args1 );        
+    }    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/ReferenceEncryptedPropertiesTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/ReferenceEncryptedPropertiesTest.java.svn-base
new file mode 100644
index 0000000..89eb563
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/.svn/text-base/ReferenceEncryptedPropertiesTest.java.svn-base
@@ -0,0 +1,535 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.*;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EncryptionRuntimeException;
+
+/**
+ * The Class EncryptedPropertiesTest.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class ReferenceEncryptedPropertiesTest extends TestCase {
+
+	/**
+	 * Instantiates a new encrypted properties test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public ReferenceEncryptedPropertiesTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ReferenceEncryptedPropertiesTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of getProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testGetProperty() throws EncryptionException {
+		System.out.println("getProperty");
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		assertNull(instance.getProperty("ridiculous"));
+	}
+
+	/**
+	 * Test of setProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testSetProperty() throws EncryptionException {
+		System.out.println("setProperty");
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		
+        instance.setProperty(name, "");
+        result = instance.getProperty(name);
+        assertEquals(result, "");
+        
+        try {
+            instance.setProperty(null, value);
+            fail("testSetProperty(): Null property name did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionRuntimeException );
+        }
+        try {
+            instance.setProperty(name, null);
+            fail("testSetProperty(): Null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionRuntimeException );
+        }
+		try {
+			instance.setProperty(null, null);			
+			fail("testSetProperty(): Null property name and valud did not result in expected exception.");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof EncryptionRuntimeException );
+		}
+	}
+
+	/**
+	 * Test the behavior when the requested key does not exist.
+	 */
+	public void testNonExistantKeyValue() throws Exception
+	{
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		assertNull(instance.getProperty("not.there"));
+	}
+
+	/**
+	 * Test of keySet method, of class org.owasp.esapi.EncryptedProperties.
+	 */
+	public void testKeySet() throws Exception
+	{
+		boolean sawTwo = false;
+		boolean sawOne = false;
+
+		System.out.println("keySet");
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		instance.setProperty("one", "two");
+		instance.setProperty("two", "three");
+		Iterator i = instance.keySet().iterator();
+		while(i.hasNext())
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+					sawOne = true;
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+					sawTwo = true;
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test storing and loading of encrypted properties.
+	 */
+	public void testStoreLoad() throws Exception
+	{
+		ReferenceEncryptedProperties toLoad = new ReferenceEncryptedProperties();
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		ByteArrayInputStream bais;
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+	    ReferenceEncryptedProperties toStore = new ReferenceEncryptedProperties();
+		toStore.setProperty("one", "two");
+		toStore.setProperty("two", "three");
+		toStore.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+		toStore.store(baos, "testStore");
+
+		bais = new ByteArrayInputStream(baos.toByteArray());
+		toLoad.load(bais);
+
+		for(Iterator i=toLoad.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", toLoad.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", toLoad.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 toStore.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test storing and loading of encrypted properties.
+	 */
+	public void testStoreLoadWithReader() throws Exception
+	{
+/*
+		//create an EncryptedProperties to store
+		ReferenceEncryptedProperties toStore = new ReferenceEncryptedProperties();
+		toStore.setProperty("one", "two");
+		toStore.setProperty("two", "three");
+		toStore.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		//store properties to a Writer
+		CharArrayWriter writer = new CharArrayWriter();
+		//toStore.store(writer, "testStore");
+
+		//read it back in from a Reader
+		Reader reader = new CharArrayReader(writer.toCharArray());
+
+		ReferenceEncryptedProperties toLoad = new ReferenceEncryptedProperties();
+		toLoad.load(reader);
+
+		//test the resulting loaded properties
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+		for(Iterator i=toLoad.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", toLoad.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", toLoad.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 toStore.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+*/
+	}
+
+	/**
+	 * Test overridden put method.
+	 */
+	public void testPut() throws Exception
+	{
+		ReferenceEncryptedProperties props = new ReferenceEncryptedProperties();
+
+		String name = "name";
+		String value = "value";
+
+		props.put(name, value);  //should work and store encrypted
+		String result = props.getProperty(name);
+		assertEquals(value, result);
+
+		Integer five = new Integer(5);
+
+		try {
+			props.put("Integer", five); //should fail and throw IllegalArgumentException
+			fail("testPut(): Non-String property value did not result in expected exception.");
+		} catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+			props.put(five, "Integer"); //should fail and throw IllegalArgumentException
+			fail("testPut(): Non-String property key did not result in expected exception.");
+		} catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+			props.put(five, five); //should fail and throw IllegalArgumentException
+			fail("testPut(): Non-String property key and value did not result in expected exception.");
+		} catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+            props.put(null, five);
+            fail("testSetProperty(): Null property name and non-String value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+        try {
+            props.put(five, null);
+            fail("testSetProperty(): Non-String key and null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+            props.put(null, value);
+            fail("testSetProperty(): Null property name did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+        try {
+            props.put(name, null);
+            fail("testSetProperty(): Null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+			props.put(null, null);
+			fail("testSetProperty(): Null property name and valud did not result in expected exception.");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof IllegalArgumentException );
+		}
+	}
+
+	/**
+	 * Test that ReferenceEncryptedProperties can be properly constructed
+	 * with an instance of Properties.
+	 */
+	public void testConstructWithProperties() {
+		Properties props = new Properties();
+		props.setProperty("one", "two");
+		props.setProperty("two", "three");
+		props.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		ReferenceEncryptedProperties eProps = new ReferenceEncryptedProperties(props);
+
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+		for(Iterator i=eProps.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", eProps.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", eProps.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 eProps.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test that ReferenceEncryptedProperties can be properly constructed
+	 * with an instance of EncryptedProperties.
+	 */
+	public void testConstructWithEncryptedProperties() throws Exception {
+		ReferenceEncryptedProperties props = new ReferenceEncryptedProperties();
+		props.setProperty("one", "two");
+		props.setProperty("two", "three");
+		props.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		ReferenceEncryptedProperties eProps = new ReferenceEncryptedProperties(props);
+
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+		for(Iterator i=eProps.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", eProps.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", eProps.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 eProps.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+
+	/**
+	 * Test overridden methods from Properties and Hashtable.
+	 */
+	public void testOverriddenMethods() throws Exception {
+		Properties props = new ReferenceEncryptedProperties();
+		props.setProperty("one", "two");
+		props.setProperty("two", "three");
+		props.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		FileOutputStream out = new FileOutputStream("ReferenceEncryptedProperties.test.txt");
+		PrintStream ps = new PrintStream(out);
+		try {
+			props.list(ps);
+			fail("testOverriddenMethods(): list(PrintStream) did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		PrintWriter pw = new PrintWriter(new FileWriter("test.out"));
+		try {
+			props.list(pw);
+			fail("testOverriddenMethods(): list(PrintWriter) did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			props.list(ps);
+			fail("testOverriddenMethods(): list(PrintStream) did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			Collection c = props.values();
+			fail("testOverriddenMethods(): values() did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			Collection c = props.entrySet();
+			fail("testOverriddenMethods(): entrySet() did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			Enumeration e = props.elements();
+			fail("testOverriddenMethods(): elements() did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+        File f1 = new File("test.out");
+        f1.delete();
+        File f = new File("ReferenceEncryptedProperties.test.txt");
+        f.delete();
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/CryptoPolicy.java b/src/test/java/org/owasp/esapi/reference/crypto/CryptoPolicy.java
new file mode 100644
index 0000000..fe3a8e9
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/CryptoPolicy.java
@@ -0,0 +1,105 @@
+package org.owasp.esapi.reference.crypto;
+
+import java.security.*;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+/**
+ * Helper class to see if unlimited strength crypto is available. If it is
+ * not, then symmetric encryption algorithms are restricted to 128-bit
+ * key size or the encryption must provide key weakening or key escrow.
+ * <p>
+ * This program attempts to generate a 256-bit AES key and use it to do
+ * to a simple encryption. If the encryption succeeds, the assumption is
+ * that the JVM being used has the "unlimited" strength JCE jurisdiction
+ * policy files installed.
+ * </p><p>
+ * We use this for JUnit tests. If unlimited strength crypto is not available,
+ * we simply skip certain JUnit tests that would require it.
+ * </p><p>
+ * The reason for not adding this class to ESAPI proper is because its mostly
+ * pointless to find out at runtime that you don't have the unlimited strength
+ * JCE jurisdiction policy files installed. If you don't, you're SOL until you
+ * install them and even if you could do that from a running JVM, chances are
+ * slim to none that one could easily get your JCE provider to work with them.
+ * (Well, one <i>might</i> be able to unload the JCE classes, but you hopefully
+ * are not running your JVM process as 'root' or other privileged account
+ * anyway, so you probably can't install these policy files from your JVM in
+ * the first place.)
+ * </p>
+ * @author kevin.w.wall at gmail.com
+ * @since 2.0
+ */
+public class CryptoPolicy {
+
+    private static boolean checked = false;
+    private static boolean unlimited = false;
+
+    /**
+     * Check to see if unlimited strength crypto is available.
+     * There is an implicit assumption that the JCE jurisdiction policy
+     * files are not going to be changing while this given JVM is running.
+     *
+     * @return True if we can provide keys longer than 128 bits.
+     */
+    public synchronized static boolean isUnlimitedStrengthCryptoAvailable()
+    {
+        if ( checked == false ) {
+            unlimited = checkCrypto();
+            checked = true;
+        }
+        return unlimited;
+    }
+
+    private static boolean checkCrypto()
+    {
+        try {
+            KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+            keyGen.init(256);   // Max sym key size is 128 unless unlimited
+                                // strength jurisdiction policy files installed.
+            SecretKey skey = keyGen.generateKey();
+            byte[] raw = skey.getEncoded();
+            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
+            
+                // This usually will throw InvalidKeyException unless the
+                // unlimited jurisdiction policy files are installed. However,
+                // it can succeed even if it's not a provider chooses to use
+                // an exemption mechanism such as key escrow, key recovery, or
+                // key weakening for this cipher instead.
+            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
+            
+                // Try the encryption on dummy string to make sure it works.
+                // Not using padding so # bytes must be multiple of AES cipher
+                // block size which is 16 bytes. Also, OK not to use UTF-8 here.
+            byte[] encrypted = cipher.doFinal("1234567890123456".getBytes());
+            assert encrypted != null : "Encryption of test string failed!";
+            ExemptionMechanism em = cipher.getExemptionMechanism();
+            if ( em != null ) {
+                System.out.println("Cipher uses exemption mechanism " + em.getName());
+                return false;   // This is actually an indeterminate case, but
+                                // we can't bank on it at least for this
+                                // (default) provider.
+            }
+        } catch( InvalidKeyException ikex ) {
+            System.out.println("CryptoPolicy: 256 bits is " +
+            		"invalid key size ==> unlimited strength crypto NOT installed!");
+            return false;
+        } catch( Exception ex ) {
+            System.out.println("Caught unexpected exception: " + ex);
+            ex.printStackTrace(System.out);
+            return false;
+        }
+        return true;
+    }
+
+    public static void main(String[] args)
+    {
+        if ( isUnlimitedStrengthCryptoAvailable() ) {
+            System.out.println("Unlimited strength crypto IS available.");
+        } else {
+            System.out.println("Unlimited strength crypto is NOT available.");
+        }
+        System.exit( isUnlimitedStrengthCryptoAvailable() ? 0 : 1 );
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesTest.java b/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesTest.java
new file mode 100644
index 0000000..d097137
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesTest.java
@@ -0,0 +1,232 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
+import java.util.Iterator;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.reference.crypto.DefaultEncryptedProperties;
+
+/**
+ * The Class EncryptedPropertiesTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class EncryptedPropertiesTest extends TestCase {
+
+	/**
+	 * Instantiates a new encrypted properties test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public EncryptedPropertiesTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(EncryptedPropertiesTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of getProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testGetProperty() throws EncryptionException {
+		System.out.println("getProperty");
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		assertNull(instance.getProperty("ridiculous"));
+	}
+
+	/**
+	 * Test of setProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testSetProperty() throws EncryptionException {
+		System.out.println("setProperty");
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		
+        instance.setProperty(name, "");
+        result = instance.getProperty(name);
+        assertEquals(result, "");
+        
+        try {
+            instance.setProperty(null, value);
+            fail("testSetProperty(): Null property name did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionException );
+        }
+        try {
+            instance.setProperty(name, null);
+            fail("testSetProperty(): Null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionException );
+        }
+		try {
+			instance.setProperty(null, null);			
+			fail("testSetProperty(): Null property name and valud did not result in expected exception.");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof EncryptionException );
+		}
+	}
+
+	/**
+	 * Test the behavior when the requested key does not exist.
+	 */
+	public void testNonExistantKeyValue() throws Exception
+	{
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		assertNull(instance.getProperty("not.there"));
+	}
+
+	/**
+	 * Test of keySet method, of class org.owasp.esapi.EncryptedProperties.
+	 */
+	public void testKeySet() throws Exception
+	{
+		boolean sawTwo = false;
+		boolean sawOne = false;
+
+		System.out.println("keySet");
+		DefaultEncryptedProperties instance = new DefaultEncryptedProperties();
+		instance.setProperty("one", "two");
+		instance.setProperty("two", "three");
+		Iterator i = instance.keySet().iterator();
+		while(i.hasNext())
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+					sawOne = true;
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+					sawTwo = true;
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test storing and loading of encrypted properties.
+	 */
+	public void testStoreLoad() throws Exception
+	{
+		DefaultEncryptedProperties toLoad = new DefaultEncryptedProperties();
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		ByteArrayInputStream bais;
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+	    DefaultEncryptedProperties toStore = new DefaultEncryptedProperties();
+		toStore.setProperty("one", "two");
+		toStore.setProperty("two", "three");
+		toStore.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+		toStore.store(baos, "testStore");
+
+		bais = new ByteArrayInputStream(baos.toByteArray());
+		toLoad.load(bais);
+
+		for(Iterator i=toLoad.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", toLoad.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", toLoad.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 toStore.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtilsTest.java b/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtilsTest.java
new file mode 100644
index 0000000..8619dbc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/EncryptedPropertiesUtilsTest.java
@@ -0,0 +1,196 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.errors.EncryptionException;
+
+/**
+ * The Class EncryptedPropertiesTest.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class EncryptedPropertiesUtilsTest extends TestCase {
+
+	private static final String KEY1	= "quick";
+	private static final String VALUE1	= "brown fox";
+	private static final String KEY2	= "jumps";
+	private static final String VALUE2	= "lazy dog";
+	private static final String KEY3	= "joe bob";
+	private static final String VALUE3	= "jim bob";
+	private static final String KEY4	= "sally sue";
+	private static final String VALUE4	= "betty mae";
+
+	private static final String PLAINTEXT_FILENAME		= "plaintext.properties";
+	private static final String ENCRYPTED_FILENAME_1	= "encrypted.properties";
+	private static final String ENCRYPTED_FILENAME_2	= "encrypted.2.properties";
+
+	/**
+	 * Instantiates a new encrypted properties test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public EncryptedPropertiesUtilsTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception {
+		//write an initial plaintext properties file
+		Properties props = new Properties();
+		props.setProperty(KEY3, VALUE3);
+		props.setProperty(KEY4, VALUE4);
+
+		props.store(new FileOutputStream(PLAINTEXT_FILENAME), "Plaintext test file created by EncryptedPropertiesUtilsTest");
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception {
+		File[] delFiles = new File[] { new File(PLAINTEXT_FILENAME), new File(ENCRYPTED_FILENAME_1), new File(ENCRYPTED_FILENAME_2) };
+        for ( File f : delFiles ) {
+            f.deleteOnExit();
+        }
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(EncryptedPropertiesUtilsTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of creating and storing a new EncryptedProperties from scratch,
+	 * as if calling:
+	 * <code>
+	 *     EncryptedPropertiesUtils --out encrypted.properties
+	 * </code>
+	 *
+	 * @throws Exception Any exception that occurs
+	 */
+	public void testCreateNew() throws Exception {
+
+		//create a new properties with no input
+		Properties props = EncryptedPropertiesUtils.loadProperties(null, null);
+		
+		//add some properties
+		Object prop1 = EncryptedPropertiesUtils.addProperty(props, KEY1, VALUE1);
+		assertNull("Expected null but returned: " + prop1, prop1);
+
+		Object prop2 = EncryptedPropertiesUtils.addProperty(props, KEY2, VALUE2);
+		assertNull("Expected null but returned: " + prop2, prop2);
+
+		//store the file 
+		EncryptedPropertiesUtils.storeProperties(ENCRYPTED_FILENAME_1, props, "Encrypted Properties File generated by EncryptedPropertiesUtilsTest");
+
+		//try reading in the resulting file
+		ReferenceEncryptedProperties loadedProps = new ReferenceEncryptedProperties();
+		loadedProps.load(new FileReader(ENCRYPTED_FILENAME_1));
+		
+		assertEquals(VALUE1, loadedProps.getProperty(KEY1));
+		assertEquals(VALUE2, loadedProps.getProperty(KEY2));
+	}
+
+	/**
+	 * Test of loading a plaintext file and storing it as an encrypted properties file,
+	 * as if calling:
+	 * <code>
+	 *     EncryptedPropertiesUtils --in plaintext.properties --out encrypted.properties --in-encrypted false
+	 * </code>
+	 *
+	 * @throws Exception Any exception that occurs
+	 */
+	public void testLoadPlaintextAndEncrypt() throws Exception {
+
+		//load the plaintext properties file
+		Properties props = EncryptedPropertiesUtils.loadProperties(PLAINTEXT_FILENAME, false);
+
+		//test some properties using getProperty
+		assertEquals(VALUE3, props.getProperty(KEY3));
+		assertEquals(VALUE4, props.getProperty(KEY4));
+
+		//store the file
+		EncryptedPropertiesUtils.storeProperties(ENCRYPTED_FILENAME_1, props, "Encrypted Properties File generated by EncryptedPropertiesUtilsTest");
+
+		//try reading in the resulting file
+		ReferenceEncryptedProperties loadedProps = new ReferenceEncryptedProperties();
+		loadedProps.load(new FileReader(ENCRYPTED_FILENAME_1));
+
+		assertEquals(VALUE3, loadedProps.getProperty(KEY3));
+		assertEquals(VALUE4, loadedProps.getProperty(KEY4));
+	}
+
+	/**
+	 * Test of loading an encrypted file, adding new properties and storing it as an encrypted properties file,
+	 * as if calling:
+	 * <code>
+	 *     EncryptedPropertiesUtils --in encrypted.properties --out encrypted.2.properties
+	 * </code>
+	 *
+	 * @throws Exception Any exception that occurs
+	 */
+	public void testLoadEncryptedAndAdd() throws Exception {
+
+		//load the plaintext properties file
+		Properties props = EncryptedPropertiesUtils.loadProperties(ENCRYPTED_FILENAME_1, true);
+
+		//test some properties
+		assertEquals(VALUE3, props.getProperty(KEY3));
+		assertEquals(VALUE4, props.getProperty(KEY4));
+
+		//add some new properties
+		EncryptedPropertiesUtils.addProperty(props, KEY1, VALUE1);
+		EncryptedPropertiesUtils.addProperty(props, KEY2, VALUE2);
+
+		//test the newly added properties
+		assertEquals(VALUE1, props.getProperty(KEY1));
+		assertEquals(VALUE2, props.getProperty(KEY2));
+
+		//store the file
+		EncryptedPropertiesUtils.storeProperties(ENCRYPTED_FILENAME_2, props, "Encrypted Properties File generated by EncryptedPropertiesUtilsTest");
+
+		//try reading in the resulting file
+		ReferenceEncryptedProperties loadedProps = new ReferenceEncryptedProperties();
+		loadedProps.load(new FileReader(ENCRYPTED_FILENAME_2));
+
+		//test the values read in
+		assertEquals(VALUE1, loadedProps.getProperty(KEY1));
+		assertEquals(VALUE2, loadedProps.getProperty(KEY2));
+		assertEquals(VALUE3, loadedProps.getProperty(KEY3));
+		assertEquals(VALUE4, loadedProps.getProperty(KEY4));
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java b/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java
new file mode 100644
index 0000000..35709a2
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java
@@ -0,0 +1,527 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.crypto.SecretKey;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.Encryptor;
+import org.owasp.esapi.crypto.CipherText;
+import org.owasp.esapi.crypto.CryptoHelper;
+import org.owasp.esapi.crypto.PlainText;
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EnterpriseSecurityException;
+import org.owasp.esapi.errors.IntegrityException;
+import org.owasp.esapi.reference.crypto.JavaEncryptor;
+
+/**
+ * The Class EncryptorTest.
+ * 
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ * @author kevin.w.wall at gmail.com
+ */
+public class EncryptorTest extends TestCase {
+    
+    /**
+	 * Instantiates a new encryptor test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public EncryptorTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    @SuppressWarnings("deprecation")
+	protected void setUp() throws Exception {
+        // This is only mechanism to change this for now. Will do this with
+        // a soon to be CryptoControls class or equivalent mechanism in a
+    	// future release.
+        ESAPI.securityConfiguration().setCipherTransformation("AES/CBC/PKCS5Padding");
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+     * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(EncryptorTest.class);
+        
+        return suite;
+    }
+
+    /**
+	 * Test of hash method, of class org.owasp.esapi.Encryptor.
+     *
+     * @throws EncryptionException
+     */
+    public void testHash() throws EncryptionException {
+        System.out.println("testHash()");
+        Encryptor instance = ESAPI.encryptor();
+        String hash1 = instance.hash("test1", "salt");
+        String hash2 = instance.hash("test2", "salt");
+        assertFalse(hash1.equals(hash2));
+        String hash3 = instance.hash("test", "salt1");
+        String hash4 = instance.hash("test", "salt2");
+        assertFalse(hash3.equals(hash4));
+    }
+
+    /**
+	 * Test of new encrypt / decrypt method for Strings whose length is
+	 * not a multiple of the cipher block size (16 bytes for AES).
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+    public void testEncryptDecrypt1() throws EncryptionException {
+        System.out.println("testEncryptDecrypt2()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = "test1234test1234tes"; // Not a multiple of block size (16 bytes)
+        try {
+            CipherText ct = instance.encrypt(new PlainText(plaintext));
+            PlainText pt = instance.decrypt(ct);
+            assertTrue( pt.toString().equals(plaintext) );
+        }
+        catch( EncryptionException e ) {
+        	fail("testEncryptDecrypt2(): Caught exception: " + e);
+        }
+    }
+
+    /**
+	 * Test of new encrypt / decrypt method for Strings whose length is
+	 * same as cipher block size (16 bytes for AES).
+	 */
+    public void testEncryptDecrypt2() {
+        System.out.println("testEncryptDecrypt2()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = "test1234test1234";
+        try {
+            CipherText ct = instance.encrypt(new PlainText(plaintext));
+            PlainText pt = instance.decrypt(ct);
+            assertTrue( pt.toString().equals(plaintext) );
+        }
+        catch( EncryptionException e ) {
+        	fail("testEncryptDecrypt2(): Caught exception: " + e);
+        }
+    }
+
+    /**
+     * Test of encrypt methods for empty String.
+     */
+    public void testEncryptEmptyStrings() {
+        System.out.println("testEncryptEmptyStrings()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = "";
+        try {
+            // System.out.println("New encryption methods");
+            CipherText ct = instance.encrypt(new PlainText(plaintext));
+            PlainText pt = instance.decrypt(ct);
+            assertTrue( pt.toString().equals("") );
+        } catch(Exception e) {
+            fail("testEncryptEmptyStrings() -- Caught exception: " + e);
+        }
+    }
+    
+    /**
+     * Test encryption method for null.
+     */
+    public void testEncryptNull() {
+        System.out.println("testEncryptNull()");
+        Encryptor instance = ESAPI.encryptor();
+        try {
+			CipherText ct = instance.encrypt( null );  // Should throw NPE or AssertionError
+            fail("New encrypt(PlainText) method did not throw. Result was: " + ct.toString());
+        } catch(Throwable t) {
+            // It should be one of these, depending on whether or not assertions are enabled.
+            assertTrue( t instanceof IllegalArgumentException || t instanceof AssertionError);
+        }
+    }
+
+    /**
+     * Test decryption method for null.
+     */
+    public void testDecryptNull() {
+        System.out.println("testDecryptNull()");
+        Encryptor instance = ESAPI.encryptor();
+        try {
+			PlainText pt = instance.decrypt( null );  // Should throw IllegalArgumentException or AssertionError
+            fail("New decrypt(PlainText) method did not throw. Result was: " + pt.toString());
+        } catch(Throwable t) {
+            // It should be one of these, depending on whether or not assertions are enabled.
+            assertTrue( t instanceof IllegalArgumentException || t instanceof AssertionError);
+        }
+    }
+    
+    /**
+     * Test of new encrypt / decrypt methods added in ESAPI 2.0.
+     */
+    public void testNewEncryptDecrypt() {
+    	System.out.println("testNewEncryptDecrypt()");
+    	try {
+
+    	    // Let's try it with a 2-key version of 3DES. This should work for all
+    	    // installations, whereas the 3-key Triple DES will only work for those
+    	    // who have the Unlimited Strength Jurisdiction Policy files installed.
+			runNewEncryptDecryptTestCase("DESede/CBC/PKCS5Padding", 112, "1234567890".getBytes("UTF-8"));
+			runNewEncryptDecryptTestCase("DESede/CBC/NoPadding", 112, "12345678".getBytes("UTF-8"));
+			
+			runNewEncryptDecryptTestCase("DES/ECB/NoPadding", 56, "test1234".getBytes("UTF-8"));
+			
+	        runNewEncryptDecryptTestCase("AES/CBC/PKCS5Padding", 128, "Encrypt the world!".getBytes("UTF-8"));
+	        
+	        // These tests are only valid (and run) if one has the JCE Unlimited
+	        // Strength Jurisdiction Policy files installed for this Java VM.
+	            // 256-bit AES
+            runNewEncryptDecryptTestCase("AES/ECB/NoPadding", 256, "test1234test1234".getBytes("UTF-8"));
+                // 168-bit (aka, 3-key) Triple DES
+            runNewEncryptDecryptTestCase("DESede/CBC/PKCS5Padding", 168, "Groucho's secret word".getBytes("UTF-8"));
+		} catch (UnsupportedEncodingException e) {
+			fail("OK, who stole UTF-8 encoding from the Java rt.jar ???");
+		}
+    	
+    }
+    
+    /**
+     * Helper method to test new encryption / decryption.
+     * @param cipherXform	Cipher transformation
+     * @param keySize	Size of key, in bits.
+     * @param plaintextBytes Byte array of plaintext.
+     * @return The base64-encoded IV+ciphertext (or just ciphertext if no IV) or
+     *         null if {@code keysize} is greater than 128 bits and unlimited
+     *         strength crypto is not available for this Java VM.
+     */
+    private String runNewEncryptDecryptTestCase(String cipherXform, int keySize, byte[] plaintextBytes) {
+    	System.out.println("New encrypt / decrypt: " + cipherXform);
+    	
+    	if ( keySize > 128 && !CryptoPolicy.isUnlimitedStrengthCryptoAvailable() ) {
+    	    System.out.println("Skipping test for cipher transformation " +
+    	                       cipherXform + " with key size of " + keySize +
+    	                       " bits because this requires JCE Unlimited Strength" +
+    	                       " Jurisdiction Policy files to be installed and they" +
+    	                       " are not.");
+    	    return null;
+    	}
+
+    	try {
+    		// Generate an appropriate random secret key
+			SecretKey skey = CryptoHelper.generateSecretKey(cipherXform, keySize);	
+			assertTrue( skey.getAlgorithm().equals(cipherXform.split("/")[0]) );
+			String cipherAlg = cipherXform.split("/")[0];
+			
+			// Adjust key size for DES and DESede specific oddities.
+			// NOTE: Key size that encrypt() method is using is 192 bits!!!
+    		//        which is 3 times 64 bits, but DES key size is only 56 bits.
+    		// See 'IMPORTANT NOTE', in JavaEncryptor, near line 376. It's a "feature"!!!
+			if ( cipherAlg.equals( "DESede" ) ) {
+				keySize = 192;
+			} else if ( cipherAlg.equals( "DES" ) ) {
+				keySize = 64;
+			} // Else... use specified keySize.
+			assertTrue( (keySize / 8) == skey.getEncoded().length );
+//			System.out.println("testNewEncryptDecrypt(): Skey length (bits) = " + 8 * skey.getEncoded().length);
+
+			// Change to a possibly different cipher. This is kludgey at best. Am thinking about an
+			// alternate way to do this using a new 'CryptoControls' class. Maybe not until release 2.1.
+			// Change the cipher transform from whatever it currently is to the specified cipherXform.
+	    	@SuppressWarnings("deprecation")
+			String oldCipherXform = ESAPI.securityConfiguration().setCipherTransformation(cipherXform);
+	    	if ( ! cipherXform.equals(oldCipherXform) ) {
+	    		System.out.println("Cipher xform changed from \"" + oldCipherXform + "\" to \"" + cipherXform + "\"");
+	    	}
+	    	
+	    	// Get an Encryptor instance with the specified, possibly new, cipher transformation.
+	    	Encryptor instance = ESAPI.encryptor();
+	    	PlainText plaintext = new PlainText(plaintextBytes);
+	    	PlainText origPlainText = new PlainText( plaintext.toString() ); // Make _copy_ of original for comparison.
+	    	
+	    	// Do the encryption with the new encrypt() method and get back the CipherText.
+	    	CipherText ciphertext = instance.encrypt(skey, plaintext);	// The new encrypt() method.
+	    	System.out.println("DEBUG: Encrypt(): CipherText object is -- " + ciphertext);
+	    	assertTrue( ciphertext != null );
+//	    	System.out.println("DEBUG: After encryption: base64-encoded IV+ciphertext: " + ciphertext.getEncodedIVCipherText());
+//	    	System.out.println("\t\tOr... " + ESAPI.encoder().decodeFromBase64(ciphertext.getEncodedIVCipherText()) );
+//	    	System.out.println("DEBUG: After encryption: base64-encoded raw ciphertext: " + ciphertext.getBase64EncodedRawCipherText());
+//	    	System.out.println("\t\tOr... " + ESAPI.encoder().decodeFromBase64(ciphertext.getBase64EncodedRawCipherText()) );
+
+	    	// If we are supposed to have overwritten the plaintext, check this to see
+	    	// if origPlainText was indeed overwritten.
+			boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText();
+			if ( overwritePlaintext ) {
+				assertTrue( isPlaintextOverwritten(plaintext) );
+			}
+	    	
+	    	// Take the resulting ciphertext and decrypt w/ new decryption method.
+	    	PlainText decryptedPlaintext  = instance.decrypt(skey, ciphertext);		// The new decrypt() method.
+	    	
+	    	// Make sure we got back the same thing we started with.
+	    	System.out.println("\tOriginal plaintext: " + origPlainText);
+	    	System.out.println("\tResult after decryption: " + decryptedPlaintext);
+			assertTrue( "Failed to decrypt properly.", origPlainText.toString().equals( decryptedPlaintext.toString() ) );
+	    	
+	    	// Restore the previous cipher transformation. For now, this is only way to do this.
+	    	@SuppressWarnings("deprecation")
+			String previousCipherXform = ESAPI.securityConfiguration().setCipherTransformation(null);
+	    	assertTrue( previousCipherXform.equals( cipherXform ) );
+	    	String defaultCipherXform = ESAPI.securityConfiguration().getCipherTransformation();
+	    	assertTrue( defaultCipherXform.equals( oldCipherXform ) );
+	    	
+	    	return ciphertext.getEncodedIVCipherText();
+		} catch (Exception e) {
+			// OK if not counted toward code coverage.
+			System.out.println("testNewEncryptDecrypt(): Caught unexpected exception: " + e.getClass().getName());
+			e.printStackTrace(System.out);
+			fail("Caught unexpected exception; msg was: " + e);
+		}
+		return null;
+    }
+    
+    private static boolean isPlaintextOverwritten(PlainText plaintext) {
+    	// Note: An assumption here that the original plaintext did not consist
+    	// entirely of all '*' characters.
+    	byte[] ptBytes = plaintext.asBytes();
+    	
+    	for ( int i = 0; i < ptBytes.length; i++ ) {
+    		if ( ptBytes[i] != '*' ) {
+    			return false;
+    		}
+    	}
+    	return true;
+    }
+    
+    // TODO - Because none of the encryption / decryption tests persists
+    //		  encrypted data across runs, that means everything is run
+    //		  under same JVM at same time thus always with the same
+    //		  _native_ byte encoding.
+    //
+    //		  Need test(s) such that data is persisted across JVM runs
+    //		  so we can test a run on (say) a Windows Intel box can decrypt
+    //		  encrypted data produced by the reference Encryptor on
+    //		  (say) a Solaris SPARC box. I.e., test that the change to
+    //		  JavaEncryptor to use UTF-8 encoding throughout works as
+    //		  desired.
+    //
+    //		  Files saved across tests need to be added to SVN (under
+    //		  resources or where) and they should be named so we know
+    //		  where and how they were created. E.g., WinOS-AES-ECB.dat,
+    //		  Sparc-Solaris-AEC-CBC-PKCS5Padding.dat, etc., but they should be
+    //		  able to be decrypted from any platform. May wish to place that
+    //		  under a separate JUnit test.
+    //
+    // TODO - Need to test rainy day paths of new encrypt / decrypt so can
+    //		  verify that exception handling working OK, etc. Maybe also in
+    //		  a separate JUnit test, since everything here seems to be sunny
+    //		  day path. (Note: Some of this no in new test case,
+    //		  org.owasp.esapi.crypto.ESAPICryptoMACByPassTest.)
+    //
+    //				-kevin wall
+    
+
+    /**
+	 * Test of sign method, of class org.owasp.esapi.Encryptor.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+    public void testSign() throws EncryptionException {
+        System.out.println("testSign()");        
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = ESAPI.randomizer().getRandomString( 32, EncoderConstants.CHAR_ALPHANUMERICS );
+        String signature = instance.sign(plaintext);
+        assertTrue( instance.verifySignature( signature, plaintext ) );
+        assertFalse( instance.verifySignature( signature, "ridiculous" ) );
+        assertFalse( instance.verifySignature( "ridiculous", plaintext ) );
+    }
+
+    /**
+	 * Test of verifySignature method, of class org.owasp.esapi.Encryptor.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+    public void testVerifySignature() throws EncryptionException {
+        System.out.println("testVerifySignature()");
+        Encryptor instance = ESAPI.encryptor();
+        String plaintext = ESAPI.randomizer().getRandomString( 32, EncoderConstants.CHAR_ALPHANUMERICS );
+        String signature = instance.sign(plaintext);
+        assertTrue( instance.verifySignature( signature, plaintext ) );
+    }
+    
+ 
+    /**
+	 * Test of seal method, of class org.owasp.esapi.Encryptor.
+	 * 
+     * @throws IntegrityException
+	 */
+    public void testSeal() throws IntegrityException {
+        System.out.println("testSeal()");
+        Encryptor instance = ESAPI.encryptor(); 
+        String plaintext = ESAPI.randomizer().getRandomString( 32, EncoderConstants.CHAR_ALPHANUMERICS );
+        String seal = instance.seal( plaintext, instance.getTimeStamp() + 1000*60 );
+        instance.verifySeal( seal );
+        
+        int progressMark = 1;
+        boolean caughtExpectedEx = false;
+        try {
+            seal = instance.seal("", instance.getTimeStamp() + 1000*60);
+            progressMark++;
+            instance.verifySeal(seal);
+            progressMark++;
+        } catch(Exception e) {
+            fail("Failed empty string test: " + e + "; progress mark = " + progressMark);
+        }
+        try {
+            seal = instance.seal(null, instance.getTimeStamp() + 1000*60);
+            fail("Did not throw expected IllegalArgumentException");
+        } catch(IllegalArgumentException e) {
+            caughtExpectedEx = true;
+        } catch(Exception e) {
+            fail("Failed null string test; did not get expected IllegalArgumentException: " + e);
+        }
+        assertTrue(caughtExpectedEx);
+        
+        try {
+            seal = instance.seal("test", 0);
+            progressMark++;
+            // instance.verifySeal(seal);
+            progressMark++;
+        } catch(Exception e) {
+            fail("Fail test with 0 timestamp: " + e + "; progress mark = " + progressMark);
+        }
+        try {
+            seal = instance.seal("test", -1);
+            progressMark++;
+            // instance.verifySeal(seal);
+            progressMark++;
+        } catch(Exception e) {
+            fail("Fail test with -1 timestamp: " + e + "; progress mark = " + progressMark);
+        }
+    }
+
+    /**
+	 * Test of verifySeal method, of class org.owasp.esapi.Encryptor.
+	 * 
+     * @throws EnterpriseSecurityException
+	 */
+    public void testVerifySeal() throws EnterpriseSecurityException {
+        final int NSEC = 5;
+        System.out.println("testVerifySeal()");
+        Encryptor instance = ESAPI.encryptor(); 
+        String plaintext = "ridiculous:with:delimiters";    // Should now work w/ : (issue #28)
+        String seal = instance.seal( plaintext, instance.getRelativeTimeStamp( 1000 * NSEC ) );
+        try {
+        	assertNotNull("Encryptor.seal() returned null", seal );
+        	assertTrue("Failed to verify seal", instance.verifySeal( seal ) );
+        } catch ( Exception e ) {
+        	fail();
+        }
+        int progressMark = 1;
+        try {
+            // NOTE: I regrouped these all into a single try / catch since they
+            //       all test the same thing. Hence if one fails, they all should.
+            //       Also changed these tests so they no longer depend on the
+            //       deprecated encrypt() methods. IMO, *all these multiple
+            //       similar tests are not really required*, as they all are more
+            //       or less testing the same thing.
+            //                                              -kevin wall
+            // ================================================================
+            // Try to validate some invalid seals.
+            //
+            // All these should return false and log a warning with an Exception stack
+            // trace caused by an EncryptionException indicating "Invalid seal".
+        	assertFalse( instance.verifySeal( plaintext ) );
+        	progressMark++;      	
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;            
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(100 + ":" + plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":" + plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":random:" + plaintext) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":random:" + plaintext+ ":badsig")  ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+            assertFalse( instance.verifySeal( instance.encrypt( new PlainText(Long.MAX_VALUE + ":random:" + plaintext + ":"+ instance.sign( Long.MAX_VALUE + ":random:" + plaintext) ) ).getBase64EncodedRawCipherText() ) );
+            progressMark++;
+        } catch ( Exception e ) {
+        	// fail("Failed invalid seal test # " + progressMark + " to verify seal.");
+            System.err.println("Failed seal verification at step # " + progressMark);
+            System.err.println("Exception was: " + e);
+            e.printStackTrace(System.err);
+        }
+        
+        try {
+            Thread.sleep(1000 * (NSEC + 1) );
+                // Seal now past expiration date.
+            assertFalse( instance.verifySeal( seal ) );
+        } catch ( Exception e ) {
+            fail("Failed expired seal test. Seal should be expired.");
+        }
+    }
+        
+
+    @SuppressWarnings("deprecation")
+    public void testEncryptionSerialization() throws EncryptionException {
+        String secretMsg = "Secret Message";
+        ESAPI.securityConfiguration().setCipherTransformation("AES/CBC/PKCS5Padding");
+        CipherText ct = ESAPI.encryptor().encrypt(new PlainText(secretMsg));
+        
+        byte[] serializedCipherText = ct.asPortableSerializedByteArray();
+        
+        PlainText plainText = ESAPI.encryptor().decrypt(
+                                CipherText.fromPortableSerializedBytes(serializedCipherText) );
+        
+        assertTrue( secretMsg.equals( plainText.toString() ) );
+    }
+    
+    /**
+     * Test of main method, of class org.owasp.esapi.Encryptor. Must be done by
+     * visual inspection for now. (Needs improvement.)
+     * @throws Exception
+     */
+    public void testMain() throws Exception {
+        System.out.println("testMain(): Encryptor Main with '-print' argument.");
+        String[] args = {};
+        JavaEncryptor.main( args );        
+        String[] args1 = {"-print"};
+        // TODO:
+        // It probably would be a better if System.out were changed to be
+        // a file or a byte stream so that the output could be slurped up
+        // and checked against (at least some of) the expected output.
+        // Left as an exercise to some future, ambitious ESAPI developer. ;-)
+        JavaEncryptor.main( args1 );        
+    }    
+}
diff --git a/src/test/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedPropertiesTest.java b/src/test/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedPropertiesTest.java
new file mode 100644
index 0000000..89eb563
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedPropertiesTest.java
@@ -0,0 +1,535 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.reference.crypto;
+
+import java.io.*;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.errors.EncryptionException;
+import org.owasp.esapi.errors.EncryptionRuntimeException;
+
+/**
+ * The Class EncryptedPropertiesTest.
+ * 
+ * @author August Detlefsen (augustd at codemagi dot com)
+ *         <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
+ * @since October 8, 2010
+ */
+public class ReferenceEncryptedPropertiesTest extends TestCase {
+
+	/**
+	 * Instantiates a new encrypted properties test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+	public ReferenceEncryptedPropertiesTest(String testName) {
+		super(testName);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void setUp() throws Exception {
+		// none
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	protected void tearDown() throws Exception {
+		// none
+	}
+
+	/**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ReferenceEncryptedPropertiesTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * Test of getProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testGetProperty() throws EncryptionException {
+		System.out.println("getProperty");
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		assertNull(instance.getProperty("ridiculous"));
+	}
+
+	/**
+	 * Test of setProperty method, of class org.owasp.esapi.EncryptedProperties.
+	 * 
+	 * @throws EncryptionException
+	 *             the encryption exception
+	 */
+	public void testSetProperty() throws EncryptionException {
+		System.out.println("setProperty");
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		String name = "name";
+		String value = "value";
+		instance.setProperty(name, value);
+		String result = instance.getProperty(name);
+		assertEquals(value, result);
+		
+        instance.setProperty(name, "");
+        result = instance.getProperty(name);
+        assertEquals(result, "");
+        
+        try {
+            instance.setProperty(null, value);
+            fail("testSetProperty(): Null property name did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionRuntimeException );
+        }
+        try {
+            instance.setProperty(name, null);
+            fail("testSetProperty(): Null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof EncryptionRuntimeException );
+        }
+		try {
+			instance.setProperty(null, null);			
+			fail("testSetProperty(): Null property name and valud did not result in expected exception.");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof EncryptionRuntimeException );
+		}
+	}
+
+	/**
+	 * Test the behavior when the requested key does not exist.
+	 */
+	public void testNonExistantKeyValue() throws Exception
+	{
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		assertNull(instance.getProperty("not.there"));
+	}
+
+	/**
+	 * Test of keySet method, of class org.owasp.esapi.EncryptedProperties.
+	 */
+	public void testKeySet() throws Exception
+	{
+		boolean sawTwo = false;
+		boolean sawOne = false;
+
+		System.out.println("keySet");
+		ReferenceEncryptedProperties instance = new ReferenceEncryptedProperties();
+		instance.setProperty("one", "two");
+		instance.setProperty("two", "three");
+		Iterator i = instance.keySet().iterator();
+		while(i.hasNext())
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+					sawOne = true;
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+					sawTwo = true;
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test storing and loading of encrypted properties.
+	 */
+	public void testStoreLoad() throws Exception
+	{
+		ReferenceEncryptedProperties toLoad = new ReferenceEncryptedProperties();
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		ByteArrayInputStream bais;
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+	    ReferenceEncryptedProperties toStore = new ReferenceEncryptedProperties();
+		toStore.setProperty("one", "two");
+		toStore.setProperty("two", "three");
+		toStore.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+		toStore.store(baos, "testStore");
+
+		bais = new ByteArrayInputStream(baos.toByteArray());
+		toLoad.load(bais);
+
+		for(Iterator i=toLoad.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", toLoad.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", toLoad.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 toStore.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test storing and loading of encrypted properties.
+	 */
+	public void testStoreLoadWithReader() throws Exception
+	{
+/*
+		//create an EncryptedProperties to store
+		ReferenceEncryptedProperties toStore = new ReferenceEncryptedProperties();
+		toStore.setProperty("one", "two");
+		toStore.setProperty("two", "three");
+		toStore.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		//store properties to a Writer
+		CharArrayWriter writer = new CharArrayWriter();
+		//toStore.store(writer, "testStore");
+
+		//read it back in from a Reader
+		Reader reader = new CharArrayReader(writer.toCharArray());
+
+		ReferenceEncryptedProperties toLoad = new ReferenceEncryptedProperties();
+		toLoad.load(reader);
+
+		//test the resulting loaded properties
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+		for(Iterator i=toLoad.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", toLoad.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", toLoad.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 toStore.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+*/
+	}
+
+	/**
+	 * Test overridden put method.
+	 */
+	public void testPut() throws Exception
+	{
+		ReferenceEncryptedProperties props = new ReferenceEncryptedProperties();
+
+		String name = "name";
+		String value = "value";
+
+		props.put(name, value);  //should work and store encrypted
+		String result = props.getProperty(name);
+		assertEquals(value, result);
+
+		Integer five = new Integer(5);
+
+		try {
+			props.put("Integer", five); //should fail and throw IllegalArgumentException
+			fail("testPut(): Non-String property value did not result in expected exception.");
+		} catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+			props.put(five, "Integer"); //should fail and throw IllegalArgumentException
+			fail("testPut(): Non-String property key did not result in expected exception.");
+		} catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+			props.put(five, five); //should fail and throw IllegalArgumentException
+			fail("testPut(): Non-String property key and value did not result in expected exception.");
+		} catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+            props.put(null, five);
+            fail("testSetProperty(): Null property name and non-String value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+        try {
+            props.put(five, null);
+            fail("testSetProperty(): Non-String key and null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+            props.put(null, value);
+            fail("testSetProperty(): Null property name did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+        try {
+            props.put(name, null);
+            fail("testSetProperty(): Null property value did not result in expected exception.");
+        } catch( Exception e ) {
+            assertTrue( e instanceof IllegalArgumentException );
+        }
+		try {
+			props.put(null, null);
+			fail("testSetProperty(): Null property name and valud did not result in expected exception.");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof IllegalArgumentException );
+		}
+	}
+
+	/**
+	 * Test that ReferenceEncryptedProperties can be properly constructed
+	 * with an instance of Properties.
+	 */
+	public void testConstructWithProperties() {
+		Properties props = new Properties();
+		props.setProperty("one", "two");
+		props.setProperty("two", "three");
+		props.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		ReferenceEncryptedProperties eProps = new ReferenceEncryptedProperties(props);
+
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+		for(Iterator i=eProps.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", eProps.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", eProps.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 eProps.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+	/**
+	 * Test that ReferenceEncryptedProperties can be properly constructed
+	 * with an instance of EncryptedProperties.
+	 */
+	public void testConstructWithEncryptedProperties() throws Exception {
+		ReferenceEncryptedProperties props = new ReferenceEncryptedProperties();
+		props.setProperty("one", "two");
+		props.setProperty("two", "three");
+		props.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		ReferenceEncryptedProperties eProps = new ReferenceEncryptedProperties(props);
+
+		boolean sawOne = false;
+		boolean sawTwo = false;
+		boolean sawSeuss = false;
+
+		for(Iterator i=eProps.keySet().iterator();i.hasNext();)
+		{
+			String key = (String)i.next();
+
+			assertNotNull("key returned from keySet() iterator was null", key);
+			if(key.equals("one"))
+				if(sawOne)
+					fail("Key one seen more than once.");
+				else
+				{
+					sawOne = true;
+					assertEquals("Key one's value was not two", "two", eProps.getProperty("one"));
+				}
+			else if(key.equals("two"))
+				if(sawTwo)
+					fail("Key two seen more than once.");
+				else
+				{
+					sawTwo = true;
+					assertEquals("Key two's value was not three", "three", eProps.getProperty("two"));
+				}
+	         else if(key.equals("seuss.schneier"))
+	                if(sawSeuss)
+	                    fail("Key seuss.schneier seen more than once.");
+	                else
+	                {
+	                    sawSeuss = true;
+	                    assertEquals("Key seuss.schneier's value was not expected value",
+	                                 "one fish, twofish, red fish, blowfish",
+	                                 eProps.getProperty("seuss.schneier"));
+	                }
+			else
+				fail("Unset key " + key + " returned from keySet().iterator()");
+		}
+		assertTrue("Key one was never seen", sawOne);
+		assertTrue("Key two was never seen", sawTwo);
+	}
+
+
+	/**
+	 * Test overridden methods from Properties and Hashtable.
+	 */
+	public void testOverriddenMethods() throws Exception {
+		Properties props = new ReferenceEncryptedProperties();
+		props.setProperty("one", "two");
+		props.setProperty("two", "three");
+		props.setProperty("seuss.schneier", "one fish, twofish, red fish, blowfish");
+
+		FileOutputStream out = new FileOutputStream("ReferenceEncryptedProperties.test.txt");
+		PrintStream ps = new PrintStream(out);
+		try {
+			props.list(ps);
+			fail("testOverriddenMethods(): list(PrintStream) did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		PrintWriter pw = new PrintWriter(new FileWriter("test.out"));
+		try {
+			props.list(pw);
+			fail("testOverriddenMethods(): list(PrintWriter) did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			props.list(ps);
+			fail("testOverriddenMethods(): list(PrintStream) did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			Collection c = props.values();
+			fail("testOverriddenMethods(): values() did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			Collection c = props.entrySet();
+			fail("testOverriddenMethods(): entrySet() did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+		try {
+			Enumeration e = props.elements();
+			fail("testOverriddenMethods(): elements() did not result in expected Exception");
+		} catch( Exception e ) {
+		    assertTrue( e instanceof UnsupportedOperationException );
+		}
+
+        File f1 = new File("test.out");
+        f1.delete();
+        File f = new File("ReferenceEncryptedProperties.test.txt");
+        f.delete();
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/reference/validation/.svn/all-wcprops b/src/test/java/org/owasp/esapi/reference/validation/.svn/all-wcprops
new file mode 100644
index 0000000..809c10b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/validation/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/validation
+END
+StringValidationRuleTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/reference/validation/.svn/entries b/src/test/java/org/owasp/esapi/reference/validation/.svn/entries
new file mode 100644
index 0000000..2baa841
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/validation/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/reference/validation
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-02-10T03:49:35.678752Z
+1138
+brent.shikoski at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+StringValidationRuleTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.385955Z
+1e89dbf5662a4d7dcf735b65e9ad49a2
+2010-02-10T03:49:35.678752Z
+1138
+brent.shikoski at gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5022
+

diff --git a/src/test/java/org/owasp/esapi/reference/validation/.svn/text-base/StringValidationRuleTest.java.svn-base b/src/test/java/org/owasp/esapi/reference/validation/.svn/text-base/StringValidationRuleTest.java.svn-base
new file mode 100644
index 0000000..9ea806e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/validation/.svn/text-base/StringValidationRuleTest.java.svn-base
@@ -0,0 +1,158 @@
+package org.owasp.esapi.reference.validation;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.errors.ValidationException;
+
+public class StringValidationRuleTest {
+
+	@Test
+	public void testWhitelistPattern() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("Alphabetic");
+		
+		Assert.assertEquals("Magnum44", validationRule.getValid("", "Magnum44"));
+		validationRule.addWhitelistPattern("^[a-zA-Z]*");
+		try {
+			validationRule.getValid("", "Magnum44");
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (ValidationException ve) {
+			Assert.assertNotNull(ve.getMessage());
+		}
+		Assert.assertEquals("MagnumPI", validationRule.getValid("", "MagnumPI"));
+		
+	}
+	
+	@Test
+	public void testWhitelistPattern_Invalid() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		//null white list patterns throw IllegalArgumentException
+		try {
+			String pattern = null;
+			validationRule.addWhitelistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		try {
+			java.util.regex.Pattern pattern = null;
+			validationRule.addWhitelistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		//invalid white list patterns throw PatternSyntaxException
+		try {
+			String pattern = "_][0}[";
+			validationRule.addWhitelistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+	}
+	
+	@Test
+	public void testWhitelist() {
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		char[] whitelistArray = new char[] {'a', 'b', 'c'};
+		Assert.assertEquals("abc", validationRule.whitelist("12345abcdef", whitelistArray));
+	}
+	
+	@Test
+	public void testBlacklistPattern() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("NoAngleBrackets");
+		
+		Assert.assertEquals("beg <script> end", validationRule.getValid("", "beg <script> end"));
+		validationRule.addBlacklistPattern("^.*(<|>).*");
+		try {
+			validationRule.getValid("", "beg <script> end");
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (ValidationException ve) {
+			Assert.assertNotNull(ve.getMessage());
+		}
+		Assert.assertEquals("beg script end", validationRule.getValid("", "beg script end"));
+	}
+	
+	@Test
+	public void testBlacklistPattern_Invalid() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		//null black list patterns throw IllegalArgumentException
+		try {
+			String pattern = null;
+			validationRule.addBlacklistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		try {
+			java.util.regex.Pattern pattern = null;
+			validationRule.addBlacklistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		//invalid black list patterns throw PatternSyntaxException
+		try {
+			String pattern = "_][0}[";
+			validationRule.addBlacklistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+	}	
+	
+	@Test
+	public void testCheckLengths() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("Max12_Min2");
+		validationRule.setMinimumLength(2);
+		validationRule.setMaximumLength(12);
+		
+		Assert.assertTrue(validationRule.isValid("", "12"));
+		Assert.assertTrue(validationRule.isValid("", "123456"));
+		Assert.assertTrue(validationRule.isValid("", "ABCDEFGHIJKL"));
+		
+		Assert.assertFalse(validationRule.isValid("", "1"));
+		Assert.assertFalse(validationRule.isValid("", "ABCDEFGHIJKLM"));
+		
+		ValidationErrorList errorList = new ValidationErrorList();
+		Assert.assertEquals("1234567890", validationRule.getValid("", "1234567890", errorList));
+		Assert.assertEquals(0, errorList.size());
+		Assert.assertEquals(null, validationRule.getValid("", "123456789012345", errorList));
+		Assert.assertEquals(1, errorList.size());
+	}
+	
+	@Test
+	public void testAllowNull() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		Assert.assertFalse(validationRule.isAllowNull());
+		Assert.assertFalse(validationRule.isValid("", null));
+		
+		validationRule.setAllowNull(true);
+		Assert.assertTrue(validationRule.isAllowNull());
+		Assert.assertTrue(validationRule.isValid("", null));
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java b/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java
new file mode 100644
index 0000000..9ea806e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java
@@ -0,0 +1,158 @@
+package org.owasp.esapi.reference.validation;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.owasp.esapi.ValidationErrorList;
+import org.owasp.esapi.errors.ValidationException;
+
+public class StringValidationRuleTest {
+
+	@Test
+	public void testWhitelistPattern() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("Alphabetic");
+		
+		Assert.assertEquals("Magnum44", validationRule.getValid("", "Magnum44"));
+		validationRule.addWhitelistPattern("^[a-zA-Z]*");
+		try {
+			validationRule.getValid("", "Magnum44");
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (ValidationException ve) {
+			Assert.assertNotNull(ve.getMessage());
+		}
+		Assert.assertEquals("MagnumPI", validationRule.getValid("", "MagnumPI"));
+		
+	}
+	
+	@Test
+	public void testWhitelistPattern_Invalid() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		//null white list patterns throw IllegalArgumentException
+		try {
+			String pattern = null;
+			validationRule.addWhitelistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		try {
+			java.util.regex.Pattern pattern = null;
+			validationRule.addWhitelistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		//invalid white list patterns throw PatternSyntaxException
+		try {
+			String pattern = "_][0}[";
+			validationRule.addWhitelistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+	}
+	
+	@Test
+	public void testWhitelist() {
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		char[] whitelistArray = new char[] {'a', 'b', 'c'};
+		Assert.assertEquals("abc", validationRule.whitelist("12345abcdef", whitelistArray));
+	}
+	
+	@Test
+	public void testBlacklistPattern() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("NoAngleBrackets");
+		
+		Assert.assertEquals("beg <script> end", validationRule.getValid("", "beg <script> end"));
+		validationRule.addBlacklistPattern("^.*(<|>).*");
+		try {
+			validationRule.getValid("", "beg <script> end");
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (ValidationException ve) {
+			Assert.assertNotNull(ve.getMessage());
+		}
+		Assert.assertEquals("beg script end", validationRule.getValid("", "beg script end"));
+	}
+	
+	@Test
+	public void testBlacklistPattern_Invalid() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		//null black list patterns throw IllegalArgumentException
+		try {
+			String pattern = null;
+			validationRule.addBlacklistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		try {
+			java.util.regex.Pattern pattern = null;
+			validationRule.addBlacklistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+		
+		//invalid black list patterns throw PatternSyntaxException
+		try {
+			String pattern = "_][0}[";
+			validationRule.addBlacklistPattern(pattern);
+			Assert.fail("Expected Exception not thrown");
+		}
+		catch (IllegalArgumentException ie) {
+			Assert.assertNotNull(ie.getMessage());
+		}
+	}	
+	
+	@Test
+	public void testCheckLengths() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("Max12_Min2");
+		validationRule.setMinimumLength(2);
+		validationRule.setMaximumLength(12);
+		
+		Assert.assertTrue(validationRule.isValid("", "12"));
+		Assert.assertTrue(validationRule.isValid("", "123456"));
+		Assert.assertTrue(validationRule.isValid("", "ABCDEFGHIJKL"));
+		
+		Assert.assertFalse(validationRule.isValid("", "1"));
+		Assert.assertFalse(validationRule.isValid("", "ABCDEFGHIJKLM"));
+		
+		ValidationErrorList errorList = new ValidationErrorList();
+		Assert.assertEquals("1234567890", validationRule.getValid("", "1234567890", errorList));
+		Assert.assertEquals(0, errorList.size());
+		Assert.assertEquals(null, validationRule.getValid("", "123456789012345", errorList));
+		Assert.assertEquals(1, errorList.size());
+	}
+	
+	@Test
+	public void testAllowNull() throws ValidationException {
+		
+		StringValidationRule validationRule = new StringValidationRule("");
+		
+		Assert.assertFalse(validationRule.isAllowNull());
+		Assert.assertFalse(validationRule.isValid("", null));
+		
+		validationRule.setAllowNull(true);
+		Assert.assertTrue(validationRule.isAllowNull());
+		Assert.assertTrue(validationRule.isValid("", null));
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/util/.svn/all-wcprops b/src/test/java/org/owasp/esapi/util/.svn/all-wcprops
new file mode 100644
index 0000000..ead7e5e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/util
+END
+ThePrefectClass.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/util/ThePrefectClass.java
+END
+FileTestUtils.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/util/FileTestUtils.java
+END
+ObjFactoryTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java
+END
+ByteConversionUtilTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/util/ByteConversionUtilTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/util/.svn/entries b/src/test/java/org/owasp/esapi/util/.svn/entries
new file mode 100644
index 0000000..20d65d5
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/entries
@@ -0,0 +1,164 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/util
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2010-06-08T04:39:48.001264Z
+1437
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+ThePrefectClass.java
+file
+
+
+
+
+2014-02-18T16:19:52.281953Z
+fe99ee0775afe4a4b55c6ea3d18d9d3e
+2009-10-24T22:32:22.875542Z
+722
+manico.james
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+467
+

+FileTestUtils.java
+file
+
+
+
+
+2014-02-18T16:19:52.281953Z
+b4d222d553446dc1f0cc8a395d5bf862
+2010-01-28T02:06:33.599473Z
+1011
+schallee at darkmist.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6643
+

+ObjFactoryTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.281953Z
+d338f47b88d822b5217551dc1d5b757f
+2010-06-08T04:39:48.001264Z
+1437
+chrisisbeef
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7263
+

+ByteConversionUtilTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.281953Z
+960cefa0dc1e179e11bb792ecfda361d
+2010-01-24T05:31:52.691830Z
+994
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3042
+

diff --git a/src/test/java/org/owasp/esapi/util/.svn/prop-base/ByteConversionUtilTest.java.svn-base b/src/test/java/org/owasp/esapi/util/.svn/prop-base/ByteConversionUtilTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/prop-base/ByteConversionUtilTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/util/.svn/prop-base/ObjFactoryTest.java.svn-base b/src/test/java/org/owasp/esapi/util/.svn/prop-base/ObjFactoryTest.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/prop-base/ObjFactoryTest.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/util/.svn/prop-base/ThePrefectClass.java.svn-base b/src/test/java/org/owasp/esapi/util/.svn/prop-base/ThePrefectClass.java.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/prop-base/ThePrefectClass.java.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/test/java/org/owasp/esapi/util/.svn/text-base/ByteConversionUtilTest.java.svn-base b/src/test/java/org/owasp/esapi/util/.svn/text-base/ByteConversionUtilTest.java.svn-base
new file mode 100644
index 0000000..fd87d5c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/text-base/ByteConversionUtilTest.java.svn-base
@@ -0,0 +1,86 @@
+package org.owasp.esapi.util;
+
+import static org.junit.Assert.*;
+import junit.framework.JUnit4TestAdapter;
+
+import org.junit.Test;
+import org.owasp.esapi.codecs.Hex;
+
+/** JUnit test for {@code ByteConversionUtil}. */
+public class ByteConversionUtilTest {
+
+    private static final String EOL = System.getProperty("line.separator", "\n");
+    private static final boolean VERBOSE = false;
+
+    /**
+     * Test conversion byte[] <--> short.
+     */
+    @Test
+    public void testShortConversion() {
+        debug("========== testShortConversion() ==========");
+        short[] testArray = { -1, 0, 1, Short.MIN_VALUE, Short.MAX_VALUE };
+
+        for(int i = 0; i < testArray.length; i++ ) {
+            byte[] bytes = ByteConversionUtil.fromShort(testArray[i]);
+            short n = ByteConversionUtil.toShort(bytes);
+            debug("i: " + i + ", value: " + testArray[i]);
+            debug("byte array: " + Hex.toHex(bytes, true));
+            debug("testArray[" + i + "]: " + Integer.toHexString(testArray[i]));
+            debug("n: " + Integer.toHexString(n) + EOL + "-----");
+            assertEquals(testArray[i], n);
+        }
+    }
+
+    /**
+     * Test conversion byte[] <--> int.
+     */
+    @Test
+    public void testIntConversion() {
+        debug("========== testIntConversion() ==========");
+        int[] testArray = { -1, 0, 1, Integer.MIN_VALUE, Integer.MAX_VALUE };
+
+        for(int i = 0; i < testArray.length; i++ ) {
+            byte[] bytes = ByteConversionUtil.fromInt(testArray[i]);
+            int n = ByteConversionUtil.toInt(bytes);
+            debug("i: " + i + ", value: " + testArray[i]);
+            debug("byte array: " + Hex.toHex(bytes, true));
+            debug("testArray[" + i + "]: " + Integer.toHexString(testArray[i]));
+            debug("n: " + Integer.toHexString(n) + EOL + "-----");
+            assertEquals(testArray[i], n);
+        }
+    }
+
+    /**
+     * Test conversion byte[] <--> long.
+     */
+    @Test
+    public void testLongConversion() {
+        debug("========== testLongConversion() ==========");
+        long[] testArray = { -1, 0, 1, Long.MIN_VALUE, Long.MAX_VALUE };
+
+        for(int i = 0; i < testArray.length; i++ ) {
+            byte[] bytes = ByteConversionUtil.fromLong(testArray[i]);
+            long n = ByteConversionUtil.toLong(bytes);
+            debug("i: " + i + ", value: " + testArray[i]);
+            debug("byte array: " + Hex.toHex(bytes, true));
+            debug("testArray[" + i + "]: " + Long.toHexString(testArray[i]));
+            debug("n: " + Long.toHexString(n) + EOL + "-----");
+            assertEquals(testArray[i], n);
+        }
+    }
+
+    /**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests} which
+     * uses a JUnit 3 test runner.
+     */
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(ByteConversionUtilTest.class);
+    }
+
+    private void debug(String msg) {
+        if ( VERBOSE ) {
+            System.err.println(msg);
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/util/.svn/text-base/FileTestUtils.java.svn-base b/src/test/java/org/owasp/esapi/util/.svn/text-base/FileTestUtils.java.svn-base
new file mode 100644
index 0000000..51f40f8
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/text-base/FileTestUtils.java.svn-base
@@ -0,0 +1,216 @@
+package org.owasp.esapi.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * Utilities to help with tests that involve files or directories.
+ */
+public class FileTestUtils
+{
+	private static final Class CLASS = FileTestUtils.class;
+	private static final String CLASS_NAME = CLASS.getName();
+	private static final String DEFAULT_PREFIX = CLASS_NAME + '.';
+	private static final String DEFAULT_SUFFIX = ".tmp";
+	private static final Random rand;
+
+	/*
+		Rational for switching from SecureRandom to Random:
+		
+		This is used for generating filenames for temporary
+		directories. Origionally this was using SecureRandom for
+		this to make /tmp races harder. This is not necessary as
+		mkdir always returns false if if the directory already
+		exists.
+		
+		Additionally, SecureRandom for some reason on linux
+		is appears to be reading from /dev/random instead of
+		/dev/urandom. As such, the many calls for temporary
+		directories in the unit tests quickly depleates the
+		entropy pool causing unit test runs to block until more
+		entropy is collected (this is why moving the mouse speeds
+		up unit tests).
+	*/
+	static
+	{
+		SecureRandom secRand = new SecureRandom();
+		rand = new Random(secRand.nextLong());
+	}
+
+	/** Private constructor as all methods are static. */
+	private FileTestUtils()
+	{
+	}
+
+	/**
+	 * Convert a long to it's hex representation. Unlike
+	 * {@ Long#toHexString(long)} this always returns 16 digits.
+	 * @param l The long to convert.
+	 * @return l in hex.
+	 */
+	public static String toHexString(long l)
+	{
+		String initial;
+		StringBuffer sb;
+
+		initial = Long.toHexString(l);
+		if(initial.length() == 16)
+			return initial;
+		sb = new StringBuffer(16);
+		sb.append(initial);
+		while(sb.length()<16)
+			sb.insert(0,'0');
+		return sb.toString();
+	}
+
+	/**
+	 * Create a temporary directory.
+	 * @param parent The parent directory for the temporary
+	 *	directory. If this is null, the system property
+	 * 	"java.io.tmpdir" is used.
+	 * @param prefix The prefix for the directory's name. If this
+	 * 	is null, the full class name of this class is used.
+	 * @param suffix The suffix for the directory's name. If this
+	 * 	is null, ".tmp" is used.
+	 * @return The newly created temporary directory.
+	 * @throws IOException if directory creation fails
+	 * @throws SecurityException if {@link File#mkdir()} throws one.
+	 */
+	public static File createTmpDirectory(File parent, String prefix, String suffix) throws IOException
+	{
+		String name;
+		File dir;
+
+		if(prefix == null)
+			prefix = DEFAULT_PREFIX;
+		else if(!prefix.endsWith("."))
+			prefix += '.';
+		if(suffix == null)
+			suffix = DEFAULT_SUFFIX;
+		else if(!suffix.startsWith("."))
+			suffix = "." + suffix;
+		if(parent == null)
+			parent = new File(System.getProperty("java.io.tmpdir"));
+		name = prefix + toHexString(rand.nextLong()) + suffix;
+		dir = new File(parent, name);
+		if(!dir.mkdir())
+			throw new IOException("Unable to create temporary directory " + dir);
+		return dir.getCanonicalFile();
+	}
+
+	/**
+	 * Create a temporary directory. This calls
+	 * {@link #createTmpDirectory(File, String, String)} with null
+	 * for parent and suffix.
+	 * @param prefix The prefix for the directory's name. If this
+	 * 	is null, the full class name of this class is used.
+	 * @return The newly created temporary directory.
+	 * @throws IOException if directory creation fails
+	 * @throws SecurityException if {@link File#mkdir()} throws one.
+	 */
+	public static File createTmpDirectory(String prefix) throws IOException
+	{
+		return createTmpDirectory(null, prefix, null);
+	}
+
+	/**
+	 * Create a temporary directory. This calls
+	 * {@link #createTmpDirectory(File, String, String)} with null
+	 * for all arguments.
+	 * @return The newly created temporary directory.
+	 * @throws IOException if directory creation fails
+	 * @throws SecurityException if {@link File#mkdir()} throws one.
+	 */
+	public static File createTmpDirectory() throws IOException
+	{
+	 	return createTmpDirectory(null,null,null);
+	}
+
+	/**
+	 * Checks that child is a directory and really a child of
+	 * parent. This verifies that the {@link File#getCanonicalFile()
+	 * canonical} child is actually a child of parent. This should
+	 * fail if the child is a symbolic link to another directory and
+	 * therefore should not be traversed in a recursive traversal of
+	 * a directory.
+	 * @param parent The supposed parent of the child
+	 * @param child The child to check
+	 * @return true if child is a directory and a direct decendant
+	 * 	of parent.
+	 * @throws IOException if {@link File#getCanonicalFile()} does
+	 * @throws NullPointerException if either parent or child
+	 * 	are null.
+	 */
+	public static boolean isChildSubDirectory(File parent, File child) throws IOException
+	{
+		File childsParent;
+
+		if(child==null)
+			throw new NullPointerException("child argument is null");
+		if(!child.isDirectory())
+			return false;
+		if(parent==null)
+			throw new NullPointerException("parent argument is null");
+		parent = parent.getCanonicalFile();
+		child = child.getCanonicalFile();
+		childsParent = child.getParentFile();
+		if(childsParent == null)
+			return false;	// sym link to /?
+		childsParent = childsParent.getCanonicalFile();	// just in case...
+		if(!parent.equals(childsParent))
+			return false;
+		return true;
+	}
+
+	/**
+	 * Delete a file. Unlinke {@link File#delete()}, this throws an
+	 * exception if deletion fails.
+	 * @param file The file to delete
+	 * @throws IOException if file is not null, exists but delete
+	 * 	fails.
+	 */
+	public static void delete(File file) throws IOException
+	{
+		if(file==null || !file.exists())
+			return;
+		if(!file.delete())
+			throw new IOException("Unable to delete file " + file.getAbsolutePath());
+	}
+
+	/**
+	 * Recursively delete a file. If file is a directory,
+	 * subdirectories and files are also deleted. Care is taken to
+	 * not traverse symbolic links in this process. A null file or
+	 * a file that does not exist is considered to already been
+	 * deleted.
+	 * @param file The file or directory to be deleted
+	 * @throws IOException if the file, or a descendant, cannot
+	 * 	be deleted.
+	 * @throws SecurityException if {@link File#delete()} does.
+	 */
+	public static void deleteRecursively(File file) throws IOException
+	{
+		File[] children;
+		File child;
+
+		if(file == null || !file.exists())
+			return;	// already deleted?
+		if(file.isDirectory())
+		{
+			children = file.listFiles();
+			for(int i=0;i<children.length;i++)
+			{
+				child = children[i];
+				if(isChildSubDirectory(file,child))
+					deleteRecursively(child);
+				else
+					delete(child);
+			}
+		}
+
+		// finally
+		delete(file);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/util/.svn/text-base/ObjFactoryTest.java.svn-base b/src/test/java/org/owasp/esapi/util/.svn/text-base/ObjFactoryTest.java.svn-base
new file mode 100644
index 0000000..2b81759
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/text-base/ObjFactoryTest.java.svn-base
@@ -0,0 +1,192 @@
+package org.owasp.esapi.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.security.Key;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.errors.ConfigurationException;
+
+public class ObjFactoryTest extends TestCase {
+	
+	// Purpose of this is to prevent a default, no-arg, public CTOR to be generated.
+	// We want to prevent this so we can use this class to test the case of where
+	// ObjectFactory<T>.make() throws an IllegalAccessException.
+	@SuppressWarnings("unused")
+	private ObjFactoryTest(int i) { ; }
+	
+    /**
+	 * Instantiates a new object factory test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public ObjFactoryTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(ObjFactoryTest.class);
+        return suite;
+    }
+    
+    /** Test that NullCipher object is correctly returned. */
+    public void testMakeNullCipher() throws ConfigurationException {
+    	String className = "javax.crypto.NullCipher";
+    	javax.crypto.Cipher nullCipher =
+    			ObjFactory.make(className, "NullCipher");
+    	assertTrue( nullCipher instanceof javax.crypto.NullCipher );
+    	System.out.println("W00t! Watch out NSA...we have a NullCipher and we're not afraid to use it!");
+    }
+    
+    /** Test that InstantiationException is thrown as the root cause when the
+     * specified class name is an abstract class or interface.
+     */
+    public void testInterface() throws ConfigurationException {
+    	Key key = null;  	
+    	try {
+    		key = ObjFactory.make("java.security.Key", "Key");
+    		assertFalse("Should not be reached - interface or abstract class", key != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof InstantiationException);
+    	}
+    }
+    
+    /** Test that IllegalAccessException is thrown as the root cause when the
+     *  specified class has no public, no-arg CTOR. Cipher has only a protected
+     *  CTOR that takes multiple parameters.
+     *  
+     *  FIXME: Need new test. This also throws an InstantiationException as the
+     *  root cause. The goal is to have it throw IllegalAccessException.
+     */
+    public void testMakeNoPublicConstructor() throws ConfigurationException {
+    	ObjFactoryTest oft = null;	
+    	try {
+    		// CHECKME: As I read
+			//	  http://java.sun.com/docs/books/tutorial/reflect/member/ctorTrouble.html
+    		// this should cause an IllegalAccessException to be thrown because it has no public,
+    		// no-arg CTOR. However, it doesn't. It throws a InstantiationException instead.
+    		oft = ObjFactory.make(ObjFactoryTest.class.getName(), "ObjectFactoryTest");
+    		assertFalse("Should not be reached - no public CTOR", oft != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		// assertTrue( cause instanceof IllegalAccessException);
+    		assertTrue( cause instanceof InstantiationException);
+    	}
+    }
+    
+    /** Test that ClassNotFoundException is thrown as the root cause when
+     * the class name to be created is not a class name that exists anywhere.
+     */
+    public void testMakeNoSuchClass() throws ConfigurationException {
+    	Object obj = null;
+    	
+    	try {
+    		obj = ObjFactory.make("kevin.wall.HasNoClass", "Object");
+    		assertFalse("Should not be reached - no such class", obj != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof ClassNotFoundException);
+    	}
+    }
+    
+    /** Test that ClassCastException is thrown as the root cause when the
+     * created class is not a subclass / does not implement the specified type.
+     * (In this case, String is not a subclass / does not implement Key.)
+     */
+    public void testMakeNotASubclass() throws ConfigurationException {
+    	Key key = null;
+    	try {
+    		key = ObjFactory.make("java.lang.String", "testMakeNotASubclass");
+    		assertFalse("Should not be reached - not a subclass", key != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		System.out.println("DEBUG: Cause was: " + cause.getClass().getName());
+    		assertTrue( cause instanceof ClassCastException);
+    	} catch(ClassCastException ccex) {
+    		assertTrue("Caught expected class cast exception", true);
+    	}
+    }
+    
+    /** Test that IllegalArgumentException is thrown as the cause when the
+     * class name is specified as an empty string.
+     */
+    public void testMakeEmptyClassName() throws ConfigurationException {
+    	Object obj = null;
+    	try {
+    		obj = ObjFactory.make("", "testMakeEmptyClassName");
+    		assertFalse("Should not be reached - not a subclass", obj != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof IllegalArgumentException);
+    	}
+    }
+    
+    /** Test that some other exception is thrown from the no-arg, public CTOR as the
+     * root cause. Had to use special external class here because strangely, this didn't
+     * work as an inner class. (Threw InstantiationException in that case instead.)
+     */
+    public void testMakeOtherException() throws ConfigurationException {
+    	@SuppressWarnings("unused")
+		ThePrefectClass ford = null;
+    	try {
+    		ford = ObjFactory.make("org.owasp.esapi.util.ThePrefectClass", "ThePrefectClass");
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+			assertTrue( cause instanceof UnsupportedOperationException);
+    	}
+    }
+    
+    /** Test case where typeName is null or empty string. */
+    public void testNullorEmptyTypeName() throws ConfigurationException {
+    	String className = "javax.crypto.NullCipher";
+    	javax.crypto.Cipher nullCipher =
+    			ObjFactory.make(className, null);
+    	assertTrue( nullCipher instanceof javax.crypto.NullCipher );
+    	nullCipher =
+			ObjFactory.make(className, "");
+    	assertTrue( nullCipher instanceof javax.crypto.NullCipher );
+    }
+    
+    /** Test case where no-arg CTOR does not exist. By all indications from
+     * Javadoc for {@code Class.newInstance()} one would think this should
+     * throw an {@code IllegalAccessException} because {@code SecretKeySpec}
+     * has two public CTORs that both take arguments. */
+    public void testMakeCipher() throws ConfigurationException {
+    	try {
+    		String className = "javax.crypto.spec.SecretKeySpec";
+    		javax.crypto.spec.SecretKeySpec skeySpec =
+    			(SecretKeySpec) ObjFactory.make(className, "SecretKeySpec");
+    		assertTrue( skeySpec != null );
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof InstantiationException);
+    	}
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/util/.svn/text-base/ThePrefectClass.java.svn-base b/src/test/java/org/owasp/esapi/util/.svn/text-base/ThePrefectClass.java.svn-base
new file mode 100644
index 0000000..28b0f33
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/.svn/text-base/ThePrefectClass.java.svn-base
@@ -0,0 +1,12 @@
+package org.owasp.esapi.util;
+
+// The "perfect" helper class for testing JUnit test, ObjFactoryTest.
+// For testing only. Doesn't work as an inner class in ObjFactoryTest.
+// Props to D. Adams for HG2G. RIP.
+public class ThePrefectClass {
+	private static final int lifeUniverseEverything = 42;
+	public ThePrefectClass() {
+		throw new UnsupportedOperationException("This public CTOR is not supported!");
+	}
+	public int getAnswer() { return lifeUniverseEverything; }
+}
diff --git a/src/test/java/org/owasp/esapi/util/ByteConversionUtilTest.java b/src/test/java/org/owasp/esapi/util/ByteConversionUtilTest.java
new file mode 100644
index 0000000..fd87d5c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/ByteConversionUtilTest.java
@@ -0,0 +1,86 @@
+package org.owasp.esapi.util;
+
+import static org.junit.Assert.*;
+import junit.framework.JUnit4TestAdapter;
+
+import org.junit.Test;
+import org.owasp.esapi.codecs.Hex;
+
+/** JUnit test for {@code ByteConversionUtil}. */
+public class ByteConversionUtilTest {
+
+    private static final String EOL = System.getProperty("line.separator", "\n");
+    private static final boolean VERBOSE = false;
+
+    /**
+     * Test conversion byte[] <--> short.
+     */
+    @Test
+    public void testShortConversion() {
+        debug("========== testShortConversion() ==========");
+        short[] testArray = { -1, 0, 1, Short.MIN_VALUE, Short.MAX_VALUE };
+
+        for(int i = 0; i < testArray.length; i++ ) {
+            byte[] bytes = ByteConversionUtil.fromShort(testArray[i]);
+            short n = ByteConversionUtil.toShort(bytes);
+            debug("i: " + i + ", value: " + testArray[i]);
+            debug("byte array: " + Hex.toHex(bytes, true));
+            debug("testArray[" + i + "]: " + Integer.toHexString(testArray[i]));
+            debug("n: " + Integer.toHexString(n) + EOL + "-----");
+            assertEquals(testArray[i], n);
+        }
+    }
+
+    /**
+     * Test conversion byte[] <--> int.
+     */
+    @Test
+    public void testIntConversion() {
+        debug("========== testIntConversion() ==========");
+        int[] testArray = { -1, 0, 1, Integer.MIN_VALUE, Integer.MAX_VALUE };
+
+        for(int i = 0; i < testArray.length; i++ ) {
+            byte[] bytes = ByteConversionUtil.fromInt(testArray[i]);
+            int n = ByteConversionUtil.toInt(bytes);
+            debug("i: " + i + ", value: " + testArray[i]);
+            debug("byte array: " + Hex.toHex(bytes, true));
+            debug("testArray[" + i + "]: " + Integer.toHexString(testArray[i]));
+            debug("n: " + Integer.toHexString(n) + EOL + "-----");
+            assertEquals(testArray[i], n);
+        }
+    }
+
+    /**
+     * Test conversion byte[] <--> long.
+     */
+    @Test
+    public void testLongConversion() {
+        debug("========== testLongConversion() ==========");
+        long[] testArray = { -1, 0, 1, Long.MIN_VALUE, Long.MAX_VALUE };
+
+        for(int i = 0; i < testArray.length; i++ ) {
+            byte[] bytes = ByteConversionUtil.fromLong(testArray[i]);
+            long n = ByteConversionUtil.toLong(bytes);
+            debug("i: " + i + ", value: " + testArray[i]);
+            debug("byte array: " + Hex.toHex(bytes, true));
+            debug("testArray[" + i + "]: " + Long.toHexString(testArray[i]));
+            debug("n: " + Long.toHexString(n) + EOL + "-----");
+            assertEquals(testArray[i], n);
+        }
+    }
+
+    /**
+     * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests} which
+     * uses a JUnit 3 test runner.
+     */
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(ByteConversionUtilTest.class);
+    }
+
+    private void debug(String msg) {
+        if ( VERBOSE ) {
+            System.err.println(msg);
+        }
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/util/FileTestUtils.java b/src/test/java/org/owasp/esapi/util/FileTestUtils.java
new file mode 100644
index 0000000..51f40f8
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/FileTestUtils.java
@@ -0,0 +1,216 @@
+package org.owasp.esapi.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * Utilities to help with tests that involve files or directories.
+ */
+public class FileTestUtils
+{
+	private static final Class CLASS = FileTestUtils.class;
+	private static final String CLASS_NAME = CLASS.getName();
+	private static final String DEFAULT_PREFIX = CLASS_NAME + '.';
+	private static final String DEFAULT_SUFFIX = ".tmp";
+	private static final Random rand;
+
+	/*
+		Rational for switching from SecureRandom to Random:
+		
+		This is used for generating filenames for temporary
+		directories. Origionally this was using SecureRandom for
+		this to make /tmp races harder. This is not necessary as
+		mkdir always returns false if if the directory already
+		exists.
+		
+		Additionally, SecureRandom for some reason on linux
+		is appears to be reading from /dev/random instead of
+		/dev/urandom. As such, the many calls for temporary
+		directories in the unit tests quickly depleates the
+		entropy pool causing unit test runs to block until more
+		entropy is collected (this is why moving the mouse speeds
+		up unit tests).
+	*/
+	static
+	{
+		SecureRandom secRand = new SecureRandom();
+		rand = new Random(secRand.nextLong());
+	}
+
+	/** Private constructor as all methods are static. */
+	private FileTestUtils()
+	{
+	}
+
+	/**
+	 * Convert a long to it's hex representation. Unlike
+	 * {@ Long#toHexString(long)} this always returns 16 digits.
+	 * @param l The long to convert.
+	 * @return l in hex.
+	 */
+	public static String toHexString(long l)
+	{
+		String initial;
+		StringBuffer sb;
+
+		initial = Long.toHexString(l);
+		if(initial.length() == 16)
+			return initial;
+		sb = new StringBuffer(16);
+		sb.append(initial);
+		while(sb.length()<16)
+			sb.insert(0,'0');
+		return sb.toString();
+	}
+
+	/**
+	 * Create a temporary directory.
+	 * @param parent The parent directory for the temporary
+	 *	directory. If this is null, the system property
+	 * 	"java.io.tmpdir" is used.
+	 * @param prefix The prefix for the directory's name. If this
+	 * 	is null, the full class name of this class is used.
+	 * @param suffix The suffix for the directory's name. If this
+	 * 	is null, ".tmp" is used.
+	 * @return The newly created temporary directory.
+	 * @throws IOException if directory creation fails
+	 * @throws SecurityException if {@link File#mkdir()} throws one.
+	 */
+	public static File createTmpDirectory(File parent, String prefix, String suffix) throws IOException
+	{
+		String name;
+		File dir;
+
+		if(prefix == null)
+			prefix = DEFAULT_PREFIX;
+		else if(!prefix.endsWith("."))
+			prefix += '.';
+		if(suffix == null)
+			suffix = DEFAULT_SUFFIX;
+		else if(!suffix.startsWith("."))
+			suffix = "." + suffix;
+		if(parent == null)
+			parent = new File(System.getProperty("java.io.tmpdir"));
+		name = prefix + toHexString(rand.nextLong()) + suffix;
+		dir = new File(parent, name);
+		if(!dir.mkdir())
+			throw new IOException("Unable to create temporary directory " + dir);
+		return dir.getCanonicalFile();
+	}
+
+	/**
+	 * Create a temporary directory. This calls
+	 * {@link #createTmpDirectory(File, String, String)} with null
+	 * for parent and suffix.
+	 * @param prefix The prefix for the directory's name. If this
+	 * 	is null, the full class name of this class is used.
+	 * @return The newly created temporary directory.
+	 * @throws IOException if directory creation fails
+	 * @throws SecurityException if {@link File#mkdir()} throws one.
+	 */
+	public static File createTmpDirectory(String prefix) throws IOException
+	{
+		return createTmpDirectory(null, prefix, null);
+	}
+
+	/**
+	 * Create a temporary directory. This calls
+	 * {@link #createTmpDirectory(File, String, String)} with null
+	 * for all arguments.
+	 * @return The newly created temporary directory.
+	 * @throws IOException if directory creation fails
+	 * @throws SecurityException if {@link File#mkdir()} throws one.
+	 */
+	public static File createTmpDirectory() throws IOException
+	{
+	 	return createTmpDirectory(null,null,null);
+	}
+
+	/**
+	 * Checks that child is a directory and really a child of
+	 * parent. This verifies that the {@link File#getCanonicalFile()
+	 * canonical} child is actually a child of parent. This should
+	 * fail if the child is a symbolic link to another directory and
+	 * therefore should not be traversed in a recursive traversal of
+	 * a directory.
+	 * @param parent The supposed parent of the child
+	 * @param child The child to check
+	 * @return true if child is a directory and a direct decendant
+	 * 	of parent.
+	 * @throws IOException if {@link File#getCanonicalFile()} does
+	 * @throws NullPointerException if either parent or child
+	 * 	are null.
+	 */
+	public static boolean isChildSubDirectory(File parent, File child) throws IOException
+	{
+		File childsParent;
+
+		if(child==null)
+			throw new NullPointerException("child argument is null");
+		if(!child.isDirectory())
+			return false;
+		if(parent==null)
+			throw new NullPointerException("parent argument is null");
+		parent = parent.getCanonicalFile();
+		child = child.getCanonicalFile();
+		childsParent = child.getParentFile();
+		if(childsParent == null)
+			return false;	// sym link to /?
+		childsParent = childsParent.getCanonicalFile();	// just in case...
+		if(!parent.equals(childsParent))
+			return false;
+		return true;
+	}
+
+	/**
+	 * Delete a file. Unlinke {@link File#delete()}, this throws an
+	 * exception if deletion fails.
+	 * @param file The file to delete
+	 * @throws IOException if file is not null, exists but delete
+	 * 	fails.
+	 */
+	public static void delete(File file) throws IOException
+	{
+		if(file==null || !file.exists())
+			return;
+		if(!file.delete())
+			throw new IOException("Unable to delete file " + file.getAbsolutePath());
+	}
+
+	/**
+	 * Recursively delete a file. If file is a directory,
+	 * subdirectories and files are also deleted. Care is taken to
+	 * not traverse symbolic links in this process. A null file or
+	 * a file that does not exist is considered to already been
+	 * deleted.
+	 * @param file The file or directory to be deleted
+	 * @throws IOException if the file, or a descendant, cannot
+	 * 	be deleted.
+	 * @throws SecurityException if {@link File#delete()} does.
+	 */
+	public static void deleteRecursively(File file) throws IOException
+	{
+		File[] children;
+		File child;
+
+		if(file == null || !file.exists())
+			return;	// already deleted?
+		if(file.isDirectory())
+		{
+			children = file.listFiles();
+			for(int i=0;i<children.length;i++)
+			{
+				child = children[i];
+				if(isChildSubDirectory(file,child))
+					deleteRecursively(child);
+				else
+					delete(child);
+			}
+		}
+
+		// finally
+		delete(file);
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java b/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java
new file mode 100644
index 0000000..2b81759
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java
@@ -0,0 +1,192 @@
+package org.owasp.esapi.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.security.Key;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.owasp.esapi.errors.ConfigurationException;
+
+public class ObjFactoryTest extends TestCase {
+	
+	// Purpose of this is to prevent a default, no-arg, public CTOR to be generated.
+	// We want to prevent this so we can use this class to test the case of where
+	// ObjectFactory<T>.make() throws an IllegalAccessException.
+	@SuppressWarnings("unused")
+	private ObjFactoryTest(int i) { ; }
+	
+    /**
+	 * Instantiates a new object factory test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public ObjFactoryTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Run all the test cases in this suite.
+     * This is to allow running from {@code org.owasp.esapi.AllTests}.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(ObjFactoryTest.class);
+        return suite;
+    }
+    
+    /** Test that NullCipher object is correctly returned. */
+    public void testMakeNullCipher() throws ConfigurationException {
+    	String className = "javax.crypto.NullCipher";
+    	javax.crypto.Cipher nullCipher =
+    			ObjFactory.make(className, "NullCipher");
+    	assertTrue( nullCipher instanceof javax.crypto.NullCipher );
+    	System.out.println("W00t! Watch out NSA...we have a NullCipher and we're not afraid to use it!");
+    }
+    
+    /** Test that InstantiationException is thrown as the root cause when the
+     * specified class name is an abstract class or interface.
+     */
+    public void testInterface() throws ConfigurationException {
+    	Key key = null;  	
+    	try {
+    		key = ObjFactory.make("java.security.Key", "Key");
+    		assertFalse("Should not be reached - interface or abstract class", key != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof InstantiationException);
+    	}
+    }
+    
+    /** Test that IllegalAccessException is thrown as the root cause when the
+     *  specified class has no public, no-arg CTOR. Cipher has only a protected
+     *  CTOR that takes multiple parameters.
+     *  
+     *  FIXME: Need new test. This also throws an InstantiationException as the
+     *  root cause. The goal is to have it throw IllegalAccessException.
+     */
+    public void testMakeNoPublicConstructor() throws ConfigurationException {
+    	ObjFactoryTest oft = null;	
+    	try {
+    		// CHECKME: As I read
+			//	  http://java.sun.com/docs/books/tutorial/reflect/member/ctorTrouble.html
+    		// this should cause an IllegalAccessException to be thrown because it has no public,
+    		// no-arg CTOR. However, it doesn't. It throws a InstantiationException instead.
+    		oft = ObjFactory.make(ObjFactoryTest.class.getName(), "ObjectFactoryTest");
+    		assertFalse("Should not be reached - no public CTOR", oft != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		// assertTrue( cause instanceof IllegalAccessException);
+    		assertTrue( cause instanceof InstantiationException);
+    	}
+    }
+    
+    /** Test that ClassNotFoundException is thrown as the root cause when
+     * the class name to be created is not a class name that exists anywhere.
+     */
+    public void testMakeNoSuchClass() throws ConfigurationException {
+    	Object obj = null;
+    	
+    	try {
+    		obj = ObjFactory.make("kevin.wall.HasNoClass", "Object");
+    		assertFalse("Should not be reached - no such class", obj != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof ClassNotFoundException);
+    	}
+    }
+    
+    /** Test that ClassCastException is thrown as the root cause when the
+     * created class is not a subclass / does not implement the specified type.
+     * (In this case, String is not a subclass / does not implement Key.)
+     */
+    public void testMakeNotASubclass() throws ConfigurationException {
+    	Key key = null;
+    	try {
+    		key = ObjFactory.make("java.lang.String", "testMakeNotASubclass");
+    		assertFalse("Should not be reached - not a subclass", key != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		System.out.println("DEBUG: Cause was: " + cause.getClass().getName());
+    		assertTrue( cause instanceof ClassCastException);
+    	} catch(ClassCastException ccex) {
+    		assertTrue("Caught expected class cast exception", true);
+    	}
+    }
+    
+    /** Test that IllegalArgumentException is thrown as the cause when the
+     * class name is specified as an empty string.
+     */
+    public void testMakeEmptyClassName() throws ConfigurationException {
+    	Object obj = null;
+    	try {
+    		obj = ObjFactory.make("", "testMakeEmptyClassName");
+    		assertFalse("Should not be reached - not a subclass", obj != null);
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof IllegalArgumentException);
+    	}
+    }
+    
+    /** Test that some other exception is thrown from the no-arg, public CTOR as the
+     * root cause. Had to use special external class here because strangely, this didn't
+     * work as an inner class. (Threw InstantiationException in that case instead.)
+     */
+    public void testMakeOtherException() throws ConfigurationException {
+    	@SuppressWarnings("unused")
+		ThePrefectClass ford = null;
+    	try {
+    		ford = ObjFactory.make("org.owasp.esapi.util.ThePrefectClass", "ThePrefectClass");
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+			assertTrue( cause instanceof UnsupportedOperationException);
+    	}
+    }
+    
+    /** Test case where typeName is null or empty string. */
+    public void testNullorEmptyTypeName() throws ConfigurationException {
+    	String className = "javax.crypto.NullCipher";
+    	javax.crypto.Cipher nullCipher =
+    			ObjFactory.make(className, null);
+    	assertTrue( nullCipher instanceof javax.crypto.NullCipher );
+    	nullCipher =
+			ObjFactory.make(className, "");
+    	assertTrue( nullCipher instanceof javax.crypto.NullCipher );
+    }
+    
+    /** Test case where no-arg CTOR does not exist. By all indications from
+     * Javadoc for {@code Class.newInstance()} one would think this should
+     * throw an {@code IllegalAccessException} because {@code SecretKeySpec}
+     * has two public CTORs that both take arguments. */
+    public void testMakeCipher() throws ConfigurationException {
+    	try {
+    		String className = "javax.crypto.spec.SecretKeySpec";
+    		javax.crypto.spec.SecretKeySpec skeySpec =
+    			(SecretKeySpec) ObjFactory.make(className, "SecretKeySpec");
+    		assertTrue( skeySpec != null );
+    	} catch(ConfigurationException ex) {
+    		Throwable cause = ex.getCause();
+    		assertTrue( cause instanceof InstantiationException);
+    	}
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/util/ThePrefectClass.java b/src/test/java/org/owasp/esapi/util/ThePrefectClass.java
new file mode 100644
index 0000000..28b0f33
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/util/ThePrefectClass.java
@@ -0,0 +1,12 @@
+package org.owasp.esapi.util;
+
+// The "perfect" helper class for testing JUnit test, ObjFactoryTest.
+// For testing only. Doesn't work as an inner class in ObjFactoryTest.
+// Props to D. Adams for HG2G. RIP.
+public class ThePrefectClass {
+	private static final int lifeUniverseEverything = 42;
+	public ThePrefectClass() {
+		throw new UnsupportedOperationException("This public CTOR is not supported!");
+	}
+	public int getAnswer() { return lifeUniverseEverything; }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/all-wcprops b/src/test/java/org/owasp/esapi/waf/.svn/all-wcprops
new file mode 100644
index 0000000..b0bc77a
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/all-wcprops
@@ -0,0 +1,119 @@
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf
+END
+DetectOutboundTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/DetectOutboundTest.java
+END
+RestrictContentTypeTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/RestrictContentTypeTest.java
+END
+RestrictExtensionTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/RestrictExtensionTest.java
+END
+BeanShellTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/BeanShellTest.java
+END
+RestrictMethodTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/RestrictMethodTest.java
+END
+GoodRequestTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/GoodRequestTest.java
+END
+WAFTestUtility.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java
+END
+AddHeaderTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/AddHeaderTest.java
+END
+MockWafServletContext.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/MockWafServletContext.java
+END
+EnforceHTTPSTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/EnforceHTTPSTest.java
+END
+MustMatchTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/MustMatchTest.java
+END
+VirtualPatchTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/VirtualPatchTest.java
+END
+WAFTestCase.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/WAFTestCase.java
+END
+RestrictUserAgentTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 96
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/RestrictUserAgentTest.java
+END
+DynamicInsertionTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/DynamicInsertionTest.java
+END
+EnforceAuthenticationTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/EnforceAuthenticationTest.java
+END
+HttpOnlyTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/HttpOnlyTest.java
+END
+WAFFilterTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/WAFFilterTest.java
+END
+MockWafFilterConfig.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/MockWafFilterConfig.java
+END
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/entries b/src/test/java/org/owasp/esapi/waf/.svn/entries
new file mode 100644
index 0000000..d242789
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/entries
@@ -0,0 +1,677 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+DetectOutboundTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+4c86dae0ae2058f199a3bbc765c02de9
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2558
+

+internal
+dir
+

+RestrictContentTypeTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+be15ddd119bc34ac9470679943a66ffa
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1793
+

+RestrictExtensionTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+1dc2a267bee6fc72d40705b5ae9f423f
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2255
+

+BeanShellTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+dd399604780bd1fa3121e28b188e8869
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1559
+

+RestrictMethodTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+a36c3ffb97cf2c967970a2ace00a074b
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2048
+

+GoodRequestTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+70c429d853062d50d236319bd8738bd3
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1445
+

+WAFTestUtility.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+939b183455358550d3026f2d11180de7
+2009-11-10T00:24:35.827023Z
+777
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3529
+

+AddHeaderTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+483c9baf07729166b59f36e75424dff6
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2093
+

+MockWafServletContext.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+777b3db940d05cc19b5dfdf9dd628d25
+2009-11-10T00:24:35.827023Z
+777
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+330
+

+EnforceHTTPSTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+dc2c2fd2967773890c55965f2c3e36ba
+2009-11-07T17:48:23.955569Z
+756
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2520
+

+MustMatchTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+99686c4923cf3ad9e4ab6e33fbde7427
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2291
+

+VirtualPatchTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+a73da4944392f1122da92ad4d7c45768
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2699
+

+WAFTestCase.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+2b30ce16a742fd91baadafdb35254a0a
+2012-06-24T01:08:09.752216Z
+1869
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2647
+

+RestrictUserAgentTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+4fbe1a5ecaaaa69e20caa97aaeeebbc6
+2009-11-20T00:33:25.026048Z
+832
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1493
+

+DynamicInsertionTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+bc428293e817cff354c9a9624c7c87d6
+2009-11-01T04:08:19.666153Z
+741
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2703
+

+EnforceAuthenticationTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+2d358fed363b8688df9347d8b4ebfca3
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1976
+

+HttpOnlyTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+256baa3aea3d300270a3028916ef3d99
+2009-11-10T00:24:35.827023Z
+777
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3075
+

+MockWafFilterConfig.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+b22cc20c786f5ac9b2e7bd20fecf5d37
+2009-11-10T00:24:35.827023Z
+777
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+368
+

+WAFFilterTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+81aaf811943ddfb2ffdfed9cd6eb55f1
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2742
+

diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/AddHeaderTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/AddHeaderTest.java.svn-base
new file mode 100644
index 0000000..0e4d692
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/AddHeaderTest.java.svn-base
@@ -0,0 +1,59 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+import junit.framework.TestSuite;
+
+public class AddHeaderTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(AddHeaderTest.class);
+	}
+	
+    /*
+     * Test whether or not the WAF correctly adds the header to the response when it should and
+     * when it shouldn't, based on paths specified in the WAF rules.
+     */
+    public void testShouldAddHeader() throws Exception {
+        
+    	System.out.println("addHeaderPolicy - Response should have FOO=BAR header added" );
+
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/addheader" ) );
+    	
+    	WAFTestUtility.createAndExecuteWAFTransaction("waf-policies/add-header-policy.xml", request, response );
+    	
+        String foo = response.getHeader( "FOO" );
+        assertTrue( foo != null && foo.equals( "BAR" ) );
+        
+    }
+    
+    public void testShouldNotAddHeader() throws Exception {
+    	System.out.println("addHeaderPolicy - Response should have FOO=BAR header added" );
+
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/marketing/foo" ) );
+    	
+    	WAFTestUtility.createAndExecuteWAFTransaction("waf-policies/add-header-policy.xml", request, response );
+    	
+        String foo = response.getHeader( "FOO" );
+        assertTrue( foo == null );
+        
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/BeanShellTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/BeanShellTest.java.svn-base
new file mode 100644
index 0000000..94e8a93
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/BeanShellTest.java.svn-base
@@ -0,0 +1,47 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+import junit.framework.TestSuite;
+
+public class BeanShellTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(BeanShellTest.class);
+	}
+
+	public void testRedirectBeanShellRule() throws Exception {
+
+		request = new MockHttpServletRequest( new URL( "http://www.example.com/beanshelltest" ) );
+
+    	WAFTestUtility.createAndExecuteWAFTransaction("waf-policies/bean-shell-policy.xml", request, response, new MockFilterChain() );
+    	
+    	HttpSession session = request.getSession();
+    	
+    	assert(session.getAttribute("simple_waf_test") != null);
+    	assert(response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY);
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/DetectOutboundTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/DetectOutboundTest.java.svn-base
new file mode 100644
index 0000000..54eefb8
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/DetectOutboundTest.java.svn-base
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class DetectOutboundTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(DetectOutboundTest.class);
+	}
+	
+	public void testBadDetectOutbound() throws Exception {
+	       
+    	System.out.println("detectOutboundPolicy - Fires if response has \"2008\" in it" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/here_is_the_2008" ) );
+    	
+    	// this setting of the body gets overridden in the MockFilterChain =(. For a hack we put it in
+    	// the URI above, which gets reflected into the response body.
+    	response.setBody( "Now is the time for all good men 2008 to come to the aid of their country" );
+        
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/detect-outbound-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY );
+        
+    }
+	
+	public void testGoodDetectOutbound() throws Exception {
+	       
+    	System.out.println("detectOutboundPolicy - should not fire even if response has \"2008\" in it because the content type is image/jpeg" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/here_is_the_2008" ) );
+    	response = new MockHttpServletResponse();
+    	response.setContentType("image/jpeg");
+    	response.setBody( "Now is the time for all good men 2008 to come to the aid of their country" );
+        
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/detect-outbound-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_OK );
+        
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/DynamicInsertionTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/DynamicInsertionTest.java.svn-base
new file mode 100644
index 0000000..300da4b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/DynamicInsertionTest.java.svn-base
@@ -0,0 +1,70 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+import junit.framework.TestSuite;
+
+public class DynamicInsertionTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(DynamicInsertionTest.class);
+	}
+	
+	public void testShouldReplaceContent() throws Exception {
+		
+    	System.out.println("dynamicInsertionPolicy - replaces </body> with 'this is a test'" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "https://www.example.com/here+</body>+there+everywhere" ) );
+        request.setScheme("https");
+        request.getSession(true).setAttribute("ESAPIUserSessionKey", user);
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/dynamic-insertion-policy.xml", request, response );
+    	
+    	assertTrue ( response.getStatus() == HttpServletResponse.SC_OK );
+    	String body = response.getBody();
+    	assertTrue ( body.indexOf("test") > -1 );
+    	System.out.println( body );
+   	
+	}
+	
+	public void testShouldNotReplaceContent() throws Exception {
+		
+		System.out.println("dynamicInsertionPolicy - should not replace '< /body>' or </body > or </bo dy> with anything" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "https://www.example.com/here+<+/body></bo+dy>+</body+>+there+everywhere" ) );
+    	request.setScheme("https");
+    	request.getSession(true).setAttribute("ESAPIUserSessionKey", user);
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/dynamic-insertion-policy.xml", request, response );
+    	
+    	assertTrue ( response.getStatus() == HttpServletResponse.SC_OK );
+    	assertTrue ( response.getBody().indexOf("test") == -1 );
+    	System.out.println( response.getBody() );
+   		
+    	
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/EnforceAuthenticationTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/EnforceAuthenticationTest.java.svn-base
new file mode 100644
index 0000000..2b83312
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/EnforceAuthenticationTest.java.svn-base
@@ -0,0 +1,52 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class EnforceAuthenticationTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(EnforceAuthenticationTest.class);
+	}
+	
+	public void testAuthenticatedRequest() throws Exception {
+		// authentication test
+	    url = new URL( "https://www.example.com/authenticated" );
+		System.out.println( "\nTest good request (user in session): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession().setAttribute("ESAPIUserSessionKey", user);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );		
+	}
+
+	public void testUnauthenticatedRequest() throws Exception {
+	    // authentication test
+	    url = new URL( "http://www.example.com/authenticated" );
+		System.out.println( "\nTest bad request (no user in session): " + url );
+	    request = new MockHttpServletRequest( url );
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest ( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );		
+	}
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/EnforceHTTPSTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/EnforceHTTPSTest.java.svn-base
new file mode 100644
index 0000000..83bb550
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/EnforceHTTPSTest.java.svn-base
@@ -0,0 +1,68 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class EnforceHTTPSTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(EnforceHTTPSTest.class);
+	}
+	
+	public void setUp() throws Exception {
+		super.setUp();
+    	WAFTestUtility.setWAFPolicy( waf, "waf-policy.xml" );
+	}
+
+	public void testGoodSchemeSSLRequired() throws Exception {
+	    // test good scheme
+		url = new URL( "https://www.example.com/" );
+		System.out.println( "\nTest good scheme (https): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession(true);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );		
+	}
+
+     
+	public void testBadSchemeSSLNotRequired () throws Exception {
+	    // test bad scheme
+	    url = new URL( "http://www.example.com/images/test.gif" );
+		System.out.println( "\nTest bad scheme (no ssl - but its not required): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession(true);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );		
+	}
+
+	public void testBadSchemeSSLRequired () throws Exception {
+	    // test bad scheme
+	    url = new URL( "http://www.example.com/secure" );
+		System.out.println( "\nTest bad scheme (no ssl - but its required): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession(true);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );		
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/GoodRequestTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/GoodRequestTest.java.svn-base
new file mode 100644
index 0000000..c1ed068
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/GoodRequestTest.java.svn-base
@@ -0,0 +1,43 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class GoodRequestTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(GoodRequestTest.class);
+	}
+	
+	public void testGoodRequest() throws Exception {
+		// should pass
+        url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "Test good URL: " + url );
+        request = new MockHttpServletRequest( url );
+        request.getSession(true);
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/HttpOnlyTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/HttpOnlyTest.java.svn-base
new file mode 100644
index 0000000..b176207
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/HttpOnlyTest.java.svn-base
@@ -0,0 +1,84 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.http.MockFilterChain;
+
+import junit.framework.TestSuite;
+
+public class HttpOnlyTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(HttpOnlyTest.class);
+	}
+	
+    /*
+     * Test all aspects of the HTTPOnly protection. Note that attaching HTTPOnly to
+     * JSESSIONIDs requires a 3-step handshake, so the first 2 response codes should 
+     * be 301, and on the 3rd response the cookie should be set. A lot of extra traffic
+     * for HTTPOnly.
+     * 
+     * That same 3-step handshake won't be needed for custom cookies generated by the
+     * application with addCookie().
+     */
+
+	// this has been commented because we decided not to try to make this work. too much
+	// hackery.
+	/*
+    public void testAddHttpOnlyOnSessionCookie() throws Exception {
+    	
+     	System.out.println("addHttpOnlyPolicy - Response should have httpOnly set on the session ID (JSESSIONID) cookie added to response" );
+   	        	
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/add-httponly-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY );
+    	
+    }
+    */
+	
+    public void testAddHttpOnlyOnCustomCookie() throws Exception {
+    	
+    	System.out.println("addHttpOnlyPolicy - Response should have httpOnly set on a custom cookie (FOOBAR) added to the response" );
+   	    
+    	request.getSession(true);
+    	
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/add-httponly-policy.xml", request, response, new HttpOnlyTestFilterChain() );
+    	
+    	//response.dump();
+     
+    	String foo = response.getHeader("Set-Cookie");
+    	assertTrue( response.getStatus() != HttpServletResponse.SC_MOVED_PERMANENTLY );
+    	assertTrue( foo.contains("HttpOnly") );
+    	
+    }
+    
+    class HttpOnlyTestFilterChain extends MockFilterChain {
+		public void doFilter(ServletRequest arg0, ServletResponse arg1)
+				throws IOException, ServletException {
+			HttpServletResponse response = (HttpServletResponse)arg1;
+			response.addCookie( new Cookie("FOO", "BAR" ) );
+		}
+    }
+    
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/MockWafFilterConfig.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/MockWafFilterConfig.java.svn-base
new file mode 100644
index 0000000..25a80bc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/MockWafFilterConfig.java.svn-base
@@ -0,0 +1,18 @@
+package org.owasp.esapi.waf;
+
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.owasp.esapi.http.MockFilterConfig;
+
+public class MockWafFilterConfig extends MockFilterConfig {
+
+	public MockWafFilterConfig(Map map) {
+		super(map);
+	}
+
+	public ServletContext getServletContext() {
+    	return new MockWafServletContext();
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/MockWafServletContext.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/MockWafServletContext.java.svn-base
new file mode 100644
index 0000000..1d5b910
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/MockWafServletContext.java.svn-base
@@ -0,0 +1,14 @@
+package org.owasp.esapi.waf;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.http.MockServletContext;
+
+public class MockWafServletContext extends MockServletContext {
+
+	public String getRealPath(String s) {
+		
+		return ESAPI.securityConfiguration().getResourceFile( "" ).getAbsolutePath() + "/" + s;
+		
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/MustMatchTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/MustMatchTest.java.svn-base
new file mode 100644
index 0000000..c92eefd
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/MustMatchTest.java.svn-base
@@ -0,0 +1,55 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class MustMatchTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(MustMatchTest.class);
+	}
+	
+	public void testUnauthorizedRequest () throws Exception {
+        // Test bad request (no x-roles header)
+        url = new URL( "https://www.example.com/admin/config" );
+		System.out.println( "\nTest bad request (request has no x-roles header): " + url );
+        request = new MockHttpServletRequest( url );
+        request.setRemoteAddr("192.168.1.5"); // necessary to pass IPRule
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );
+	}
+	
+	public void testAuthorizedRequest() throws Exception {
+        // Test good request (request has x-roles header)
+        url = new URL( "https://www.example.com/admin/config" );
+		System.out.println( "\nTest good request (request has x-roles header): " + url );
+        request = new MockHttpServletRequest( url );
+        request.addHeader("x-roles", "admin" );
+        request.setRemoteAddr("192.168.1.100");
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictContentTypeTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictContentTypeTest.java.svn-base
new file mode 100644
index 0000000..430600c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictContentTypeTest.java.svn-base
@@ -0,0 +1,51 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+public class RestrictContentTypeTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(RestrictContentTypeTest.class);
+	}
+
+	public void testNoContentType() throws Exception {
+
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-content-type-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_OK);
+	}
+	
+	public void testGoodContentType() throws Exception {
+		request.addHeader("Content-Type","text/html");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-content-type-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_OK);
+	}
+	
+	public void testBadContentType() throws Exception {
+		request.addHeader("Content-Type","multipart/form-upload");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-content-type-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY);
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictExtensionTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictExtensionTest.java.svn-base
new file mode 100644
index 0000000..7aee58e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictExtensionTest.java.svn-base
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class RestrictExtensionTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(RestrictExtensionTest.class);
+	}
+	
+	public void testGoodExtension() throws Exception {
+        
+    	System.out.println("restrictExtensionPolicy - approve this URL (doesn't end in .log or anything else evil)" );
+
+        request = new MockHttpServletRequest( new URL( "http://www.example.com/logfiles/12192009.jpg" ) );
+        request.getSession(true); // pass HttpOnly test...
+    	response = new MockHttpServletResponse();
+        
+        WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-extension-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() != HttpServletResponse.SC_MOVED_PERMANENTLY );
+    }
+
+	public void testBadExtension() throws Exception {
+        
+    	System.out.println("restrictExtensionPolicy - reject any URL ending in .log" );
+
+        MockHttpServletRequest request = new MockHttpServletRequest( new URL( "http://www.example.com/logfiles/12192009.log" ) );
+    	MockHttpServletResponse response = new MockHttpServletResponse();
+        
+        WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-extension-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictMethodTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictMethodTest.java.svn-base
new file mode 100644
index 0000000..0203bb8
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictMethodTest.java.svn-base
@@ -0,0 +1,56 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class RestrictMethodTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(RestrictMethodTest.class);
+	}
+	
+	public void testGoodMethod() throws Exception {
+		// test good method
+	    url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "\nTest good method: " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.setMethod( "TRACE" );
+	    request.getSession(true); // so the app will pass the HttpOnly test...
+	    
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest(waf, request, response, HttpServletResponse.SC_OK );
+	}
+	
+	public void testBadMethod() throws Exception {
+		// test bad method
+	    url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "\nTest bad method: " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.setMethod( "JEFF" );
+	    request.getSession(true); // so the app will pass the HttpOnly test...
+	    
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );
+	}    
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictUserAgentTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictUserAgentTest.java.svn-base
new file mode 100644
index 0000000..1cd641d
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/RestrictUserAgentTest.java.svn-base
@@ -0,0 +1,46 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+public class RestrictUserAgentTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(RestrictUserAgentTest.class);
+	}
+	
+	public void testBadUserAgent() throws Exception {
+		
+		request.addHeader("User-Agent","GoogleBot");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-user-agent-policy.xml", request, response );
+		
+		assert(response.getStatus() == 403);
+	}
+	
+	public void testGoodUserAgent() throws Exception {
+		
+		request.addHeader("User-Agent","MSIE NT Compatible");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-user-agent-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_OK);
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/VirtualPatchTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/VirtualPatchTest.java.svn-base
new file mode 100644
index 0000000..4be65ef
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/VirtualPatchTest.java.svn-base
@@ -0,0 +1,60 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+import junit.framework.TestSuite;
+
+public class VirtualPatchTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(VirtualPatchTest.class);
+	}
+	
+	public void testNonAttacktAfterVirtualPatch() throws Exception {
+		// should pass
+        url = new URL( "https://www.example.com/virtualpatch.jsp" );
+		System.out.println( "Testing non-attack after virtual patch on URL: " + url );
+        request = new MockHttpServletRequest( url );
+        request.getSession(true);
+        request.setScheme("https");
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+        request.addParameter("bar", "09124asd135r123irh2938rh9c82hr3hareohvw"); // alphanums are allowed
+        request.addParameter("foo", "<script>' oR 1=1-- bad.attax.google.com jar:"); // this parameter should not be touched by the patch
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );
+	}
+	
+	public void testAttackAfterVirtualPatch() throws Exception {
+		// should fail
+        url = new URL( "https://www.example.com/foo.jsp" );
+		System.out.println( "Testing attack after virtual patch on URL: " + url );
+        request = new MockHttpServletRequest( url );
+        request.getSession(true);
+        request.setScheme("https");
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+        request.addParameter("bar", "09124asd135r123ir>h2938rh9c82hr3hareohvw"); // non-alphanums are not allowed (there is 1 in the middle)
+        request.addParameter("foo", "SADFSDfSDFSDF123123123"); // this parameter should not be touched by the patch
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFFilterTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFFilterTest.java.svn-base
new file mode 100644
index 0000000..fdb9f08
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFFilterTest.java.svn-base
@@ -0,0 +1,81 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * This is the main TestSuite for all the WAF tests. Some of the WAF
+ * tests utilize a large policy file containing a bunch of unrelated
+ * rules, and some use very small policy files that only exercise
+ * specific functionality. Some may use both. 
+ * 
+ * There is an unlimited combination of rules to be exercised together, 
+ * so the small policy files test the strict functionality, while the
+ * larger policy files (hopefully) give us assurance that the rules 
+ * won't interfere with each other.
+ */
+
+public class WAFFilterTest extends TestCase {
+    
+    /**
+	 * Instantiates a new WAF test.
+	 * 
+	 * @param testName the test name
+	 */
+    public WAFFilterTest(String testName) {
+        super(testName);
+    }
+
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+
+    	TestSuite suite = new TestSuite(WAFFilterTest.class);
+
+        suite.addTest(AddHeaderTest.suite());
+        suite.addTest(BeanShellTest.suite());
+        suite.addTest(DetectOutboundTest.suite());
+        suite.addTest(EnforceAuthenticationTest.suite());
+        suite.addTest(EnforceHTTPSTest.suite());
+        suite.addTest(GoodRequestTest.suite());
+        suite.addTest(HttpOnlyTest.suite());
+        suite.addTest(MustMatchTest.suite());
+        suite.addTest(DynamicInsertionTest.suite());
+        suite.addTest(RestrictContentTypeTest.suite());
+        suite.addTest(RestrictExtensionTest.suite());
+        suite.addTest(RestrictMethodTest.suite());
+        suite.addTest(RestrictUserAgentTest.suite());
+        suite.addTest(VirtualPatchTest.suite());
+        
+        return suite;
+    }
+    
+    public void testConfigurationCanBeRead() throws Exception {
+    	
+    	ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter();
+    	WAFTestUtility.setWAFPolicy(waf, "waf-policy.xml");
+
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFTestCase.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFTestCase.java.svn-base
new file mode 100644
index 0000000..c0c63f7
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFTestCase.java.svn-base
@@ -0,0 +1,65 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.User;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+import junit.framework.TestCase;
+
+public abstract class WAFTestCase extends TestCase {
+
+	protected MockHttpServletRequest request;
+	protected MockHttpServletResponse response;
+	protected URL url;
+	protected ESAPIWebApplicationFirewallFilter waf;
+	
+	protected static User user = null;
+	
+	public void setUp() throws Exception {
+	    // setup the user in session
+		
+		if ( user == null ) {
+			String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+			Authenticator instance = ESAPI.authenticator();
+			String password = instance.generateStrongPassword();
+			instance.setCurrentUser(user);
+			user = instance.createUser(accountName, password, password);
+			user.enable();
+		}
+		
+		request = new MockHttpServletRequest( new URL( "http://www.example.com/index" ) );
+        response = new MockHttpServletResponse();
+        waf = new ESAPIWebApplicationFirewallFilter();
+        
+        WAFTestUtility.setWAFPolicy(waf, "/waf-policy.xml");
+	}
+    
+	public void createAndExecuteWAFResponseCodeTest( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, int expectedResult ) throws Exception {
+    	assertEquals ( expectedResult, WAFTestUtility.createAndExecuteWAFTransaction( waf, request, response) );	
+	}
+    
+    public void createAndExecuteWAFResponseCodeTest( String policy, MockHttpServletRequest request, MockHttpServletResponse response, int expectedResult ) throws Exception {
+    	assertEquals ( expectedResult, WAFTestUtility.createAndExecuteWAFTransaction( policy, request, response) );	
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFTestUtility.java.svn-base b/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFTestUtility.java.svn-base
new file mode 100644
index 0000000..3a58b38
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/.svn/text-base/WAFTestUtility.java.svn-base
@@ -0,0 +1,91 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * 
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.FilterConfig;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockFilterConfig;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * This class holds a number of static utilities to make writing WAF test cases easy. Definitely not useful
+ * for anything other than testing.
+ */
+public class WAFTestUtility {
+
+    public static void setWAFPolicy( ESAPIWebApplicationFirewallFilter waf, String policyFile ) throws Exception {
+        Map map = new HashMap();
+    	map.put( "configuration", policyFile );
+    	map.put( "log_settings", "../log4j.xml");
+    	FilterConfig mfc = new MockWafFilterConfig( map );
+    	waf.init( mfc );
+    }
+    
+    public static int checkWAFResult( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain chain ) throws Exception {
+
+        //request.dump();
+    	waf.doFilter(request, response, chain);
+        //response.dump();
+        
+        return response.getStatus();
+       
+    }  
+
+    public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception {
+
+		MockFilterChain chain = new MockFilterChain();
+		
+		return WAFTestUtility.checkWAFResult(waf, request, response, chain);
+		
+	}
+
+    public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception {
+
+
+    	return WAFTestUtility.checkWAFResult(waf, request, response, filterChain);
+		
+	}
+    
+    public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception {
+
+    	ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter();
+    	File f = ESAPI.securityConfiguration().getResourceFile(policy);
+    	waf.setConfiguration(f.getAbsolutePath(),"");
+
+		return createAndExecuteWAFTransaction(waf, request, response );
+		
+	}
+    
+    public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception {
+
+    	ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter();
+    	File f = ESAPI.securityConfiguration().getResourceFile(policy);        
+    	waf.setConfiguration(f.getAbsolutePath(),"");
+
+		return createAndExecuteWAFTransaction(waf, request, response, filterChain );
+		
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/AddHeaderTest.java b/src/test/java/org/owasp/esapi/waf/AddHeaderTest.java
new file mode 100644
index 0000000..0e4d692
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/AddHeaderTest.java
@@ -0,0 +1,59 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+import junit.framework.TestSuite;
+
+public class AddHeaderTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(AddHeaderTest.class);
+	}
+	
+    /*
+     * Test whether or not the WAF correctly adds the header to the response when it should and
+     * when it shouldn't, based on paths specified in the WAF rules.
+     */
+    public void testShouldAddHeader() throws Exception {
+        
+    	System.out.println("addHeaderPolicy - Response should have FOO=BAR header added" );
+
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/addheader" ) );
+    	
+    	WAFTestUtility.createAndExecuteWAFTransaction("waf-policies/add-header-policy.xml", request, response );
+    	
+        String foo = response.getHeader( "FOO" );
+        assertTrue( foo != null && foo.equals( "BAR" ) );
+        
+    }
+    
+    public void testShouldNotAddHeader() throws Exception {
+    	System.out.println("addHeaderPolicy - Response should have FOO=BAR header added" );
+
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/marketing/foo" ) );
+    	
+    	WAFTestUtility.createAndExecuteWAFTransaction("waf-policies/add-header-policy.xml", request, response );
+    	
+        String foo = response.getHeader( "FOO" );
+        assertTrue( foo == null );
+        
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/waf/BeanShellTest.java b/src/test/java/org/owasp/esapi/waf/BeanShellTest.java
new file mode 100644
index 0000000..94e8a93
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/BeanShellTest.java
@@ -0,0 +1,47 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+import junit.framework.TestSuite;
+
+public class BeanShellTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(BeanShellTest.class);
+	}
+
+	public void testRedirectBeanShellRule() throws Exception {
+
+		request = new MockHttpServletRequest( new URL( "http://www.example.com/beanshelltest" ) );
+
+    	WAFTestUtility.createAndExecuteWAFTransaction("waf-policies/bean-shell-policy.xml", request, response, new MockFilterChain() );
+    	
+    	HttpSession session = request.getSession();
+    	
+    	assert(session.getAttribute("simple_waf_test") != null);
+    	assert(response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY);
+
+	}
+
+}
diff --git a/src/test/java/org/owasp/esapi/waf/DetectOutboundTest.java b/src/test/java/org/owasp/esapi/waf/DetectOutboundTest.java
new file mode 100644
index 0000000..54eefb8
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/DetectOutboundTest.java
@@ -0,0 +1,63 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class DetectOutboundTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(DetectOutboundTest.class);
+	}
+	
+	public void testBadDetectOutbound() throws Exception {
+	       
+    	System.out.println("detectOutboundPolicy - Fires if response has \"2008\" in it" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/here_is_the_2008" ) );
+    	
+    	// this setting of the body gets overridden in the MockFilterChain =(. For a hack we put it in
+    	// the URI above, which gets reflected into the response body.
+    	response.setBody( "Now is the time for all good men 2008 to come to the aid of their country" );
+        
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/detect-outbound-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY );
+        
+    }
+	
+	public void testGoodDetectOutbound() throws Exception {
+	       
+    	System.out.println("detectOutboundPolicy - should not fire even if response has \"2008\" in it because the content type is image/jpeg" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "http://www.example.com/here_is_the_2008" ) );
+    	response = new MockHttpServletResponse();
+    	response.setContentType("image/jpeg");
+    	response.setBody( "Now is the time for all good men 2008 to come to the aid of their country" );
+        
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/detect-outbound-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_OK );
+        
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/DynamicInsertionTest.java b/src/test/java/org/owasp/esapi/waf/DynamicInsertionTest.java
new file mode 100644
index 0000000..300da4b
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/DynamicInsertionTest.java
@@ -0,0 +1,70 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+import junit.framework.TestSuite;
+
+public class DynamicInsertionTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(DynamicInsertionTest.class);
+	}
+	
+	public void testShouldReplaceContent() throws Exception {
+		
+    	System.out.println("dynamicInsertionPolicy - replaces </body> with 'this is a test'" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "https://www.example.com/here+</body>+there+everywhere" ) );
+        request.setScheme("https");
+        request.getSession(true).setAttribute("ESAPIUserSessionKey", user);
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/dynamic-insertion-policy.xml", request, response );
+    	
+    	assertTrue ( response.getStatus() == HttpServletResponse.SC_OK );
+    	String body = response.getBody();
+    	assertTrue ( body.indexOf("test") > -1 );
+    	System.out.println( body );
+   	
+	}
+	
+	public void testShouldNotReplaceContent() throws Exception {
+		
+		System.out.println("dynamicInsertionPolicy - should not replace '< /body>' or </body > or </bo dy> with anything" );
+   	    
+    	request = new MockHttpServletRequest( new URL( "https://www.example.com/here+<+/body></bo+dy>+</body+>+there+everywhere" ) );
+    	request.setScheme("https");
+    	request.getSession(true).setAttribute("ESAPIUserSessionKey", user);
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/dynamic-insertion-policy.xml", request, response );
+    	
+    	assertTrue ( response.getStatus() == HttpServletResponse.SC_OK );
+    	assertTrue ( response.getBody().indexOf("test") == -1 );
+    	System.out.println( response.getBody() );
+   		
+    	
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/EnforceAuthenticationTest.java b/src/test/java/org/owasp/esapi/waf/EnforceAuthenticationTest.java
new file mode 100644
index 0000000..2b83312
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/EnforceAuthenticationTest.java
@@ -0,0 +1,52 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class EnforceAuthenticationTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(EnforceAuthenticationTest.class);
+	}
+	
+	public void testAuthenticatedRequest() throws Exception {
+		// authentication test
+	    url = new URL( "https://www.example.com/authenticated" );
+		System.out.println( "\nTest good request (user in session): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession().setAttribute("ESAPIUserSessionKey", user);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );		
+	}
+
+	public void testUnauthenticatedRequest() throws Exception {
+	    // authentication test
+	    url = new URL( "http://www.example.com/authenticated" );
+		System.out.println( "\nTest bad request (no user in session): " + url );
+	    request = new MockHttpServletRequest( url );
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest ( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );		
+	}
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/owasp/esapi/waf/EnforceHTTPSTest.java b/src/test/java/org/owasp/esapi/waf/EnforceHTTPSTest.java
new file mode 100644
index 0000000..83bb550
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/EnforceHTTPSTest.java
@@ -0,0 +1,68 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class EnforceHTTPSTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(EnforceHTTPSTest.class);
+	}
+	
+	public void setUp() throws Exception {
+		super.setUp();
+    	WAFTestUtility.setWAFPolicy( waf, "waf-policy.xml" );
+	}
+
+	public void testGoodSchemeSSLRequired() throws Exception {
+	    // test good scheme
+		url = new URL( "https://www.example.com/" );
+		System.out.println( "\nTest good scheme (https): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession(true);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );		
+	}
+
+     
+	public void testBadSchemeSSLNotRequired () throws Exception {
+	    // test bad scheme
+	    url = new URL( "http://www.example.com/images/test.gif" );
+		System.out.println( "\nTest bad scheme (no ssl - but its not required): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession(true);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );		
+	}
+
+	public void testBadSchemeSSLRequired () throws Exception {
+	    // test bad scheme
+	    url = new URL( "http://www.example.com/secure" );
+		System.out.println( "\nTest bad scheme (no ssl - but its required): " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.getSession(true);
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );		
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/GoodRequestTest.java b/src/test/java/org/owasp/esapi/waf/GoodRequestTest.java
new file mode 100644
index 0000000..c1ed068
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/GoodRequestTest.java
@@ -0,0 +1,43 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class GoodRequestTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(GoodRequestTest.class);
+	}
+	
+	public void testGoodRequest() throws Exception {
+		// should pass
+        url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "Test good URL: " + url );
+        request = new MockHttpServletRequest( url );
+        request.getSession(true);
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/HttpOnlyTest.java b/src/test/java/org/owasp/esapi/waf/HttpOnlyTest.java
new file mode 100644
index 0000000..b176207
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/HttpOnlyTest.java
@@ -0,0 +1,84 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.http.MockFilterChain;
+
+import junit.framework.TestSuite;
+
+public class HttpOnlyTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(HttpOnlyTest.class);
+	}
+	
+    /*
+     * Test all aspects of the HTTPOnly protection. Note that attaching HTTPOnly to
+     * JSESSIONIDs requires a 3-step handshake, so the first 2 response codes should 
+     * be 301, and on the 3rd response the cookie should be set. A lot of extra traffic
+     * for HTTPOnly.
+     * 
+     * That same 3-step handshake won't be needed for custom cookies generated by the
+     * application with addCookie().
+     */
+
+	// this has been commented because we decided not to try to make this work. too much
+	// hackery.
+	/*
+    public void testAddHttpOnlyOnSessionCookie() throws Exception {
+    	
+     	System.out.println("addHttpOnlyPolicy - Response should have httpOnly set on the session ID (JSESSIONID) cookie added to response" );
+   	        	
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/add-httponly-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY );
+    	
+    }
+    */
+	
+    public void testAddHttpOnlyOnCustomCookie() throws Exception {
+    	
+    	System.out.println("addHttpOnlyPolicy - Response should have httpOnly set on a custom cookie (FOOBAR) added to the response" );
+   	    
+    	request.getSession(true);
+    	
+    	WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/add-httponly-policy.xml", request, response, new HttpOnlyTestFilterChain() );
+    	
+    	//response.dump();
+     
+    	String foo = response.getHeader("Set-Cookie");
+    	assertTrue( response.getStatus() != HttpServletResponse.SC_MOVED_PERMANENTLY );
+    	assertTrue( foo.contains("HttpOnly") );
+    	
+    }
+    
+    class HttpOnlyTestFilterChain extends MockFilterChain {
+		public void doFilter(ServletRequest arg0, ServletResponse arg1)
+				throws IOException, ServletException {
+			HttpServletResponse response = (HttpServletResponse)arg1;
+			response.addCookie( new Cookie("FOO", "BAR" ) );
+		}
+    }
+    
+}
diff --git a/src/test/java/org/owasp/esapi/waf/MockWafFilterConfig.java b/src/test/java/org/owasp/esapi/waf/MockWafFilterConfig.java
new file mode 100644
index 0000000..25a80bc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/MockWafFilterConfig.java
@@ -0,0 +1,18 @@
+package org.owasp.esapi.waf;
+
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.owasp.esapi.http.MockFilterConfig;
+
+public class MockWafFilterConfig extends MockFilterConfig {
+
+	public MockWafFilterConfig(Map map) {
+		super(map);
+	}
+
+	public ServletContext getServletContext() {
+    	return new MockWafServletContext();
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/MockWafServletContext.java b/src/test/java/org/owasp/esapi/waf/MockWafServletContext.java
new file mode 100644
index 0000000..1d5b910
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/MockWafServletContext.java
@@ -0,0 +1,14 @@
+package org.owasp.esapi.waf;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.http.MockServletContext;
+
+public class MockWafServletContext extends MockServletContext {
+
+	public String getRealPath(String s) {
+		
+		return ESAPI.securityConfiguration().getResourceFile( "" ).getAbsolutePath() + "/" + s;
+		
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/MustMatchTest.java b/src/test/java/org/owasp/esapi/waf/MustMatchTest.java
new file mode 100644
index 0000000..c92eefd
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/MustMatchTest.java
@@ -0,0 +1,55 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class MustMatchTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(MustMatchTest.class);
+	}
+	
+	public void testUnauthorizedRequest () throws Exception {
+        // Test bad request (no x-roles header)
+        url = new URL( "https://www.example.com/admin/config" );
+		System.out.println( "\nTest bad request (request has no x-roles header): " + url );
+        request = new MockHttpServletRequest( url );
+        request.setRemoteAddr("192.168.1.5"); // necessary to pass IPRule
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );
+	}
+	
+	public void testAuthorizedRequest() throws Exception {
+        // Test good request (request has x-roles header)
+        url = new URL( "https://www.example.com/admin/config" );
+		System.out.println( "\nTest good request (request has x-roles header): " + url );
+        request = new MockHttpServletRequest( url );
+        request.addHeader("x-roles", "admin" );
+        request.setRemoteAddr("192.168.1.100");
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/RestrictContentTypeTest.java b/src/test/java/org/owasp/esapi/waf/RestrictContentTypeTest.java
new file mode 100644
index 0000000..430600c
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/RestrictContentTypeTest.java
@@ -0,0 +1,51 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+public class RestrictContentTypeTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(RestrictContentTypeTest.class);
+	}
+
+	public void testNoContentType() throws Exception {
+
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-content-type-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_OK);
+	}
+	
+	public void testGoodContentType() throws Exception {
+		request.addHeader("Content-Type","text/html");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-content-type-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_OK);
+	}
+	
+	public void testBadContentType() throws Exception {
+		request.addHeader("Content-Type","multipart/form-upload");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-content-type-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY);
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/RestrictExtensionTest.java b/src/test/java/org/owasp/esapi/waf/RestrictExtensionTest.java
new file mode 100644
index 0000000..7aee58e
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/RestrictExtensionTest.java
@@ -0,0 +1,57 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class RestrictExtensionTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(RestrictExtensionTest.class);
+	}
+	
+	public void testGoodExtension() throws Exception {
+        
+    	System.out.println("restrictExtensionPolicy - approve this URL (doesn't end in .log or anything else evil)" );
+
+        request = new MockHttpServletRequest( new URL( "http://www.example.com/logfiles/12192009.jpg" ) );
+        request.getSession(true); // pass HttpOnly test...
+    	response = new MockHttpServletResponse();
+        
+        WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-extension-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() != HttpServletResponse.SC_MOVED_PERMANENTLY );
+    }
+
+	public void testBadExtension() throws Exception {
+        
+    	System.out.println("restrictExtensionPolicy - reject any URL ending in .log" );
+
+        MockHttpServletRequest request = new MockHttpServletRequest( new URL( "http://www.example.com/logfiles/12192009.log" ) );
+    	MockHttpServletResponse response = new MockHttpServletResponse();
+        
+        WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-extension-policy.xml", request, response );
+    	
+    	assertTrue( response.getStatus() == HttpServletResponse.SC_MOVED_PERMANENTLY );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/RestrictMethodTest.java b/src/test/java/org/owasp/esapi/waf/RestrictMethodTest.java
new file mode 100644
index 0000000..0203bb8
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/RestrictMethodTest.java
@@ -0,0 +1,56 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+public class RestrictMethodTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(RestrictMethodTest.class);
+	}
+	
+	public void testGoodMethod() throws Exception {
+		// test good method
+	    url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "\nTest good method: " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.setMethod( "TRACE" );
+	    request.getSession(true); // so the app will pass the HttpOnly test...
+	    
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest(waf, request, response, HttpServletResponse.SC_OK );
+	}
+	
+	public void testBadMethod() throws Exception {
+		// test bad method
+	    url = new URL( "http://www.example.com/index.jsp" );
+		System.out.println( "\nTest bad method: " + url );
+	    request = new MockHttpServletRequest( url );
+	    request.setMethod( "JEFF" );
+	    request.getSession(true); // so the app will pass the HttpOnly test...
+	    
+		response = new MockHttpServletResponse();
+		createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );
+	}    
+}
diff --git a/src/test/java/org/owasp/esapi/waf/RestrictUserAgentTest.java b/src/test/java/org/owasp/esapi/waf/RestrictUserAgentTest.java
new file mode 100644
index 0000000..1cd641d
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/RestrictUserAgentTest.java
@@ -0,0 +1,46 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestSuite;
+
+public class RestrictUserAgentTest extends WAFTestCase {
+	
+	public static TestSuite suite() {
+		return new TestSuite(RestrictUserAgentTest.class);
+	}
+	
+	public void testBadUserAgent() throws Exception {
+		
+		request.addHeader("User-Agent","GoogleBot");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-user-agent-policy.xml", request, response );
+		
+		assert(response.getStatus() == 403);
+	}
+	
+	public void testGoodUserAgent() throws Exception {
+		
+		request.addHeader("User-Agent","MSIE NT Compatible");
+		
+		WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/restrict-user-agent-policy.xml", request, response );
+    	
+		assert(response.getStatus() == HttpServletResponse.SC_OK);
+	}
+	
+}
diff --git a/src/test/java/org/owasp/esapi/waf/VirtualPatchTest.java b/src/test/java/org/owasp/esapi/waf/VirtualPatchTest.java
new file mode 100644
index 0000000..4be65ef
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/VirtualPatchTest.java
@@ -0,0 +1,60 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+import junit.framework.TestSuite;
+
+public class VirtualPatchTest extends WAFTestCase {
+
+	public static TestSuite suite() {
+		return new TestSuite(VirtualPatchTest.class);
+	}
+	
+	public void testNonAttacktAfterVirtualPatch() throws Exception {
+		// should pass
+        url = new URL( "https://www.example.com/virtualpatch.jsp" );
+		System.out.println( "Testing non-attack after virtual patch on URL: " + url );
+        request = new MockHttpServletRequest( url );
+        request.getSession(true);
+        request.setScheme("https");
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+        request.addParameter("bar", "09124asd135r123irh2938rh9c82hr3hareohvw"); // alphanums are allowed
+        request.addParameter("foo", "<script>' oR 1=1-- bad.attax.google.com jar:"); // this parameter should not be touched by the patch
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_OK );
+	}
+	
+	public void testAttackAfterVirtualPatch() throws Exception {
+		// should fail
+        url = new URL( "https://www.example.com/foo.jsp" );
+		System.out.println( "Testing attack after virtual patch on URL: " + url );
+        request = new MockHttpServletRequest( url );
+        request.getSession(true);
+        request.setScheme("https");
+        request.getSession().setAttribute("ESAPIUserSessionKey", user);
+        request.addParameter("bar", "09124asd135r123ir>h2938rh9c82hr3hareohvw"); // non-alphanums are not allowed (there is 1 in the middle)
+        request.addParameter("foo", "SADFSDfSDFSDF123123123"); // this parameter should not be touched by the patch
+    	response = new MockHttpServletResponse();
+    	createAndExecuteWAFResponseCodeTest( waf, request, response, HttpServletResponse.SC_MOVED_PERMANENTLY );
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/WAFFilterTest.java b/src/test/java/org/owasp/esapi/waf/WAFFilterTest.java
new file mode 100644
index 0000000..fdb9f08
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/WAFFilterTest.java
@@ -0,0 +1,81 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * This is the main TestSuite for all the WAF tests. Some of the WAF
+ * tests utilize a large policy file containing a bunch of unrelated
+ * rules, and some use very small policy files that only exercise
+ * specific functionality. Some may use both. 
+ * 
+ * There is an unlimited combination of rules to be exercised together, 
+ * so the small policy files test the strict functionality, while the
+ * larger policy files (hopefully) give us assurance that the rules 
+ * won't interfere with each other.
+ */
+
+public class WAFFilterTest extends TestCase {
+    
+    /**
+	 * Instantiates a new WAF test.
+	 * 
+	 * @param testName the test name
+	 */
+    public WAFFilterTest(String testName) {
+        super(testName);
+    }
+
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+
+    	TestSuite suite = new TestSuite(WAFFilterTest.class);
+
+        suite.addTest(AddHeaderTest.suite());
+        suite.addTest(BeanShellTest.suite());
+        suite.addTest(DetectOutboundTest.suite());
+        suite.addTest(EnforceAuthenticationTest.suite());
+        suite.addTest(EnforceHTTPSTest.suite());
+        suite.addTest(GoodRequestTest.suite());
+        suite.addTest(HttpOnlyTest.suite());
+        suite.addTest(MustMatchTest.suite());
+        suite.addTest(DynamicInsertionTest.suite());
+        suite.addTest(RestrictContentTypeTest.suite());
+        suite.addTest(RestrictExtensionTest.suite());
+        suite.addTest(RestrictMethodTest.suite());
+        suite.addTest(RestrictUserAgentTest.suite());
+        suite.addTest(VirtualPatchTest.suite());
+        
+        return suite;
+    }
+    
+    public void testConfigurationCanBeRead() throws Exception {
+    	
+    	ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter();
+    	WAFTestUtility.setWAFPolicy(waf, "waf-policy.xml");
+
+    }
+
+}
diff --git a/src/test/java/org/owasp/esapi/waf/WAFTestCase.java b/src/test/java/org/owasp/esapi/waf/WAFTestCase.java
new file mode 100644
index 0000000..c0c63f7
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/WAFTestCase.java
@@ -0,0 +1,65 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.net.URL;
+
+import org.owasp.esapi.Authenticator;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.EncoderConstants;
+import org.owasp.esapi.User;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+import junit.framework.TestCase;
+
+public abstract class WAFTestCase extends TestCase {
+
+	protected MockHttpServletRequest request;
+	protected MockHttpServletResponse response;
+	protected URL url;
+	protected ESAPIWebApplicationFirewallFilter waf;
+	
+	protected static User user = null;
+	
+	public void setUp() throws Exception {
+	    // setup the user in session
+		
+		if ( user == null ) {
+			String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
+			Authenticator instance = ESAPI.authenticator();
+			String password = instance.generateStrongPassword();
+			instance.setCurrentUser(user);
+			user = instance.createUser(accountName, password, password);
+			user.enable();
+		}
+		
+		request = new MockHttpServletRequest( new URL( "http://www.example.com/index" ) );
+        response = new MockHttpServletResponse();
+        waf = new ESAPIWebApplicationFirewallFilter();
+        
+        WAFTestUtility.setWAFPolicy(waf, "/waf-policy.xml");
+	}
+    
+	public void createAndExecuteWAFResponseCodeTest( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, int expectedResult ) throws Exception {
+    	assertEquals ( expectedResult, WAFTestUtility.createAndExecuteWAFTransaction( waf, request, response) );	
+	}
+    
+    public void createAndExecuteWAFResponseCodeTest( String policy, MockHttpServletRequest request, MockHttpServletResponse response, int expectedResult ) throws Exception {
+    	assertEquals ( expectedResult, WAFTestUtility.createAndExecuteWAFTransaction( policy, request, response) );	
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java b/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java
new file mode 100644
index 0000000..3a58b38
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java
@@ -0,0 +1,91 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2009 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Arshan Dabirsiaghi <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * 
+ * @created 2009
+ */
+package org.owasp.esapi.waf;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.FilterConfig;
+
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.http.MockFilterChain;
+import org.owasp.esapi.http.MockFilterConfig;
+import org.owasp.esapi.http.MockHttpServletRequest;
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * This class holds a number of static utilities to make writing WAF test cases easy. Definitely not useful
+ * for anything other than testing.
+ */
+public class WAFTestUtility {
+
+    public static void setWAFPolicy( ESAPIWebApplicationFirewallFilter waf, String policyFile ) throws Exception {
+        Map map = new HashMap();
+    	map.put( "configuration", policyFile );
+    	map.put( "log_settings", "../log4j.xml");
+    	FilterConfig mfc = new MockWafFilterConfig( map );
+    	waf.init( mfc );
+    }
+    
+    public static int checkWAFResult( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain chain ) throws Exception {
+
+        //request.dump();
+    	waf.doFilter(request, response, chain);
+        //response.dump();
+        
+        return response.getStatus();
+       
+    }  
+
+    public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception {
+
+		MockFilterChain chain = new MockFilterChain();
+		
+		return WAFTestUtility.checkWAFResult(waf, request, response, chain);
+		
+	}
+
+    public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception {
+
+
+    	return WAFTestUtility.checkWAFResult(waf, request, response, filterChain);
+		
+	}
+    
+    public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception {
+
+    	ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter();
+    	File f = ESAPI.securityConfiguration().getResourceFile(policy);
+    	waf.setConfiguration(f.getAbsolutePath(),"");
+
+		return createAndExecuteWAFTransaction(waf, request, response );
+		
+	}
+    
+    public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception {
+
+    	ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter();
+    	File f = ESAPI.securityConfiguration().getResourceFile(policy);        
+    	waf.setConfiguration(f.getAbsolutePath(),"");
+
+		return createAndExecuteWAFTransaction(waf, request, response, filterChain );
+		
+	}
+}
diff --git a/src/test/java/org/owasp/esapi/waf/internal/.svn/all-wcprops b/src/test/java/org/owasp/esapi/waf/internal/.svn/all-wcprops
new file mode 100644
index 0000000..9556cf1
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/internal/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/internal
+END
+InterceptingHttpServletRequestTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletRequestTest.java
+END
+InterceptingHttpServletResponseTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletResponseTest.java
+END
diff --git a/src/test/java/org/owasp/esapi/waf/internal/.svn/entries b/src/test/java/org/owasp/esapi/waf/internal/.svn/entries
new file mode 100644
index 0000000..0f28896
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/internal/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/java/org/owasp/esapi/waf/internal
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-07-10T05:22:23.827015Z
+565
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+InterceptingHttpServletRequestTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+c000b099bbf506efdb3e0a26eb867a7b
+2009-05-10T04:33:18.539487Z
+510
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2042
+

+InterceptingHttpServletResponseTest.java
+file
+
+
+
+
+2014-02-18T16:19:52.373955Z
+0b47e7054d7d93da5b525ab07f11235b
+2009-07-10T05:22:23.827015Z
+565
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2281
+

diff --git a/src/test/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHttpServletRequestTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHttpServletRequestTest.java.svn-base
new file mode 100644
index 0000000..f6b1370
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHttpServletRequestTest.java.svn-base
@@ -0,0 +1,76 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.waf.internal;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class InterceptingHttpServletRequestTest extends TestCase {
+
+    /**
+	 * Instantiates a new test.
+	 *
+	 * @param testName
+	 *            the test name
+	 */
+    public InterceptingHttpServletRequestTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 *
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(InterceptingHttpServletRequestTest.class);
+        return suite;
+    }
+
+
+    /**
+	 * Test.
+     */
+    public void testRequest() throws Exception {
+        System.out.println("InterceptingHTTPServletRequest");
+   	    MockHttpServletRequest mreq = new MockHttpServletRequest();
+   	    mreq.setMethod( "GET" );
+        InterceptingHTTPServletRequest ireq = new InterceptingHTTPServletRequest(mreq);
+        assertEquals( mreq.getMethod(), ireq.getMethod() );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHttpServletResponseTest.java.svn-base b/src/test/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHttpServletResponseTest.java.svn-base
new file mode 100644
index 0000000..20a97bc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/internal/.svn/text-base/InterceptingHttpServletResponseTest.java.svn-base
@@ -0,0 +1,80 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class InterceptingHttpServletResponseTest extends TestCase {
+    
+    /**
+	 * Instantiates a new test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public InterceptingHttpServletResponseTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(InterceptingHttpServletResponseTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test.
+     */
+    public void testRequest() throws Exception {
+        System.out.println("InterceptingHTTPServletResponse");
+   	    MockHttpServletResponse mres = new MockHttpServletResponse();
+        InterceptingHTTPServletResponse ires = new InterceptingHTTPServletResponse(mres, false, new ArrayList() );
+        // isos.println( "Hello" );
+        // ires.getOutputStream().println( "Hello" );
+        // ires.getWriter().println("Hello");
+        // assertEquals( "Hello\r\n", new String( ires.getInterceptingServletOutputStream().getResponseBytes() ) );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletRequestTest.java b/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletRequestTest.java
new file mode 100644
index 0000000..f6b1370
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletRequestTest.java
@@ -0,0 +1,76 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.waf.internal;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletRequest;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class InterceptingHttpServletRequestTest extends TestCase {
+
+    /**
+	 * Instantiates a new test.
+	 *
+	 * @param testName
+	 *            the test name
+	 */
+    public InterceptingHttpServletRequestTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 *
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(InterceptingHttpServletRequestTest.class);
+        return suite;
+    }
+
+
+    /**
+	 * Test.
+     */
+    public void testRequest() throws Exception {
+        System.out.println("InterceptingHTTPServletRequest");
+   	    MockHttpServletRequest mreq = new MockHttpServletRequest();
+   	    mreq.setMethod( "GET" );
+        InterceptingHTTPServletRequest ireq = new InterceptingHTTPServletRequest(mreq);
+        assertEquals( mreq.getMethod(), ireq.getMethod() );
+    }
+}
diff --git a/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletResponseTest.java b/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletResponseTest.java
new file mode 100644
index 0000000..20a97bc
--- /dev/null
+++ b/src/test/java/org/owasp/esapi/waf/internal/InterceptingHttpServletResponseTest.java
@@ -0,0 +1,80 @@
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ * 
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ * 
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ * 
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.owasp.esapi.waf.internal;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.owasp.esapi.http.MockHttpServletResponse;
+
+/**
+ * @author Jeff Williams (jeff.williams at aspectsecurity.com)
+ */
+public class InterceptingHttpServletResponseTest extends TestCase {
+    
+    /**
+	 * Instantiates a new test.
+	 * 
+	 * @param testName
+	 *            the test name
+	 */
+    public InterceptingHttpServletResponseTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void setUp() throws Exception {
+    	// none
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+    	// none
+    }
+
+    /**
+	 * Suite.
+	 * 
+	 * @return the test
+	 */
+    public static Test suite() {
+        TestSuite suite = new TestSuite(InterceptingHttpServletResponseTest.class);
+        return suite;
+    }
+
+    
+    /**
+	 * Test.
+     */
+    public void testRequest() throws Exception {
+        System.out.println("InterceptingHTTPServletResponse");
+   	    MockHttpServletResponse mres = new MockHttpServletResponse();
+        InterceptingHTTPServletResponse ires = new InterceptingHTTPServletResponse(mres, false, new ArrayList() );
+        // isos.println( "Hello" );
+        // ires.getOutputStream().println( "Hello" );
+        // ires.getWriter().println("Hello");
+        // assertEquals( "Hello\r\n", new String( ires.getInterceptingServletOutputStream().getResponseBytes() ) );
+    }
+}
diff --git a/src/test/resources/.svn/all-wcprops b/src/test/resources/.svn/all-wcprops
new file mode 100644
index 0000000..c679a40
--- /dev/null
+++ b/src/test/resources/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources
+END
+log4j.xml
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/log4j.xml
+END
+log4j.dtd
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/log4j.dtd
+END
+Readme.txt
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/Readme.txt
+END
+ESAPI2.0-ciphertext-portable.ser
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/ESAPI2.0-ciphertext-portable.ser
+END
diff --git a/src/test/resources/.svn/entries b/src/test/resources/.svn/entries
new file mode 100644
index 0000000..82ecc66
--- /dev/null
+++ b/src/test/resources/.svn/entries
@@ -0,0 +1,170 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/resources
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-31T22:38:40.250368Z
+1891
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+log4j.dtd
+file
+
+
+
+
+2014-02-18T16:19:52.509957Z
+8ac4c1b0a927e184ef859bb4366dc0a6
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6718
+

+Readme.txt
+file
+
+
+
+
+2014-02-18T16:19:52.509957Z
+f6d290fc37ef2d3df11358f110fc4e8e
+2013-08-31T22:38:40.250368Z
+1891
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+256
+

+properties
+dir
+

+ESAPI2.0-ciphertext-portable.ser
+file
+
+
+
+
+2014-02-18T16:19:52.509957Z
+c587991eec22615a8428b88aec377b10
+2013-08-31T22:38:40.250368Z
+1891
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+114
+

+log4j.xml
+file
+
+
+
+
+2014-02-18T16:19:52.509957Z
+65140367922494355e555e8509b53364
+2011-03-23T16:19:55.345993Z
+1746
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1599
+

+esapi
+dir
+

diff --git a/src/test/resources/.svn/prop-base/ESAPI2.0-ciphertext-portable.ser.svn-base b/src/test/resources/.svn/prop-base/ESAPI2.0-ciphertext-portable.ser.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/src/test/resources/.svn/prop-base/ESAPI2.0-ciphertext-portable.ser.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/src/test/resources/.svn/text-base/ESAPI2.0-ciphertext-portable.ser.svn-base b/src/test/resources/.svn/text-base/ESAPI2.0-ciphertext-portable.ser.svn-base
new file mode 100644
index 0000000..dfe173c
Binary files /dev/null and b/src/test/resources/.svn/text-base/ESAPI2.0-ciphertext-portable.ser.svn-base differ
diff --git a/src/test/resources/.svn/text-base/Readme.txt.svn-base b/src/test/resources/.svn/text-base/Readme.txt.svn-base
new file mode 100644
index 0000000..1bb4585
--- /dev/null
+++ b/src/test/resources/.svn/text-base/Readme.txt.svn-base
@@ -0,0 +1,5 @@
+ESAPI2.0-ciphertext-portable.ser is a test for the backwards compatibility
+of ESAPI 2.1.x and later crypto with ESAPI 2.0 crypto.
+
+The file was created on Windows using the 128-bit test key from
+Encryptor.MasterKey and encrypted with AES/CBC/PKCS5Padding.
diff --git a/src/test/resources/.svn/text-base/log4j.dtd.svn-base b/src/test/resources/.svn/text-base/log4j.dtd.svn-base
new file mode 100644
index 0000000..1aabd96
--- /dev/null
+++ b/src/test/resources/.svn/text-base/log4j.dtd.svn-base
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Authors: Chris Taylor, Ceki Gulcu. -->
+
+<!-- Version: 1.2 -->
+
+<!-- A configuration element consists of optional renderer
+elements,appender elements, categories and an optional root
+element. -->
+
+<!ELEMENT log4j:configuration (renderer*, appender*,plugin*, (category|logger)*,root?,
+                               (categoryFactory|loggerFactory)?)>
+
+<!-- The "threshold" attribute takes a level value below which -->
+<!-- all logging statements are disabled. -->
+
+<!-- Setting the "debug" enable the printing of internal log4j logging   -->
+<!-- statements.                                                         -->
+
+<!-- By default, debug attribute is "null", meaning that we not do touch -->
+<!-- internal log4j logging settings. The "null" value for the threshold -->
+<!-- attribute can be misleading. The threshold field of a repository	 -->
+<!-- cannot be set to null. The "null" value for the threshold attribute -->
+<!-- simply means don't touch the threshold field, the threshold field   --> 
+<!-- keeps its old value.                                                -->
+     
+<!ATTLIST log4j:configuration
+  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  threshold                (all|trace|debug|info|warn|error|fatal|off|null) "null"
+  debug                    (true|false|null)  "null"
+  reset                    (true|false) "false"
+>
+
+<!-- renderer elements allow the user to customize the conversion of  -->
+<!-- message objects to String.                                       -->
+
+<!ELEMENT renderer EMPTY>
+<!ATTLIST renderer
+  renderedClass  CDATA #REQUIRED
+  renderingClass CDATA #REQUIRED
+>
+
+<!-- Appenders must have a name and a class. -->
+<!-- Appenders may contain an error handler, a layout, optional parameters -->
+<!-- and filters. They may also reference (or include) other appenders. -->
+<!ELEMENT appender (errorHandler?, param*,
+      rollingPolicy?, triggeringPolicy?, connectionSource?,
+      layout?, filter*, appender-ref*)>
+<!ATTLIST appender
+  name 		CDATA 	#REQUIRED
+  class 	CDATA	#REQUIRED
+>
+
+<!ELEMENT layout (param*)>
+<!ATTLIST layout
+  class		CDATA	#REQUIRED
+>
+
+<!ELEMENT filter (param*)>
+<!ATTLIST filter
+  class		CDATA	#REQUIRED
+>
+
+<!-- ErrorHandlers can be of any class. They can admit any number of -->
+<!-- parameters. -->
+
+<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)> 
+<!ATTLIST errorHandler
+   class        CDATA   #REQUIRED 
+>
+
+<!ELEMENT root-ref EMPTY>
+
+<!ELEMENT logger-ref EMPTY>
+<!ATTLIST logger-ref
+  ref CDATA #REQUIRED
+>
+
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+  name		CDATA   #REQUIRED
+  value		CDATA	#REQUIRED
+>
+
+
+<!-- The priority class is org.apache.log4j.Level by default -->
+<!ELEMENT priority (param*)>
+<!ATTLIST priority
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+<!-- The level class is org.apache.log4j.Level by default -->
+<!ELEMENT level (param*)>
+<!ATTLIST level
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named category. -->
+<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
+<!ATTLIST category
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named logger. -->
+<!ELEMENT logger (level?,appender-ref*)>
+<!ATTLIST logger
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+
+<!ELEMENT categoryFactory (param*)>
+<!ATTLIST categoryFactory 
+   class        CDATA #REQUIRED>
+
+<!ELEMENT loggerFactory (param*)>
+<!ATTLIST loggerFactory
+   class        CDATA #REQUIRED>
+
+<!ELEMENT appender-ref EMPTY>
+<!ATTLIST appender-ref
+  ref CDATA #REQUIRED
+>
+
+<!-- plugins must have a name and class and can have optional parameters -->
+<!ELEMENT plugin (param*, connectionSource?)>
+<!ATTLIST plugin
+  name 		CDATA 	   #REQUIRED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT connectionSource (dataSource?, param*)>
+<!ATTLIST connectionSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT dataSource (param*)>
+<!ATTLIST dataSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT triggeringPolicy ((param|filter)*)>
+<!ATTLIST triggeringPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT rollingPolicy (param*)>
+<!ATTLIST rollingPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+
+<!-- If no priority element is specified, then the configurator MUST not -->
+<!-- touch the priority of root. -->
+<!-- The root category always exists and cannot be subclassed. -->
+<!ELEMENT root (param*, (priority|level)?, appender-ref*)>
+
+
+<!-- ==================================================================== -->
+<!--                       A logging event                                -->
+<!-- ==================================================================== -->
+<!ELEMENT log4j:eventSet (log4j:event*)>
+<!ATTLIST log4j:eventSet
+  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  version                (1.1|1.2) "1.2" 
+  includesLocationInfo   (true|false) "true"
+>
+
+
+
+<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?, 
+                       log4j:locationInfo?, log4j:properties?) >
+
+<!-- The timestamp format is application dependent. -->
+<!ATTLIST log4j:event
+    logger     CDATA #REQUIRED
+    level      CDATA #REQUIRED
+    thread     CDATA #REQUIRED
+    timestamp  CDATA #REQUIRED
+    time       CDATA #IMPLIED
+>
+
+<!ELEMENT log4j:message (#PCDATA)>
+<!ELEMENT log4j:NDC (#PCDATA)>
+
+<!ELEMENT log4j:throwable (#PCDATA)>
+
+<!ELEMENT log4j:locationInfo EMPTY>
+<!ATTLIST log4j:locationInfo
+  class  CDATA	#REQUIRED
+  method CDATA	#REQUIRED
+  file   CDATA	#REQUIRED
+  line   CDATA	#REQUIRED
+>
+
+<!ELEMENT log4j:properties (log4j:data*)>
+
+<!ELEMENT log4j:data EMPTY>
+<!ATTLIST log4j:data
+  name   CDATA	#REQUIRED
+  value  CDATA	#REQUIRED
+>
diff --git a/src/test/resources/.svn/text-base/log4j.xml.svn-base b/src/test/resources/.svn/text-base/log4j.xml.svn-base
new file mode 100644
index 0000000..04c2b43
--- /dev/null
+++ b/src/test/resources/.svn/text-base/log4j.xml.svn-base
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!-- main resources -->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+  <appender name="console" class="org.apache.log4j.ConsoleAppender"> 
+    <param name="Target" value="System.out"/> 
+    <layout class="org.apache.log4j.PatternLayout"> 
+      <param name="ConversionPattern" value="%-5p %m%n"/> 
+    </layout> 
+  </appender>
+
+    <appender name="file" class="org.apache.log4j.FileAppender">
+        <param name="File" value="target/unit-tests.log"/>
+        <layout class="org.apache.log4j.PatternLayout">
+          <param name="ConversionPattern" value="%-5p %m%n"/>
+        </layout>
+    </appender>
+
+  <logger name="org.owasp.esapi.reference.TestTrace">
+    <level value="trace"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestDebug">
+    <level value="debug"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestInfo">
+    <level value="info"/>
+ </logger>
+
+  <logger name="org.owasp.esapi.reference.TestWarning">
+    <level value="warn"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestError">
+    <level value="error"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestFatal">
+    <level value="fatal"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference">
+    <level value="info"/>
+  </logger>
+
+  <root>
+    <priority value="debug" />
+    <appender-ref ref="file" />
+  </root>
+
+  <loggerFactory class="org.owasp.esapi.reference.Log4JLoggerFactory"/>
+
+</log4j:configuration>
diff --git a/src/test/resources/ESAPI2.0-ciphertext-portable.ser b/src/test/resources/ESAPI2.0-ciphertext-portable.ser
new file mode 100644
index 0000000..dfe173c
Binary files /dev/null and b/src/test/resources/ESAPI2.0-ciphertext-portable.ser differ
diff --git a/src/test/resources/Readme.txt b/src/test/resources/Readme.txt
new file mode 100644
index 0000000..1bb4585
--- /dev/null
+++ b/src/test/resources/Readme.txt
@@ -0,0 +1,5 @@
+ESAPI2.0-ciphertext-portable.ser is a test for the backwards compatibility
+of ESAPI 2.1.x and later crypto with ESAPI 2.0 crypto.
+
+The file was created on Windows using the 128-bit test key from
+Encryptor.MasterKey and encrypted with AES/CBC/PKCS5Padding.
diff --git a/src/test/resources/esapi/.svn/all-wcprops b/src/test/resources/esapi/.svn/all-wcprops
new file mode 100644
index 0000000..72eeb40
--- /dev/null
+++ b/src/test/resources/esapi/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi
+END
+users.txt
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/users.txt
+END
+ESAPI-AccessControlPolicy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 90
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml
+END
+antisamy-esapi.xml
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/antisamy-esapi.xml
+END
+validation.properties
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/validation.properties
+END
+waf-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policy.xml
+END
+ESAPI.properties
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/ESAPI.properties
+END
diff --git a/src/test/resources/esapi/.svn/entries b/src/test/resources/esapi/.svn/entries
new file mode 100644
index 0000000..d230103
--- /dev/null
+++ b/src/test/resources/esapi/.svn/entries
@@ -0,0 +1,238 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/resources/esapi
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-29T00:58:32.384605Z
+1885
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+ESAPI.properties
+file
+
+
+
+
+2014-02-18T16:19:52.501957Z
+8ed13c9214dc175b7a39081d2fcd6ab1
+2013-08-29T00:58:32.384605Z
+1885
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+25105
+

+users.txt
+file
+
+
+
+
+2014-02-18T16:19:52.501957Z
+d41d8cd98f00b204e9800998ecf8427e
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+0
+

+fbac-policies
+dir
+

+ESAPI-AccessControlPolicy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.501957Z
+05b199b9241e5fc2fdf41f8b85698ead
+2009-04-17T20:10:12.050344Z
+466
+mikehfauzy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5357
+

+waf-policies
+dir
+

+antisamy-esapi.xml
+file
+
+
+
+
+2014-02-18T16:19:52.501957Z
+1ae3bef5c1f2cf729dbbbe00585a6446
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+31816
+

+validation.properties
+file
+
+
+
+
+2014-02-18T16:19:52.501957Z
+88f9ba326a0102788da15201535d3271
+2010-11-07T05:06:49.492325Z
+1652
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1646
+

+waf-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.501957Z
+fb933b29e0625f3267af3c4470409d9e
+2009-11-20T00:33:25.026048Z
+832
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4214
+

diff --git a/src/test/resources/esapi/.svn/text-base/ESAPI-AccessControlPolicy.xml.svn-base b/src/test/resources/esapi/.svn/text-base/ESAPI-AccessControlPolicy.xml.svn-base
new file mode 100644
index 0000000..2ed0732
--- /dev/null
+++ b/src/test/resources/esapi/.svn/text-base/ESAPI-AccessControlPolicy.xml.svn-base
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<AccessControlPolicy>
+	<AccessControlRules>
+		<AccessControlRule
+			name="AlwaysTrue"
+			description="Access is always granted"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="AlwaysFalse"
+			description="Access is always denied"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameter"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoRuntimeParameterACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoPolicyParameter"
+			description="Access depends on the value of the policy parameter: isTrue"
+			class="org.owasp.esapi.reference.accesscontrol.policyloader.EchoDynaBeanPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="true"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- 
+			The following Rules are for backwards compatibility with
+			the deprecated AcessController 1.0 reference implementation
+			specification
+		-->
+		<AccessControlRule
+			name="AC 1.0 Data"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/> 
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForData"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String, java.lang.Object"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 File"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFile"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Function"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFunction"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Service"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForService"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 URL"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForURL"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- End Rules for backwards compatibility with Access Controller 1.0 -->
+	</AccessControlRules>
+</AccessControlPolicy>
+
+
+
+<!-- 
+We have these as runtime tests, but not as policy file load tests yet.
+		<AccessControlRule
+			name="EchoRuntimeParameterClassCastException"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="This is not a boolean"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueNull"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="null"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueEmpty"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value=""/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueMissing"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean"/>
+			</Parameters>
+		</AccessControlRule>
+-->
+
+<!-- we should add tests for name and type errors too -->
diff --git a/src/test/resources/esapi/.svn/text-base/ESAPI.properties.svn-base b/src/test/resources/esapi/.svn/text-base/ESAPI.properties.svn-base
new file mode 100644
index 0000000..1435b7c
--- /dev/null
+++ b/src/test/resources/esapi/.svn/text-base/ESAPI.properties.svn-base
@@ -0,0 +1,465 @@
+#
+# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
+# 
+# This file is part of the Open Web Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2008,2009 - The OWASP Foundation
+#
+# DISCUSS: This may cause a major backwards compatibility issue, etc. but
+#		   from a name space perspective, we probably should have prefaced
+#		   all the property names with ESAPI or at least OWASP. Otherwise
+#		   there could be problems is someone loads this properties file into
+#		   the System properties.  We could also put this file into the
+#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
+#		   ESAPI properties be defined that would overwrite these defaults.
+#		   That keeps the application's properties relatively simple as usually
+#		   they will only want to override a few properties. If looks like we
+#		   already support multiple override levels of this in the
+#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
+#		   defaults in the esapi.jar itself. That way, if the jar is signed,
+#		   we could detect if those properties had been tampered with. (The
+#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
+#		   but off course there is an execution penalty (similar to the way
+#		   that the separate sunjce.jar used to be when a class from it was
+#		   first loaded). Thoughts?
+###############################################################################
+#
+# WARNING: Operating system protection should be used to lock down the .esapi
+# resources directory and all the files inside and all the directories all the
+# way up to the root directory of the file system.  Note that if you are using
+# file-based implementations, that some files may need to be read-write as they
+# get updated dynamically.
+#
+# Before using, be sure to update the MasterKey and MasterSalt as described below.
+# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
+#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
+#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
+#		able to decrypt your data with ESAPI 2.0.
+#
+#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
+#
+#===========================================================================
+# ESAPI Configuration
+#
+# If true, then print all the ESAPI properties set here when they are loaded.
+# If false, they are not printed. Useful to reduce output when running JUnit tests.
+# If you need to troubleshoot a properties related problem, turning this on may help,
+# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
+# as part of production ESAPI, mostly for backward compatibility.)
+ESAPI.printProperties=false
+
+# ESAPI is designed to be easily extensible. You can use the reference implementation
+# or implement your own providers to take advantage of your enterprise's security
+# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
+#
+#    String ciphertext =
+#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
+#    CipherText cipherText =
+#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
+#
+# Below you can specify the classname for the provider that you wish to use in your
+# application. The only requirement is that it implement the appropriate ESAPI interface.
+# This allows you to switch security implementations in the future without rewriting the
+# entire application.
+#
+# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
+ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
+# FileBasedAuthenticator requires users.txt file in .esapi directory
+ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
+ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
+ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
+
+ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
+ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
+ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
+# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
+ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
+#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
+#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
+ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
+ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
+
+#===========================================================================
+# ESAPI Authenticator
+#
+Authenticator.AllowedLoginAttempts=3
+Authenticator.MaxOldPasswordHashes=13
+Authenticator.UsernameParameterName=username
+Authenticator.PasswordParameterName=password
+# RememberTokenDuration (in days)
+Authenticator.RememberTokenDuration=14
+# Session Timeouts (in minutes)
+Authenticator.IdleTimeoutDuration=20
+Authenticator.AbsoluteTimeoutDuration=120
+
+#===========================================================================
+# ESAPI Encoder
+#
+# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
+# Failure to canonicalize input is a very common mistake when implementing validation schemes.
+# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
+# following code to canonicalize data.
+#
+#      ESAPI.Encoder().canonicalize( "%22hello world&#x22;" );
+#  
+# Multiple encoding is when a single encoding format is applied multiple times. Allowing
+# multiple encoding is strongly discouraged.
+Encoder.AllowMultipleEncoding=false
+
+# Mixed encoding is when multiple different encoding formats are applied, or when
+# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
+Encoder.AllowMixedEncoding=false
+
+# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
+# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
+# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
+Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
+
+
+#===========================================================================
+# ESAPI Encryption
+#
+# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
+# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+# There is not currently any support for key rotation, so be careful when changing your key and salt as it
+# will invalidate all signed, encrypted, and hashed data.
+#
+# WARNING: Not all combinations of algorithms and key lengths are supported.
+# If you choose to use a key length greater than 128, you MUST download the
+# unlimited strength policy files and install in the lib directory of your JRE/JDK.
+# See http://java.sun.com/javase/downloads/index.jsp for more information.
+#
+# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
+# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
+# possible, these methods should be avoided as they use ECB cipher mode, which in almost
+# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
+# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
+# should only use this compatibility setting if you have persistent data encrypted with
+# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
+# you have decrypted all of your old encrypted data and then re-encrypted it with
+# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
+# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
+# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
+# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
+# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
+# that requires downloading the special jurisdiction policy files mentioned above.)
+#
+#		***** IMPORTANT: These are for JUnit testing. Test files may have been
+#						 encrypted using these values so do not change these or
+#						 those tests will fail. The version under
+#							src/main/resources/.esapi/ESAPI.properties
+#						 will be delivered with Encryptor.MasterKey and
+#						 Encryptor.MasterSalt set to the empty string.
+#
+#						 FINAL NOTE:
+#                           If Maven changes these when run, that needs to be fixed.
+#       256-bit key... requires unlimited strength jurisdiction policy files
+### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
+#       128-bit key
+Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
+Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
+# Encryptor.MasterSalt=
+
+# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
+# encryption and hashing. (That is it will look to this provider first, but it
+# will defer to other providers if the requested algorithm is not implemented
+# by this provider.) If left unset, ESAPI will just use your Java VM's current
+# preferred JCE provider, which is generally set in the file
+# "$JAVA_HOME/jre/lib/security/java.security".
+#
+# The main intent of this is to allow ESAPI symmetric encryption to be
+# used with a FIPS 140-2 compliant crypto-module. For details, see the section
+# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
+# the ESAPI 2.0 Symmetric Encryption User Guide, at:
+# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
+# However, this property also allows you to easily use an alternate JCE provider
+# such as "Bouncy Castle" without having to make changes to "java.security".
+# See Javadoc for SecurityProviderLoader for further details. If you wish to use
+# a provider that is not known to SecurityProviderLoader, you may specify the
+# fully-qualified class name of the JCE provider class that implements
+# java.security.Provider. If the name contains a '.', this is interpreted as
+# a fully-qualified class name that implements java.security.Provider.
+#
+# NOTE: Setting this property has the side-effect of changing it in your application
+#       as well, so if you are using JCE in your application directly rather than
+#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
+#       preferred JCE provider there as well.
+#
+# Default: Keeps the JCE provider set to whatever JVM sets it to.
+Encryptor.PreferredJCEProvider=
+
+# AES is the most widely used and strongest encryption algorithm. This
+# should agree with your Encryptor.CipherTransformation property.
+# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
+# very weak. It is essentially a password-based encryption key, hashed
+# with MD5 around 1K times and then encrypted with the weak DES algorithm
+# (56-bits) using ECB mode and an unspecified padding (it is
+# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
+# "AES/CBC/PKCSPadding". If you want to change these, change them here.
+# Warning: This property does not control the default reference implementation for
+#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
+#		   in the future.
+# @deprecated
+Encryptor.EncryptionAlgorithm=AES
+#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
+Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
+
+# Applies to ESAPI 2.0 and later only!
+# Comma-separated list of cipher modes that provide *BOTH*
+# confidentiality *AND* message authenticity. (NIST refers to such cipher
+# modes as "combined modes" so that's what we shall call them.) If any of these
+# cipher modes are used then no MAC is calculated and stored
+# in the CipherText upon encryption. Likewise, if one of these
+# cipher modes is used with decryption, no attempt will be made
+# to validate the MAC contained in the CipherText object regardless
+# of whether it contains one or not. Since the expectation is that
+# these cipher modes support support message authenticity already,
+# injecting a MAC in the CipherText object would be at best redundant.
+#
+# Note that as of JDK 1.5, the SunJCE provider does not support *any*
+# of these cipher modes. Of these listed, only GCM and CCM are currently
+# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
+# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
+# padding modes.
+Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
+
+# Applies to ESAPI 2.0 and later only!
+# Additional cipher modes allowed for ESAPI 2.0 encryption. These
+# cipher modes are in _addition_ to those specified by the property
+# 'Encryptor.cipher_modes.combined_modes'.
+# Note: We will add support for streaming modes like CFB & OFB once
+# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
+# (probably in ESAPI 2.1).
+#
+#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
+#					here as this is an extremely weak mode. However, we *must*
+#					allow it here so we can test ECB mode. That is important
+#					since the logic is somewhat different (i.e., ECB mode does
+#					not use an IV).
+# DISCUSS: Better name?
+#	NOTE: ECB added only for testing purposes. Don't try this at home!
+Encryptor.cipher_modes.additional_allowed=CBC,ECB
+
+# 128-bit is almost always sufficient and appears to be more resistant to
+# related key attacks than is 256-bit AES. Use '_' to use default key size
+# for cipher algorithms (where it makes sense because the algorithm supports
+# a variable key size). Key length must agree to what's provided as the
+# cipher transformation, otherwise this will be ignored after logging a
+# warning.
+#
+# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
+Encryptor.EncryptionKeyLength=128
+
+# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
+# (All cipher modes except ECB require an IV.) There are two choices: we can either
+# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
+# the IV does not need to be hidden from adversaries, it is important that the
+# adversary not be allowed to choose it. Also, random IVs are generally much more
+# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
+# such as CFB and OFB use a different IV for each encryption with a given key so
+# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
+# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
+# uncomment the Encryptor.fixedIV.
+#
+# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
+Encryptor.ChooseIVMethod=random
+# If you choose to use a fixed IV, then you must place a fixed IV here that
+# is known to all others who are sharing your secret key. The format should
+# be a hex string that is the same length as the cipher block size for the
+# cipher algorithm that you are using. The following is an example for AES
+# from an AES test vector for AES-128/CBC as described in:
+# NIST Special Publication 800-38A (2001 Edition)
+# "Recommendation for Block Cipher Modes of Operation".
+# (Note that the block size for AES is 16 bytes == 128 bits.)
+#
+Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
+
+# Whether or not CipherText should use a message authentication code (MAC) with it.
+# This prevents an adversary from altering the IV as well as allowing a more
+# fool-proof way of determining the decryption failed because of an incorrect
+# key being supplied. This refers to the "separate" MAC calculated and stored
+# in CipherText, not part of any MAC that is calculated as a result of a
+# "combined mode" cipher mode.
+#
+# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
+# set this property to false.
+Encryptor.CipherText.useMAC=true
+
+# Whether or not the PlainText object may be overwritten and then marked
+# eligible for garbage collection. If not set, this is still treated as 'true'.
+Encryptor.PlainText.overwrite=true
+
+# Do not use DES except in a legacy situations. 56-bit is way too small key size.
+#Encryptor.EncryptionKeyLength=56
+#Encryptor.EncryptionAlgorithm=DES
+
+# TripleDES is considered strong enough for most purposes.
+#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
+#			requires downloading the special jurisdiction policy from Sun.
+#Encryptor.EncryptionKeyLength=168
+#Encryptor.EncryptionAlgorithm=DESede
+
+Encryptor.HashAlgorithm=SHA-512
+Encryptor.HashIterations=1024
+Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
+Encryptor.DigitalSignatureKeyLength=1024
+Encryptor.RandomAlgorithm=SHA1PRNG
+Encryptor.CharacterEncoding=UTF-8
+# Currently supported choices for JDK 1.5 and 1.6 are:
+#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
+#	HmacSHA512 (512 bits).
+# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
+# these JDKs support it.
+Encryptor.KDF.PRF=HmacSHA256
+
+#===========================================================================
+# ESAPI HttpUtilties
+#
+# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
+# protect against malicious data from attackers, such as unprintable characters, escaped characters,
+# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
+# headers, and CSRF tokens.
+#
+# Default file upload location (remember to escape backslashes with \\)
+HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
+# let this default to java.io.tmpdir for testing
+#HttpUtilities.UploadTempDir=C:\\temp
+# Force flags on cookies, if you use HttpUtilities to set cookies
+HttpUtilities.ForceHttpOnlySession=false
+HttpUtilities.ForceSecureSession=false
+HttpUtilities.ForceHttpOnlyCookies=true
+HttpUtilities.ForceSecureCookies=true
+# Maximum size of HTTP headers
+HttpUtilities.MaxHeaderSize=4096
+# File upload configuration
+HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
+HttpUtilities.MaxUploadFileBytes=500000000
+# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
+# container, and any other technologies you may be using. Failure to do this may expose you
+# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
+HttpUtilities.ResponseContentType=text/html; charset=UTF-8
+# This is the name of the cookie used to represent the HTTP session
+# Typically this will be the default "JSESSIONID" 
+HttpUtilities.HttpSessionIdName=JSESSIONID
+
+
+
+#===========================================================================
+# ESAPI Executor
+# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
+Executor.WorkingDirectory=C:\\Windows\\Temp
+Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
+
+
+#===========================================================================
+# ESAPI Logging
+# Set the application name if these logs are combined with other applications
+Logger.ApplicationName=ExampleApplication
+# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
+Logger.LogEncodingRequired=false
+# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
+Logger.LogApplicationName=true
+# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
+Logger.LogServerIP=true
+# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
+# want to place it in a specific directory.
+Logger.LogFileName=ESAPI_logging_file
+# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
+Logger.MaxLogFileSize=10000000
+
+
+#===========================================================================
+# ESAPI Intrusion Detection
+#
+# Each event has a base to which .count, .interval, and .action are added
+# The IntrusionException will fire if we receive "count" events within "interval" seconds
+# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
+#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
+#
+# Custom Events
+# Names must start with "event." as the base
+# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
+# You can also disable intrusion detection completely by changing
+# the following parameter to true
+#
+IntrusionDetector.Disable=false
+#
+IntrusionDetector.event.test.count=2
+IntrusionDetector.event.test.interval=10
+IntrusionDetector.event.test.actions=disable,log
+
+# Exception Events
+# All EnterpriseSecurityExceptions are registered automatically
+# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
+# Use the fully qualified classname of the exception as the base
+
+# any intrusion is an attack
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
+
+# for test purposes
+# CHECKME: Shouldn't there be something in the property name itself that designates
+#		   that these are for testing???
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
+
+# rapid validation errors indicate scans or attacks in progress
+# org.owasp.esapi.errors.ValidationException.count=10
+# org.owasp.esapi.errors.ValidationException.interval=10
+# org.owasp.esapi.errors.ValidationException.actions=log,logout
+
+# sessions jumping between hosts indicates session hijacking
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
+
+
+#===========================================================================
+# ESAPI Validation
+#
+# The ESAPI Validator works on regular expressions with defined names. You can define names
+# either here, or you may define application specific patterns in a separate file defined below.
+# This allows enterprises to specify both organizational standards as well as application specific
+# validation rules.
+#
+Validator.ConfigurationFile=validation.properties
+
+# Validators used by ESAPI
+Validator.AccountName=^[a-zA-Z0-9]{3,20}$
+Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
+Validator.RoleName=^[a-z]{1,20}$
+Validator.Redirect=^\\/test.*$
+
+# Global HTTP Validation Rules
+# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
+Validator.HTTPScheme=^(http|https)$
+Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
+Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
+Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
+Validator.HTTPURL=^.*$
+Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
+
+# Contributed by Fraenku at gmx.ch
+# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
+Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
+Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
+Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
+Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
+Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
+
+
+# Validation of file related input
+Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+
+# Validation of dates. Controls whether or not 'lenient' dates are accepted.
+# See DataFormat.setLenient(boolean flag) for further details.
+Validator.AcceptLenientDates=false
\ No newline at end of file
diff --git a/src/test/resources/esapi/.svn/text-base/antisamy-esapi.xml.svn-base b/src/test/resources/esapi/.svn/text-base/antisamy-esapi.xml.svn-base
new file mode 100644
index 0000000..500ab59
--- /dev/null
+++ b/src/test/resources/esapi/.svn/text-base/antisamy-esapi.xml.svn-base
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+	
+<!-- 
+W3C rules retrieved from:
+http://www.w3.org/TR/html401/struct/global.html
+-->
+	
+<!--
+Slashdot allowed tags taken from "Reply" page:
+<b> <i> <p> <br> <a> <ol> <ul> <li> <dl> <dt> <dd> <em> <strong> <tt> <blockquote> <div> <ecode> <quote>
+-->
+
+<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xsi:noNamespaceSchemaLocation="antisamy.xsd">
+	
+	<directives>
+		<directive name="omitXmlDeclaration" value="true"/>
+		<directive name="omitDoctypeDeclaration" value="true"/>
+		<directive name="maxInputSize" value="500000"/>
+		<directive name="embedStyleSheets" value="false"/>
+	</directives>
+	
+	
+	<common-regexps>
+		
+		<!-- 
+		From W3C:
+		This attribute assigns a class name or set of class names to an
+		element. Any number of elements may be assigned the same class
+		name or names. Multiple class names must be separated by white 
+		space characters.
+		-->
+		
+		<regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' -->
+		<regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/>
+		<regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/>
+	
+	</common-regexps>
+	
+	<!-- 
+	
+	Tag.name = a, b, div, body, etc.
+	Tag.action = filter: remove tags, but keep content, validate: keep content as long as it passes rules, remove: remove tag and contents
+	Attribute.name = id, class, href, align, width, etc.
+	Attribute.onInvalid = what to do when the attribute is invalid, e.g., remove the tag (removeTag), remove the attribute (removeAttribute), filter the tag (filterTag)
+	Attribute.description = What rules in English you want to tell the users they can have for this attribute. Include helpful things so they'll be able to tune their HTML
+	 
+	 -->
+
+	<!-- 
+	Some attributes are common to all (or most) HTML tags. There aren't many that qualify for this. You have to make sure there's no
+	collisions between any of these attribute names with attribute names of other tags that are for different purposes.
+	-->
+
+	<common-attributes>
+		
+
+		<attribute name="lang" description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
+		 	<regexp-list>
+		 		<regexp value="[a-zA-Z]{2,20}"/>
+		 	</regexp-list>
+		 </attribute>
+		 
+		 <attribute name="title" description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
+		 	<regexp-list>
+		 		<regexp name="htmlTitle"/>
+		 	</regexp-list>
+		 </attribute>
+
+		<attribute name="href" onInvalid="filterTag">
+			<regexp-list>
+				<regexp name="onsiteURL"/>
+				<regexp name="offsiteURL"/>
+			</regexp-list>
+		</attribute>
+	
+		<attribute name="align" description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
+			<literal-list>
+				<literal value="center"/>
+				<literal value="left"/>
+				<literal value="right"/>
+				<literal value="justify"/>
+				<literal value="char"/>
+			</literal-list>
+		</attribute>
+
+	</common-attributes>
+
+
+	<!--
+	This requires normal updates as browsers continue to diverge from the W3C and each other. As long as the browser wars continue
+	this is going to continue. I'm not sure war is the right word for what's going on. Doesn't somebody have to win a war after 
+	a while?
+	 -->
+	
+	<global-tag-attributes>
+		<attribute name="title"/>
+		<attribute name="lang"/>
+	</global-tag-attributes>
+
+
+	<tag-rules>
+
+		<!-- Tags related to JavaScript -->
+
+		<tag name="script" action="remove"/>
+		<tag name="noscript" action="remove"/>
+		
+		<!-- Frame & related tags -->
+		
+		<tag name="iframe" action="remove"/>
+		<tag name="frameset" action="remove"/>
+		<tag name="frame" action="remove"/>
+		<tag name="noframes" action="remove"/>
+		
+
+		<!-- All reasonable formatting tags -->
+		
+		<tag name="p" action="validate">
+			<attribute name="align"/>
+		</tag>
+
+		<tag name="div" action="validate"/>		
+		<tag name="i" action="validate"/>
+		<tag name="b" action="validate"/>
+		<tag name="em" action="validate"/>
+		<tag name="blockquote" action="validate"/>
+		<tag name="tt" action="validate"/>
+		
+		<tag name="br" action="truncate"/>
+
+		<!-- Custom Slashdot tags, though we're trimming the idea of having a possible mismatching end tag with the endtag="" attribute -->
+		
+		<tag name="quote" action="validate"/>
+		<tag name="ecode" action="validate"/> 
+		
+						
+		<!-- Anchor and anchor related tags -->
+		
+		<tag name="a" action="validate">
+
+			<attribute name="href" onInvalid="filterTag"/>
+			<attribute name="nohref">
+				<literal-list>
+					<literal value="nohref"/>
+					<literal value=""/>
+				</literal-list>
+			</attribute>
+			<attribute name="rel">
+				<literal-list>
+					<literal value="nofollow"/>
+				</literal-list>
+			</attribute>
+		</tag>
+
+		<!-- List tags -->
+
+		<tag name="ul" action="validate"/>
+		<tag name="ol" action="validate"/>
+		<tag name="li" action="validate"/>
+		
+	</tag-rules>
+
+
+
+	<!--  No CSS on Slashdot posts -->
+
+	<css-rules>
+	</css-rules>
+
+
+	<html-entities>
+		<entity name="amp" cdata="&"/>
+		<entity name="nbsp" cdata="&#160;"/>
+		
+		<entity name="iexcl" cdata="&#161;"/> <!--inverted exclamation mark, U+00A1 ISOnum -->
+		<entity name="cent" cdata="&#162;"/> <!--cent sign, U+00A2 ISOnum -->
+		<entity name="pound" cdata="&#163;"/> <!--pound sign, U+00A3 ISOnum -->
+		<entity name="curren" cdata="&#164;"/> <!--currency sign, U+00A4 ISOnum -->
+		<entity name="yen" cdata="&#165;"/> <!--yen sign = yuan sign, U+00A5 ISOnum -->
+		<entity name="brvbar" cdata="&#166;"/> <!--broken bar = broken vertical bar, U+00A6 ISOnum -->
+		<entity name="sect" cdata="&#167;"/> <!--section sign, U+00A7 ISOnum -->
+		<entity name="uml" cdata="&#168;"/> <!--diaeresis = spacing diaeresis, U+00A8 ISOdia -->
+		<entity name="copy" cdata="&#169;"/> <!--copyright sign, U+00A9 ISOnum -->
+		<entity name="ordf" cdata="&#170;"/> <!--feminine ordinal indicator, U+00AA ISOnum -->
+		<entity name="laquo" cdata="&#171;"/> <!--left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum -->
+		<entity name="not" cdata="&#172;"/> <!--not sign, U+00AC ISOnum -->
+		<entity name="shy" cdata="&#173;"/> <!--soft hyphen = discretionary hyphen,U+00AD ISOnum -->
+		<entity name="reg" cdata="&#174;"/> <!--registered sign = registered trade mark sign, U+00AE ISOnum -->
+		<entity name="macr" cdata="&#175;"/> <!--macron = spacing macron = overline = APL overbar, U+00AF ISOdia -->
+		<entity name="deg" cdata="&#176;"/> <!--degree sign, U+00B0 ISOnum -->
+		<entity name="plusmn" cdata="&#177;"/> <!--plus-minus sign = plus-or-minus sign, U+00B1 ISOnum -->
+		<entity name="sup2" cdata="&#178;"/> <!--superscript two = superscript digit two = squared, U+00B2 ISOnum -->
+		<entity name="sup3" cdata="&#179;"/> <!--superscript three = superscript digit three= cubed, U+00B3 ISOnum -->
+		<entity name="acute" cdata="&#180;"/> <!--acute accent = spacing acute, U+00B4 ISOdia -->
+		<entity name="micro" cdata="&#181;"/> <!--micro sign, U+00B5 ISOnum -->
+		<entity name="para" cdata="&#182;"/> <!--pilcrow sign = paragraph sign, U+00B6 ISOnum -->
+		<entity name="middot" cdata="&#183;"/> <!--middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum -->
+		<entity name="cedil" cdata="&#184;"/> <!--cedilla = spacing cedilla, U+00B8 ISOdia -->
+		<entity name="sup1" cdata="&#185;"/> <!--superscript one = superscript digit one,U+00B9 ISOnum -->
+		<entity name="ordm" cdata="&#186;"/> <!--masculine ordinal indicator, U+00BA ISOnum -->
+		<entity name="raquo" cdata="&#187;"/> <!--right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum -->
+		<entity name="frac14" cdata="&#188;"/> <!--vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum -->
+		<entity name="frac12" cdata="&#189;"/> <!--vulgar fraction one half = fraction one half, U+00BD ISOnum -->
+		<entity name="frac34" cdata="&#190;"/> <!--vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum -->
+		<entity name="iquest" cdata="&#191;"/> <!--inverted question mark = turned question mark, U+00BF ISOnum -->
+		<entity name="Agrave" cdata="&#192;"/> <!--latin capital letter A with grave = latin capital letter A grave,U+00C0 ISOlat1 -->
+		<entity name="Aacute" cdata="&#193;"/> <!--latin capital letter A with acute,U+00C1 ISOlat1 -->
+		<entity name="Acirc" cdata="&#194;"/> <!--latin capital letter A with circumflex,U+00C2 ISOlat1 -->
+		<entity name="Atilde" cdata="&#195;"/> <!--latin capital letter A with tilde,U+00C3 ISOlat1 -->
+		<entity name="Auml" cdata="&#196;"/> <!--latin capital letter A with diaeresis,U+00C4 ISOlat1 -->
+		<entity name="Aring" cdata="&#197;"/> <!--latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 -->
+		<entity name="AElig" cdata="&#198;"/> <!--latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 -->
+		<entity name="Ccedil" cdata="&#199;"/> <!--latin capital letter C with cedilla, U+00C7 ISOlat1 -->
+		<entity name="Egrave" cdata="&#200;"/> <!--latin capital letter E with grave, U+00C8 ISOlat1 -->
+		<entity name="Eacute" cdata="&#201;"/> <!--latin capital letter E with acute,U+00C9 ISOlat1 -->
+		<entity name="Ecirc" cdata="&#202;"/> <!--latin capital letter E with circumflex,U+00CA ISOlat1 -->
+		<entity name="Euml" cdata="&#203;"/> <!--latin capital letter E with diaeresis, U+00CB ISOlat1 -->
+		<entity name="Igrave" cdata="&#204;"/> <!--latin capital letter I with grave, U+00CC ISOlat1 -->
+		<entity name="Iacute" cdata="&#205;"/> <!--latin capital letter I with acute, U+00CD ISOlat1 -->
+		<entity name="Icirc" cdata="&#206;"/> <!--latin capital letter I with circumflex, U+00CE ISOlat1 -->
+		<entity name="Iuml" cdata="&#207;"/> <!--latin capital letter I with diaeresis, U+00CF ISOlat1 -->
+		<entity name="ETH" cdata="&#208;"/> <!--latin capital letter ETH, U+00D0 ISOlat1 -->
+		<entity name="Ntilde" cdata="&#209;"/> <!--latin capital letter N with tilde, U+00D1 ISOlat1 -->
+		<entity name="Ograve" cdata="&#210;"/> <!--latin capital letter O with grave, U+00D2 ISOlat1 -->
+		<entity name="Oacute" cdata="&#211;"/> <!--latin capital letter O with acute, U+00D3 ISOlat1 -->
+		<entity name="Ocirc" cdata="&#212;"/> <!--latin capital letter O with circumflex, U+00D4 ISOlat1 -->
+		<entity name="Otilde" cdata="&#213;"/> <!--latin capital letter O with tilde, U+00D5 ISOlat1 -->
+		<entity name="Ouml" cdata="&#214;"/> <!--latin capital letter O with diaeresis, U+00D6 ISOlat1 -->
+		<entity name="times" cdata="&#215;"/> <!--multiplication sign, U+00D7 ISOnum -->
+		<entity name="Oslash" cdata="&#216;"/> <!--latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 -->
+		<entity name="Ugrave" cdata="&#217;"/> <!--latin capital letter U with grave, U+00D9 ISOlat1 -->
+		<entity name="Uacute" cdata="&#218;"/> <!--latin capital letter U with acute, U+00DA ISOlat1 -->
+		<entity name="Ucirc" cdata="&#219;"/> <!--latin capital letter U with circumflex, U+00DB ISOlat1 -->
+		<entity name="Uuml" cdata="&#220;"/> <!--latin capital letter U with diaeresis, U+00DC ISOlat1 -->
+		<entity name="Yacute" cdata="&#221;"/> <!--latin capital letter Y with acute, U+00DD ISOlat1 -->
+		<entity name="THORN" cdata="&#222;"/> <!--latin capital letter THORN, U+00DE ISOlat1 -->
+		<entity name="szlig" cdata="&#223;"/> <!--latin small letter sharp s = ess-zed, U+00DF ISOlat1 -->
+		<entity name="agrave" cdata="&#224;"/> <!--latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 -->
+		<entity name="aacute" cdata="&#225;"/> <!--latin small letter a with acute, U+00E1 ISOlat1 -->
+		<entity name="acirc" cdata="&#226;"/> <!--latin small letter a with circumflex, U+00E2 ISOlat1 -->
+		<entity name="atilde" cdata="&#227;"/> <!--latin small letter a with tilde, U+00E3 ISOlat1 -->
+		<entity name="auml" cdata="&#228;"/> <!--latin small letter a with diaeresis, U+00E4 ISOlat1 -->
+		<entity name="aring" cdata="&#229;"/> <!--latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 -->
+		<entity name="aelig" cdata="&#230;"/> <!--latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 -->
+		<entity name="ccedil" cdata="&#231;"/> <!--latin small letter c with cedilla, U+00E7 ISOlat1 -->
+		<entity name="egrave" cdata="&#232;"/> <!--latin small letter e with grave, U+00E8 ISOlat1 -->
+		<entity name="eacute" cdata="&#233;"/> <!--latin small letter e with acute, U+00E9 ISOlat1 -->
+		<entity name="ecirc" cdata="&#234;"/> <!--latin small letter e with circumflex, U+00EA ISOlat1 -->
+		<entity name="euml" cdata="&#235;"/> <!--latin small letter e with diaeresis, U+00EB ISOlat1 -->
+		<entity name="igrave" cdata="&#236;"/> <!--latin small letter i with grave, U+00EC ISOlat1 -->
+		<entity name="iacute" cdata="&#237;"/> <!--latin small letter i with acute, U+00ED ISOlat1 -->
+		<entity name="icirc" cdata="&#238;"/> <!--latin small letter i with circumflex, U+00EE ISOlat1 -->
+		<entity name="iuml" cdata="&#239;"/> <!--latin small letter i with diaeresis, U+00EF ISOlat1 -->
+		<entity name="eth" cdata="&#240;"/> <!--latin small letter eth, U+00F0 ISOlat1 -->
+		<entity name="ntilde" cdata="&#241;"/> <!--latin small letter n with tilde, U+00F1 ISOlat1 -->
+		<entity name="ograve" cdata="&#242;"/> <!--latin small letter o with grave, U+00F2 ISOlat1 -->
+		<entity name="oacute" cdata="&#243;"/> <!--latin small letter o with acute, U+00F3 ISOlat1 -->
+		<entity name="ocirc " cdata="&#244;"/> <!--latin small letter o with circumflex, U+00F4 ISOlat1 -->
+		<entity name="otilde" cdata="&#245;"/> <!--latin small letter o with tilde, U+00F5 ISOlat1 -->
+		<entity name="ouml" cdata="&#246;"/> <!--latin small letter o with diaeresis, U+00F6 ISOlat1 -->
+		<entity name="divide" cdata="&#247;"/> <!--division sign, U+00F7 ISOnum -->
+		<entity name="oslash" cdata="&#248;"/> <!--latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 -->
+		<entity name="ugrave" cdata="&#249;"/> <!--latin small letter u with grave, U+00F9 ISOlat1 -->
+		<entity name="uacute" cdata="&#250;"/> <!--latin small letter u with acute, U+00FA ISOlat1 -->
+		<entity name="ucirc" cdata="&#251;"/> <!--latin small letter u with circumflex, U+00FB ISOlat1 -->
+		<entity name="uuml" cdata="&#252;"/> <!--latin small letter u with diaeresis, U+00FC ISOlat1 -->
+		<entity name="yacute" cdata="&#253;"/> <!--latin small letter y with acute, U+00FD ISOlat1 -->
+		<entity name="thorn" cdata="&#254;"/> <!--latin small letter thorn, U+00FE ISOlat1 -->
+		<entity name="yuml" cdata="&#255;"/> <!--latin small letter y with diaeresis, U+00FF ISOlat1 -->
+		                                  
+		<entity name="fnof" cdata="&#402;"/> <!--latin small f with hook = function = florin, U+0192 ISOtech -->
+		
+		<!-- Greek -->
+		<entity name="Alpha" cdata="&#913;"/> <!--greek capital letter alpha, U+0391 -->
+		<entity name="Beta" cdata="&#914;"/> <!--greek capital letter beta, U+0392 -->
+		<entity name="Gamma" cdata="&#915;"/> <!--greek capital letter gamma, U+0393 ISOgrk3 -->
+		<entity name="Delta" cdata="&#916;"/> <!--greek capital letter delta, U+0394 ISOgrk3 -->
+		<entity name="Epsilon" cdata="&#917;"/> <!--greek capital letter epsilon, U+0395 -->
+		<entity name="Zeta" cdata="&#918;"/> <!--greek capital letter zeta, U+0396 -->
+		<entity name="Eta" cdata="&#919;"/> <!--greek capital letter eta, U+0397 -->
+		<entity name="Theta" cdata="&#920;"/> <!--greek capital letter theta, U+0398 ISOgrk3 -->
+		<entity name="Iota" cdata="&#921;"/> <!--greek capital letter iota, U+0399 -->
+		<entity name="Kappa" cdata="&#922;"/> <!--greek capital letter kappa, U+039A -->
+		<entity name="Lambda" cdata="&#923;"/> <!--greek capital letter lambda, U+039B ISOgrk3 -->
+		<entity name="Mu" cdata="&#924;"/> <!--greek capital letter mu, U+039C -->
+		<entity name="Nu" cdata="&#925;"/> <!--greek capital letter nu, U+039D -->
+		<entity name="Xi" cdata="&#926;"/> <!--greek capital letter xi, U+039E ISOgrk3 -->
+		<entity name="Omicron" cdata="&#927;"/> <!--greek capital letter omicron, U+039F -->
+		<entity name="Pi" cdata="&#928;"/> <!--greek capital letter pi, U+03A0 ISOgrk3 -->
+		<entity name="Rho" cdata="&#929;"/> <!--greek capital letter rho, U+03A1 -->
+		<!-- there is no Sigmaf, and no U+03A2 character either -->
+		<entity name="Sigma" cdata="&#931;"/> <!--greek capital letter sigma, U+03A3 ISOgrk3 -->
+		<entity name="Tau" cdata="&#932;"/> <!--greek capital letter tau, U+03A4 -->
+		<entity name="Upsilon" cdata="&#933;"/> <!--greek capital letter upsilon,U+03A5 ISOgrk3 -->
+		<entity name="Phi" cdata="&#934;"/> <!--greek capital letter phi,U+03A6 ISOgrk3 -->
+		<entity name="Chi" cdata="&#935;"/> <!--greek capital letter chi, U+03A7 -->
+		<entity name="Psi" cdata="&#936;"/> <!--greek capital letter psi,U+03A8 ISOgrk3 -->
+		<entity name="Omega" cdata="&#937;"/> <!--greek capital letter omega,U+03A9 ISOgrk3 -->
+		
+		<entity name="alpha" cdata="&#945;"/> <!--greek small letter alpha,U+03B1 ISOgrk3 -->
+		<entity name="beta" cdata="&#946;"/> <!--greek small letter beta, U+03B2 ISOgrk3 -->
+		<entity name="gamma" cdata="&#947;"/> <!--greek small letter gamma,U+03B3 ISOgrk3 -->
+		<entity name="delta" cdata="&#948;"/> <!--greek small letter delta,U+03B4 ISOgrk3 -->
+		<entity name="epsilon" cdata="&#949;"/> <!--greek small letter epsilon,U+03B5 ISOgrk3 -->
+		<entity name="zeta" cdata="&#950;"/> <!--greek small letter zeta, U+03B6 ISOgrk3 -->
+		<entity name="eta" cdata="&#951;"/> <!--greek small letter eta, U+03B7 ISOgrk3 -->
+		<entity name="theta" cdata="&#952;"/> <!--greek small letter theta, U+03B8 ISOgrk3 -->
+		<entity name="iota" cdata="&#953;"/> <!--greek small letter iota, U+03B9 ISOgrk3 -->
+		<entity name="kappa" cdata="&#954;"/> <!--greek small letter kappa,U+03BA ISOgrk3 -->
+		<entity name="lambda" cdata="&#955;"/> <!--greek small letter lambda, U+03BB ISOgrk3 -->
+		<entity name="mu" cdata="&#956;"/> <!--greek small letter mu, U+03BC ISOgrk3 -->
+		<entity name="nu" cdata="&#957;"/> <!--greek small letter nu, U+03BD ISOgrk3 -->
+		<entity name="xi" cdata="&#958;"/> <!--greek small letter xi, U+03BE ISOgrk3 -->
+		<entity name="omicron" cdata="&#959;"/> <!--greek small letter omicron, U+03BF NEW -->
+		<entity name="pi" cdata="&#960;"/> <!--greek small letter pi, U+03C0 ISOgrk3 -->
+		<entity name="rho" cdata="&#961;"/> <!--greek small letter rho, U+03C1 ISOgrk3 -->
+		<entity name="sigmaf" cdata="&#962;"/> <!--greek small letter final sigma, U+03C2 ISOgrk3 -->
+		<entity name="sigma" cdata="&#963;"/> <!--greek small letter sigma, U+03C3 ISOgrk3 -->
+		<entity name="tau" cdata="&#964;"/> <!--greek small letter tau, U+03C4 ISOgrk3 -->
+		<entity name="upsilon" cdata="&#965;"/> <!--greek small letter upsilon, U+03C5 ISOgrk3 -->
+		<entity name="phi" cdata="&#966;"/> <!--greek small letter phi, U+03C6 ISOgrk3 -->
+		<entity name="chi" cdata="&#967;"/> <!--greek small letter chi, U+03C7 ISOgrk3 -->
+		<entity name="psi" cdata="&#968;"/> <!--greek small letter psi, U+03C8 ISOgrk3 -->
+		<entity name="omega" cdata="&#969;"/> <!--greek small letter omega, U+03C9 ISOgrk3 -->
+		<entity name="thetasym" cdata="&#977;"/> <!--greek small letter theta symbol, U+03D1 NEW -->
+		<entity name="upsih" cdata="&#978;"/> <!--greek upsilon with hook symbol, U+03D2 NEW -->
+		<entity name="piv" cdata="&#982;"/> <!--greek pi symbol, U+03D6 ISOgrk3 -->
+		
+		<!-- General Punctuation -->
+		<entity name="bull" cdata="&#8226;"/> <!--bullet = black small circle, U+2022 ISOpub  -->
+		<!-- bullet is NOT the same as bullet operator, U+2219 -->
+		<entity name="hellip" cdata="&#8230;"/> <!--horizontal ellipsis = three dot leader, U+2026 ISOpub  -->
+		<entity name="prime" cdata="&#8242;"/> <!--prime = minutes = feet, U+2032 ISOtech -->
+		<entity name="Prime" cdata="&#8243;"/> <!--double prime = seconds = inches, U+2033 ISOtech -->
+		<entity name="oline" cdata="&#8254;"/> <!--overline = spacing overscore, U+203E NEW -->
+		<entity name="frasl" cdata="&#8260;"/> <!--fraction slash, U+2044 NEW -->
+		
+		<!-- Letterlike Symbols -->
+		<entity name="weierp" cdata="&#8472;"/> <!--script capital P = power set = Weierstrass p, U+2118 ISOamso -->
+		<entity name="image" cdata="&#8465;"/> <!--blackletter capital I = imaginary part, U+2111 ISOamso -->
+		<entity name="real" cdata="&#8476;"/> <!--blackletter capital R = real part symbol, U+211C ISOamso -->
+		<entity name="trade" cdata="&#8482;"/> <!--trade mark sign, U+2122 ISOnum -->
+		<entity name="alefsym" cdata="&#8501;"/> <!--alef symbol = first transfinite cardinal, U+2135 NEW -->
+		<!-- alef symbol is NOT the same as hebrew letter alef,
+		     U+05D0 although the same glyph could be used to depict both characters -->
+		
+		<!-- Arrows -->
+		<entity name="larr" cdata="&#8592;"/> <!--leftwards arrow, U+2190 ISOnum -->
+		<entity name="uarr" cdata="&#8593;"/> <!--upwards arrow, U+2191 ISOnum-->
+		<entity name="rarr" cdata="&#8594;"/> <!--rightwards arrow, U+2192 ISOnum -->
+		<entity name="darr" cdata="&#8595;"/> <!--downwards arrow, U+2193 ISOnum -->
+		<entity name="harr" cdata="&#8596;"/> <!--left right arrow, U+2194 ISOamsa -->
+		<entity name="crarr" cdata="&#8629;"/> <!--downwards arrow with corner leftwards
+		                                     = carriage return, U+21B5 NEW -->
+		<entity name="lArr" cdata="&#8656;"/> <!--leftwards double arrow, U+21D0 ISOtech -->
+		
+		<!-- ISO 10646 does not say that lArr is the same as the 'is implied by' arrow
+		    but also does not have any other character for that function. So ? lArr can
+		    be used for 'is implied by' as ISOtech suggests -->
+		    
+		<entity name="uArr" cdata="&#8657;"/> <!--upwards double arrow, U+21D1 ISOamsa -->
+		<entity name="rArr" cdata="&#8658;"/> <!--rightwards double arrow, U+21D2 ISOtech -->
+		
+		<!-- ISO 10646 does not say this is the 'implies' character but does not have 
+		     another character with this function so ?
+		     rArr can be used for 'implies' as ISOtech suggests -->
+		     
+		<entity name="dArr" cdata="&#8659;"/> <!--downwards double arrow, U+21D3 ISOamsa -->
+		<entity name="hArr" cdata="&#8660;"/> <!--left right double arrow, U+21D4 ISOamsa -->
+		
+		<!-- Mathematical Operators -->
+		<entity name="forall" cdata="&#8704;"/> <!--for all, U+2200 ISOtech -->
+		<entity name="part" cdata="&#8706;"/> <!--partial differential, U+2202 ISOtech  -->
+		<entity name="exist" cdata="&#8707;"/> <!--there exists, U+2203 ISOtech -->
+		<entity name="empty" cdata="&#8709;"/> <!--empty set = null set = diameter,U+2205 ISOamso -->
+		<entity name="nabla" cdata="&#8711;"/> <!--nabla = backward difference, U+2207 ISOtech -->
+		<entity name="isin" cdata="&#8712;"/> <!--element of, U+2208 ISOtech -->
+		<entity name="notin" cdata="&#8713;"/> <!--not an element of, U+2209 ISOtech -->
+		<entity name="ni" cdata="&#8715;"/> <!--contains as member, U+220B ISOtech -->
+	
+		<!-- should there be a more memorable name than 'ni'? -->
+		<entity name="prod" cdata="&#8719;"/> <!--n-ary product = product sign, U+220F ISOamsb -->
+		
+		<!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though
+		     the same glyph might be used for both -->
+		     
+		<entity name="sum" cdata="&#8721;"/> <!--n-ary sumation, U+2211 ISOamsb -->
+		
+		<!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+		     though the same glyph might be used for both -->
+		
+		<entity name="minus" cdata="&#8722;"/> <!--minus sign, U+2212 ISOtech -->
+		<entity name="lowast" cdata="&#8727;"/> <!--asterisk operator, U+2217 ISOtech -->
+		<entity name="radic" cdata="&#8730;"/> <!--square root = radical sign, U+221A ISOtech -->
+		<entity name="prop" cdata="&#8733;"/> <!--proportional to, U+221D ISOtech -->
+		<entity name="infin" cdata="&#8734;"/> <!--infinity, U+221E ISOtech -->
+		<entity name="ang" cdata="&#8736;"/> <!--angle, U+2220 ISOamso -->
+		<entity name="and" cdata="&#8743;"/> <!--logical and = wedge, U+2227 ISOtech -->
+		<entity name="or" cdata="&#8744;"/> <!--logical or = vee, U+2228 ISOtech -->
+		<entity name="cap" cdata="&#8745;"/> <!--intersection = cap, U+2229 ISOtech -->
+		<entity name="cup" cdata="&#8746;"/> <!--union = cup, U+222A ISOtech -->
+		<entity name="int" cdata="&#8747;"/> <!--integral, U+222B ISOtech -->
+		<entity name="there4" cdata="&#8756;"/> <!--therefore, U+2234 ISOtech -->
+		<entity name="sim" cdata="&#8764;"/> <!--tilde operator = varies with = similar to, U+223C ISOtech -->
+		
+		<!-- tilde operator is NOT the same character as the tilde, U+007E,
+		     although the same glyph might be used to represent both  -->
+		     
+		<entity name="cong" cdata="&#8773;"/> <!--approximately equal to, U+2245 ISOtech -->
+		<entity name="asymp" cdata="&#8776;"/> <!--almost equal to = asymptotic to, U+2248 ISOamsr -->
+		<entity name="ne" cdata="&#8800;"/> <!--not equal to, U+2260 ISOtech -->
+		<entity name="equiv" cdata="&#8801;"/> <!--identical to, U+2261 ISOtech -->
+		<entity name="le" cdata="&#8804;"/> <!--less-than or equal to, U+2264 ISOtech -->
+		<entity name="ge" cdata="&#8805;"/> <!--greater-than or equal to, U+2265 ISOtech -->
+		<entity name="sub" cdata="&#8834;"/> <!--subset of, U+2282 ISOtech -->
+		<entity name="sup" cdata="&#8835;"/> <!--superset of, U+2283 ISOtech -->
+		
+		<!-- note that nsup, 'not a superset of, U+2283' is not covered by the Symbol 
+		     font encoding and is not included. Should it be, for symmetry?
+		     It is in ISOamsn  --> 
+		
+		<entity name="nsub" cdata="&#8836;"/> <!--not a subset of, U+2284 ISOamsn -->
+		<entity name="sube" cdata="&#8838;"/> <!--subset of or equal to, U+2286 ISOtech -->
+		<entity name="supe" cdata="&#8839;"/> <!--superset of or equal to, U+2287 ISOtech -->
+		<entity name="oplus" cdata="&#8853;"/> <!--circled plus = direct sum, U+2295 ISOamsb -->
+		<entity name="otimes" cdata="&#8855;"/> <!--circled times = vector product, U+2297 ISOamsb -->
+		<entity name="perp" cdata="&#8869;"/> <!--up tack = orthogonal to = perpendicular, U+22A5 ISOtech -->
+		<entity name="sdot" cdata="&#8901;"/> <!--dot operator, U+22C5 ISOamsb -->
+		<!-- dot operator is NOT the same character as U+00B7 middle dot -->
+		
+		<!-- Miscellaneous Technical -->
+		<entity name="lceil" cdata="&#8968;"/> <!--left ceiling = apl upstile, U+2308 ISOamsc  -->
+		<entity name="rceil" cdata="&#8969;"/> <!--right ceiling, U+2309 ISOamsc  -->
+		<entity name="lfloor" cdata="&#8970;"/> <!--left floor = apl downstile, U+230A ISOamsc  -->
+		<entity name="rfloor" cdata="&#8971;"/> <!--right floor, U+230B ISOamsc  -->
+		<entity name="lang" cdata="&#9001;"/> <!--left-pointing angle bracket = bra, U+2329 ISOtech -->
+		<!-- lang is NOT the same character as U+003C 'less than' 
+		     or U+2039 'single left-pointing angle quotation mark' -->
+		<entity name="rang" cdata="&#9002;"/> <!--right-pointing angle bracket = ket, U+232A ISOtech -->
+		<!-- rang is NOT the same character as U+003E 'greater than' or U+203A 'single right-pointing angle quotation mark' -->
+		
+		<!-- Geometric Shapes -->
+		<entity name="loz" cdata="&#9674;"/> <!--lozenge, U+25CA ISOpub -->
+		
+		<!-- Miscellaneous Symbols -->
+		<entity name="spades" cdata="&#9824;"/> <!--black spade suit, U+2660 ISOpub -->
+		<!-- black here seems to mean filled as opposed to hollow -->
+		<entity name="clubs" cdata="&#9827;"/> <!--black club suit = shamrock, U+2663 ISOpub -->
+		<entity name="hearts" cdata="&#9829;"/> <!--black heart suit = valentine, U+2665 ISOpub -->
+		<entity name="diams" cdata="&#9830;"/> <!--black diamond suit, U+2666 ISOpub -->
+		
+		<entity name="quot" cdata="&#34;"  /> <!--quotation mark = APL quote, U+0022 ISOnum -->
+		<!-- Latin Extended-A -->
+		<entity name="OElig" cdata="&#338;" /> <!--latin capital ligature OE, U+0152 ISOlat2 -->
+		<entity name="oelig" cdata="&#339;" /> <!--latin small ligature oe, U+0153 ISOlat2 -->
+		<!-- ligature is a misnomer, this is a separate character in some languages -->
+		<entity name="Scaron" cdata="&#352;" /> <!--latin capital letter S with caron, U+0160 ISOlat2 -->
+		<entity name="scaron" cdata="&#353;" /> <!--latin small letter s with caron, U+0161 ISOlat2 -->
+		<entity name="Yuml" cdata="&#376;" /> <!--latin capital letter Y with diaeresis, U+0178 ISOlat2 -->
+		
+		<!-- Spacing Modifier Letters -->
+		<entity name="circ" cdata="&#710;" /> <!--modifier letter circumflex accent, U+02C6 ISOpub -->
+		<entity name="tilde" cdata="&#732;" /> <!--small tilde, U+02DC ISOdia -->
+		
+		<!-- General Punctuation -->
+		<entity name="ensp" cdata="&#8194;"/> <!--en space, U+2002 ISOpub -->
+		<entity name="emsp" cdata="&#8195;"/> <!--em space, U+2003 ISOpub -->
+		<entity name="thinsp" cdata="&#8201;"/> <!--thin space, U+2009 ISOpub -->
+		<entity name="zwnj" cdata="&#8204;"/> <!--zero width non-joiner, U+200C NEW RFC 2070 -->
+		<entity name="zwj" cdata="&#8205;"/> <!--zero width joiner, U+200D NEW RFC 2070 -->
+		<entity name="lrm" cdata="&#8206;"/> <!--left-to-right mark, U+200E NEW RFC 2070 -->
+		<entity name="rlm" cdata="&#8207;"/> <!--right-to-left mark, U+200F NEW RFC 2070 -->
+		<entity name="ndash" cdata="&#8211;"/> <!--en dash, U+2013 ISOpub -->
+		<entity name="mdash" cdata="&#8212;"/> <!--em dash, U+2014 ISOpub -->
+		<entity name="lsquo" cdata="&#8216;"/> <!--left single quotation mark, U+2018 ISOnum -->
+		<entity name="rsquo" cdata="&#8217;"/> <!--right single quotation mark, U+2019 ISOnum -->
+		<entity name="sbquo" cdata="&#8218;"/> <!--single low-9 quotation mark, U+201A NEW -->
+		<entity name="ldquo" cdata="&#8220;"/> <!--left double quotation mark, U+201C ISOnum -->
+		<entity name="rdquo" cdata="&#8221;"/> <!--right double quotation mark, U+201D ISOnum -->
+		<entity name="bdquo" cdata="&#8222;"/> <!--double low-9 quotation mark, U+201E NEW -->
+		<entity name="dagger" cdata="&#8224;"/> <!--dagger, U+2020 ISOpub -->
+		<entity name="Dagger" cdata="&#8225;"/> <!--double dagger, U+2021 ISOpub -->
+		<entity name="permil" cdata="&#8240;"/> <!--per mille sign, U+2030 ISOtech -->
+		<entity name="lsaquo" cdata="&#8249;"/> <!--single left-pointing angle quotation mark, U+2039 ISO proposed -->
+		<!-- lsaquo is proposed but not yet ISO standardized -->
+		<entity name="rsaquo" cdata="&#8250;"/> <!--single right-pointing angle quotation mark, U+203A ISO proposed -->
+		<!-- rsaquo is proposed but not yet ISO standardized -->
+		<entity name="euro" cdata="&#8364;" /> <!--euro sign, U+20AC NEW -->
+	</html-entities>
+
+</anti-samy-rules>
diff --git a/src/test/resources/esapi/.svn/text-base/users.txt.svn-base b/src/test/resources/esapi/.svn/text-base/users.txt.svn-base
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/esapi/.svn/text-base/validation.properties.svn-base b/src/test/resources/esapi/.svn/text-base/validation.properties.svn-base
new file mode 100644
index 0000000..18e037f
--- /dev/null
+++ b/src/test/resources/esapi/.svn/text-base/validation.properties.svn-base
@@ -0,0 +1,29 @@
+# The ESAPI validator does many security checks on input, such as canonicalization
+# and whitelist validation. Note that all of these validation rules are applied *after*
+# canonicalization. Double-encoded characters (even with different encodings involved,
+# are never allowed.
+#
+# To use:
+#
+# First set up a pattern below. You can choose any name you want, prefixed by the word
+# "Validation." For example:
+#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+# 
+# Then you can validate in your code against the pattern like this:
+#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
+# Where maxLength and allowNull are set for you needs, respectively.
+#
+# But note, when you use boolean variants of validation functions, you lose critical 
+# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
+# and use the returned user input which is in canonical form. Consider the following:
+#  
+# try {
+#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
+#
+Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
+Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
+Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
+Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
+Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
+
diff --git a/src/test/resources/esapi/.svn/text-base/waf-policy.xml.svn-base b/src/test/resources/esapi/.svn/text-base/waf-policy.xml.svn-base
new file mode 100644
index 0000000..05d75b2
--- /dev/null
+++ b/src/test/resources/esapi/.svn/text-base/waf-policy.xml.svn-base
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+		This file defines a set of "web application firewall" rules that
+		defend a web application against certain types of attacks. These rules
+		are loaded and enforced by a filter that sits in front of a web
+		application and has access to both request and response on both the
+		way in and the way out.
+	-->
+
+<policy>
+
+
+	<!--
+		Setup some simple aliases to use elsewhere in the WAF policy. Alias
+		types are: string (default), regex. String is a literal string, regex
+		is a pattern.
+	-->
+	<aliases>
+		<alias name="INPUT_VALIDATION_ERROR">/security/input.jsp</alias>
+		<alias name="ADMIN_PATH" type="regex">^/admin/.*</alias>
+	</aliases>
+
+
+
+	<!--
+		Set the overall WAF mode of operation. The mode can be either "block"
+		or "log". "block" mode will send all errors to the web page defined in
+		the error-handling configuration. "log" mode will not change HTTP
+		requests at all, but will simply log errors.
+	-->
+	<settings>
+		<mode>redirect</mode>
+		<session-cookie-name>JSESSIONID</session-cookie-name>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+
+
+	<!--
+		Set authentication rules by path.
+	-->
+	<authentication-rules path="/.*"  key="ESAPIUserSessionKey" >
+		<path-exception>/</path-exception>
+		<path-exception>/index.html</path-exception>
+		<path-exception>/login.jsp</path-exception>
+		<path-exception>/index.jsp</path-exception>
+		<path-exception type="regex">/images/.*</path-exception>
+		<path-exception type="regex">/css/.*</path-exception>
+		<path-exception type="regex">/help/.*</path-exception>
+	</authentication-rules>
+
+
+	<bean-shell-rules>	
+		<!--  	<bean-shell-script 
+			id="example1" 
+			file="waf-policies/bean-shell-rule.bsh" 
+			stage="before-request-body"/>
+		-->
+	</bean-shell-rules>
+	
+	<!--
+		Set authorization rules by path. Types are: regex Operators for
+		must-match are: contains,equals,inList,exists
+	-->
+	<authorization-rules>
+		<restrict-source-ip type="regex"
+			ip-regex="(192\.168\.1\..*|127.0.0.1)">/admin/.*</restrict-source-ip>
+		<must-match path="^/admin/.*" variable="request.headers.x-roles"
+			operator="contains" value="admin" />
+	</authorization-rules>
+
+
+
+	<!--
+		Set rules for incoming URLs.
+	-->
+	<url-rules>
+		<restrict-extension deny=".jpg" />
+
+		<restrict-method deny="GET" path=".*\.do$" />
+		<restrict-method allow="^(GET|POST|TRACE)$" />
+
+		<enforce-https path="/.*">
+			<path-exception>/index.html</path-exception>
+			<path-exception>/index.jsp</path-exception>
+			<path-exception type="regex">/images/.*</path-exception>
+			<path-exception type="regex">/css/.*</path-exception>
+			<path-exception type="regex">/help/.*</path-exception>
+		</enforce-https>
+	</url-rules>
+
+
+
+	<!--
+		Set rules for incoming headers and parameters.
+	-->
+	<header-rules>
+		<restrict-content-type deny=".*multipart.*" />
+		<restrict-content-type allow="text/plain" />
+		<restrict-content-type allow="x-www-form-urlencoded" />
+		<restrict-user-agent deny=".*GoogleBot.*" />
+		<restrict-user-agent allow=".*" />
+	</header-rules>
+
+
+	<!--
+		Set virtual patches to match specific vulnerability patterns.
+	-->
+	<virtual-patches>
+		<virtual-patch id="1234" path="/foo.jsp" variable="request.parameters.bar"
+			pattern="[0-9a-zA-Z]" message="zomg attax" />
+	</virtual-patches>
+
+
+
+	<!-- Set rules for outbound headers and data -->
+
+	<outbound-rules>
+
+		<add-header name="FOO" value="BAR" path="/.*">
+			<path-exception type="regex">/foobar/.*</path-exception>
+		</add-header>
+
+		<add-http-only-flag>
+			<cookie name=".*" />
+		</add-http-only-flag>
+
+		<add-secure-flag>
+			<cookie name=".*" />
+		</add-secure-flag>
+
+ 		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+
+		<dynamic-insertion
+			pattern="(<input.*)type\s+=\s+"+hidden"+(.*/>)">
+			<replacement>\1\2</replacement>
+		</dynamic-insertion>
+
+		<detect-content content-type=".*text/.*" pattern=".*2008.*" />
+
+	</outbound-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml b/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml
new file mode 100644
index 0000000..2ed0732
--- /dev/null
+++ b/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<AccessControlPolicy>
+	<AccessControlRules>
+		<AccessControlRule
+			name="AlwaysTrue"
+			description="Access is always granted"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysTrueACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="AlwaysFalse"
+			description="Access is always denied"
+			class="org.owasp.esapi.reference.accesscontrol.AlwaysFalseACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameter"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoRuntimeParameterACR">
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoPolicyParameter"
+			description="Access depends on the value of the policy parameter: isTrue"
+			class="org.owasp.esapi.reference.accesscontrol.policyloader.EchoDynaBeanPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="true"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- 
+			The following Rules are for backwards compatibility with
+			the deprecated AcessController 1.0 reference implementation
+			specification
+		-->
+		<AccessControlRule
+			name="AC 1.0 Data"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/> 
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForData"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String, java.lang.Object"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 File"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFile"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Function"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForFunction"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 Service"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForService"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="AC 1.0 URL"
+			description="See delegateClass's code comments"
+			class="org.owasp.esapi.reference.accesscontrol.DelegatingACR">
+			<Parameters>
+				<Parameter name="delegateClass" type="String" value="org.owasp.esapi.reference.accesscontrol.FileBasedACRs"/>
+				<Parameter name="delegateMethod" type="String" value="isAuthorizedForURL"/>
+				<Parameter name="parameterClasses" type="StringArray" value="java.lang.String"/>
+			</Parameters>
+		</AccessControlRule>
+		
+		<!-- End Rules for backwards compatibility with Access Controller 1.0 -->
+	</AccessControlRules>
+</AccessControlPolicy>
+
+
+
+<!-- 
+We have these as runtime tests, but not as policy file load tests yet.
+		<AccessControlRule
+			name="EchoRuntimeParameterClassCastException"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="This is not a boolean"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueNull"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value="null"/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueEmpty"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean" value=""/>
+			</Parameters>
+		</AccessControlRule>
+		<AccessControlRule
+			name="EchoRuntimeParameterValueMissing"
+			description="Access depends on the value of the runtime parameter"
+			class="org.owasp.esapi.reference.accesscontrol.EchoPolicyParameterACR">
+			<Parameters>
+				<Parameter name="isTrue" type="Boolean"/>
+			</Parameters>
+		</AccessControlRule>
+-->
+
+<!-- we should add tests for name and type errors too -->
diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties
new file mode 100644
index 0000000..1435b7c
--- /dev/null
+++ b/src/test/resources/esapi/ESAPI.properties
@@ -0,0 +1,465 @@
+#
+# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
+# 
+# This file is part of the Open Web Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2008,2009 - The OWASP Foundation
+#
+# DISCUSS: This may cause a major backwards compatibility issue, etc. but
+#		   from a name space perspective, we probably should have prefaced
+#		   all the property names with ESAPI or at least OWASP. Otherwise
+#		   there could be problems is someone loads this properties file into
+#		   the System properties.  We could also put this file into the
+#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
+#		   ESAPI properties be defined that would overwrite these defaults.
+#		   That keeps the application's properties relatively simple as usually
+#		   they will only want to override a few properties. If looks like we
+#		   already support multiple override levels of this in the
+#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
+#		   defaults in the esapi.jar itself. That way, if the jar is signed,
+#		   we could detect if those properties had been tampered with. (The
+#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
+#		   but off course there is an execution penalty (similar to the way
+#		   that the separate sunjce.jar used to be when a class from it was
+#		   first loaded). Thoughts?
+###############################################################################
+#
+# WARNING: Operating system protection should be used to lock down the .esapi
+# resources directory and all the files inside and all the directories all the
+# way up to the root directory of the file system.  Note that if you are using
+# file-based implementations, that some files may need to be read-write as they
+# get updated dynamically.
+#
+# Before using, be sure to update the MasterKey and MasterSalt as described below.
+# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
+#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
+#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
+#		able to decrypt your data with ESAPI 2.0.
+#
+#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
+#
+#===========================================================================
+# ESAPI Configuration
+#
+# If true, then print all the ESAPI properties set here when they are loaded.
+# If false, they are not printed. Useful to reduce output when running JUnit tests.
+# If you need to troubleshoot a properties related problem, turning this on may help,
+# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
+# as part of production ESAPI, mostly for backward compatibility.)
+ESAPI.printProperties=false
+
+# ESAPI is designed to be easily extensible. You can use the reference implementation
+# or implement your own providers to take advantage of your enterprise's security
+# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
+#
+#    String ciphertext =
+#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
+#    CipherText cipherText =
+#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
+#
+# Below you can specify the classname for the provider that you wish to use in your
+# application. The only requirement is that it implement the appropriate ESAPI interface.
+# This allows you to switch security implementations in the future without rewriting the
+# entire application.
+#
+# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
+ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
+# FileBasedAuthenticator requires users.txt file in .esapi directory
+ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
+ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
+ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
+
+ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
+ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
+ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
+# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
+ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
+#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
+#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
+ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
+ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
+
+#===========================================================================
+# ESAPI Authenticator
+#
+Authenticator.AllowedLoginAttempts=3
+Authenticator.MaxOldPasswordHashes=13
+Authenticator.UsernameParameterName=username
+Authenticator.PasswordParameterName=password
+# RememberTokenDuration (in days)
+Authenticator.RememberTokenDuration=14
+# Session Timeouts (in minutes)
+Authenticator.IdleTimeoutDuration=20
+Authenticator.AbsoluteTimeoutDuration=120
+
+#===========================================================================
+# ESAPI Encoder
+#
+# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
+# Failure to canonicalize input is a very common mistake when implementing validation schemes.
+# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
+# following code to canonicalize data.
+#
+#      ESAPI.Encoder().canonicalize( "%22hello world&#x22;" );
+#  
+# Multiple encoding is when a single encoding format is applied multiple times. Allowing
+# multiple encoding is strongly discouraged.
+Encoder.AllowMultipleEncoding=false
+
+# Mixed encoding is when multiple different encoding formats are applied, or when
+# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
+Encoder.AllowMixedEncoding=false
+
+# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
+# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
+# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
+Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
+
+
+#===========================================================================
+# ESAPI Encryption
+#
+# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
+# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+# There is not currently any support for key rotation, so be careful when changing your key and salt as it
+# will invalidate all signed, encrypted, and hashed data.
+#
+# WARNING: Not all combinations of algorithms and key lengths are supported.
+# If you choose to use a key length greater than 128, you MUST download the
+# unlimited strength policy files and install in the lib directory of your JRE/JDK.
+# See http://java.sun.com/javase/downloads/index.jsp for more information.
+#
+# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
+# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
+# possible, these methods should be avoided as they use ECB cipher mode, which in almost
+# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
+# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
+# should only use this compatibility setting if you have persistent data encrypted with
+# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
+# you have decrypted all of your old encrypted data and then re-encrypted it with
+# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
+# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
+# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
+# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
+# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
+# that requires downloading the special jurisdiction policy files mentioned above.)
+#
+#		***** IMPORTANT: These are for JUnit testing. Test files may have been
+#						 encrypted using these values so do not change these or
+#						 those tests will fail. The version under
+#							src/main/resources/.esapi/ESAPI.properties
+#						 will be delivered with Encryptor.MasterKey and
+#						 Encryptor.MasterSalt set to the empty string.
+#
+#						 FINAL NOTE:
+#                           If Maven changes these when run, that needs to be fixed.
+#       256-bit key... requires unlimited strength jurisdiction policy files
+### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
+#       128-bit key
+Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
+Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
+# Encryptor.MasterSalt=
+
+# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
+# encryption and hashing. (That is it will look to this provider first, but it
+# will defer to other providers if the requested algorithm is not implemented
+# by this provider.) If left unset, ESAPI will just use your Java VM's current
+# preferred JCE provider, which is generally set in the file
+# "$JAVA_HOME/jre/lib/security/java.security".
+#
+# The main intent of this is to allow ESAPI symmetric encryption to be
+# used with a FIPS 140-2 compliant crypto-module. For details, see the section
+# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
+# the ESAPI 2.0 Symmetric Encryption User Guide, at:
+# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
+# However, this property also allows you to easily use an alternate JCE provider
+# such as "Bouncy Castle" without having to make changes to "java.security".
+# See Javadoc for SecurityProviderLoader for further details. If you wish to use
+# a provider that is not known to SecurityProviderLoader, you may specify the
+# fully-qualified class name of the JCE provider class that implements
+# java.security.Provider. If the name contains a '.', this is interpreted as
+# a fully-qualified class name that implements java.security.Provider.
+#
+# NOTE: Setting this property has the side-effect of changing it in your application
+#       as well, so if you are using JCE in your application directly rather than
+#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
+#       preferred JCE provider there as well.
+#
+# Default: Keeps the JCE provider set to whatever JVM sets it to.
+Encryptor.PreferredJCEProvider=
+
+# AES is the most widely used and strongest encryption algorithm. This
+# should agree with your Encryptor.CipherTransformation property.
+# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
+# very weak. It is essentially a password-based encryption key, hashed
+# with MD5 around 1K times and then encrypted with the weak DES algorithm
+# (56-bits) using ECB mode and an unspecified padding (it is
+# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
+# "AES/CBC/PKCSPadding". If you want to change these, change them here.
+# Warning: This property does not control the default reference implementation for
+#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
+#		   in the future.
+# @deprecated
+Encryptor.EncryptionAlgorithm=AES
+#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
+Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
+
+# Applies to ESAPI 2.0 and later only!
+# Comma-separated list of cipher modes that provide *BOTH*
+# confidentiality *AND* message authenticity. (NIST refers to such cipher
+# modes as "combined modes" so that's what we shall call them.) If any of these
+# cipher modes are used then no MAC is calculated and stored
+# in the CipherText upon encryption. Likewise, if one of these
+# cipher modes is used with decryption, no attempt will be made
+# to validate the MAC contained in the CipherText object regardless
+# of whether it contains one or not. Since the expectation is that
+# these cipher modes support support message authenticity already,
+# injecting a MAC in the CipherText object would be at best redundant.
+#
+# Note that as of JDK 1.5, the SunJCE provider does not support *any*
+# of these cipher modes. Of these listed, only GCM and CCM are currently
+# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
+# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
+# padding modes.
+Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
+
+# Applies to ESAPI 2.0 and later only!
+# Additional cipher modes allowed for ESAPI 2.0 encryption. These
+# cipher modes are in _addition_ to those specified by the property
+# 'Encryptor.cipher_modes.combined_modes'.
+# Note: We will add support for streaming modes like CFB & OFB once
+# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
+# (probably in ESAPI 2.1).
+#
+#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
+#					here as this is an extremely weak mode. However, we *must*
+#					allow it here so we can test ECB mode. That is important
+#					since the logic is somewhat different (i.e., ECB mode does
+#					not use an IV).
+# DISCUSS: Better name?
+#	NOTE: ECB added only for testing purposes. Don't try this at home!
+Encryptor.cipher_modes.additional_allowed=CBC,ECB
+
+# 128-bit is almost always sufficient and appears to be more resistant to
+# related key attacks than is 256-bit AES. Use '_' to use default key size
+# for cipher algorithms (where it makes sense because the algorithm supports
+# a variable key size). Key length must agree to what's provided as the
+# cipher transformation, otherwise this will be ignored after logging a
+# warning.
+#
+# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
+Encryptor.EncryptionKeyLength=128
+
+# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
+# (All cipher modes except ECB require an IV.) There are two choices: we can either
+# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
+# the IV does not need to be hidden from adversaries, it is important that the
+# adversary not be allowed to choose it. Also, random IVs are generally much more
+# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
+# such as CFB and OFB use a different IV for each encryption with a given key so
+# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
+# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
+# uncomment the Encryptor.fixedIV.
+#
+# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
+Encryptor.ChooseIVMethod=random
+# If you choose to use a fixed IV, then you must place a fixed IV here that
+# is known to all others who are sharing your secret key. The format should
+# be a hex string that is the same length as the cipher block size for the
+# cipher algorithm that you are using. The following is an example for AES
+# from an AES test vector for AES-128/CBC as described in:
+# NIST Special Publication 800-38A (2001 Edition)
+# "Recommendation for Block Cipher Modes of Operation".
+# (Note that the block size for AES is 16 bytes == 128 bits.)
+#
+Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
+
+# Whether or not CipherText should use a message authentication code (MAC) with it.
+# This prevents an adversary from altering the IV as well as allowing a more
+# fool-proof way of determining the decryption failed because of an incorrect
+# key being supplied. This refers to the "separate" MAC calculated and stored
+# in CipherText, not part of any MAC that is calculated as a result of a
+# "combined mode" cipher mode.
+#
+# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
+# set this property to false.
+Encryptor.CipherText.useMAC=true
+
+# Whether or not the PlainText object may be overwritten and then marked
+# eligible for garbage collection. If not set, this is still treated as 'true'.
+Encryptor.PlainText.overwrite=true
+
+# Do not use DES except in a legacy situations. 56-bit is way too small key size.
+#Encryptor.EncryptionKeyLength=56
+#Encryptor.EncryptionAlgorithm=DES
+
+# TripleDES is considered strong enough for most purposes.
+#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
+#			requires downloading the special jurisdiction policy from Sun.
+#Encryptor.EncryptionKeyLength=168
+#Encryptor.EncryptionAlgorithm=DESede
+
+Encryptor.HashAlgorithm=SHA-512
+Encryptor.HashIterations=1024
+Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
+Encryptor.DigitalSignatureKeyLength=1024
+Encryptor.RandomAlgorithm=SHA1PRNG
+Encryptor.CharacterEncoding=UTF-8
+# Currently supported choices for JDK 1.5 and 1.6 are:
+#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
+#	HmacSHA512 (512 bits).
+# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
+# these JDKs support it.
+Encryptor.KDF.PRF=HmacSHA256
+
+#===========================================================================
+# ESAPI HttpUtilties
+#
+# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
+# protect against malicious data from attackers, such as unprintable characters, escaped characters,
+# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
+# headers, and CSRF tokens.
+#
+# Default file upload location (remember to escape backslashes with \\)
+HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
+# let this default to java.io.tmpdir for testing
+#HttpUtilities.UploadTempDir=C:\\temp
+# Force flags on cookies, if you use HttpUtilities to set cookies
+HttpUtilities.ForceHttpOnlySession=false
+HttpUtilities.ForceSecureSession=false
+HttpUtilities.ForceHttpOnlyCookies=true
+HttpUtilities.ForceSecureCookies=true
+# Maximum size of HTTP headers
+HttpUtilities.MaxHeaderSize=4096
+# File upload configuration
+HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
+HttpUtilities.MaxUploadFileBytes=500000000
+# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
+# container, and any other technologies you may be using. Failure to do this may expose you
+# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
+HttpUtilities.ResponseContentType=text/html; charset=UTF-8
+# This is the name of the cookie used to represent the HTTP session
+# Typically this will be the default "JSESSIONID" 
+HttpUtilities.HttpSessionIdName=JSESSIONID
+
+
+
+#===========================================================================
+# ESAPI Executor
+# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
+Executor.WorkingDirectory=C:\\Windows\\Temp
+Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
+
+
+#===========================================================================
+# ESAPI Logging
+# Set the application name if these logs are combined with other applications
+Logger.ApplicationName=ExampleApplication
+# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
+Logger.LogEncodingRequired=false
+# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
+Logger.LogApplicationName=true
+# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
+Logger.LogServerIP=true
+# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
+# want to place it in a specific directory.
+Logger.LogFileName=ESAPI_logging_file
+# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
+Logger.MaxLogFileSize=10000000
+
+
+#===========================================================================
+# ESAPI Intrusion Detection
+#
+# Each event has a base to which .count, .interval, and .action are added
+# The IntrusionException will fire if we receive "count" events within "interval" seconds
+# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
+#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
+#
+# Custom Events
+# Names must start with "event." as the base
+# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
+# You can also disable intrusion detection completely by changing
+# the following parameter to true
+#
+IntrusionDetector.Disable=false
+#
+IntrusionDetector.event.test.count=2
+IntrusionDetector.event.test.interval=10
+IntrusionDetector.event.test.actions=disable,log
+
+# Exception Events
+# All EnterpriseSecurityExceptions are registered automatically
+# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
+# Use the fully qualified classname of the exception as the base
+
+# any intrusion is an attack
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
+
+# for test purposes
+# CHECKME: Shouldn't there be something in the property name itself that designates
+#		   that these are for testing???
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
+
+# rapid validation errors indicate scans or attacks in progress
+# org.owasp.esapi.errors.ValidationException.count=10
+# org.owasp.esapi.errors.ValidationException.interval=10
+# org.owasp.esapi.errors.ValidationException.actions=log,logout
+
+# sessions jumping between hosts indicates session hijacking
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
+
+
+#===========================================================================
+# ESAPI Validation
+#
+# The ESAPI Validator works on regular expressions with defined names. You can define names
+# either here, or you may define application specific patterns in a separate file defined below.
+# This allows enterprises to specify both organizational standards as well as application specific
+# validation rules.
+#
+Validator.ConfigurationFile=validation.properties
+
+# Validators used by ESAPI
+Validator.AccountName=^[a-zA-Z0-9]{3,20}$
+Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
+Validator.RoleName=^[a-z]{1,20}$
+Validator.Redirect=^\\/test.*$
+
+# Global HTTP Validation Rules
+# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
+Validator.HTTPScheme=^(http|https)$
+Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
+Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
+Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
+Validator.HTTPURL=^.*$
+Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
+
+# Contributed by Fraenku at gmx.ch
+# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
+Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
+Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
+Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
+Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
+Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
+
+
+# Validation of file related input
+Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+
+# Validation of dates. Controls whether or not 'lenient' dates are accepted.
+# See DataFormat.setLenient(boolean flag) for further details.
+Validator.AcceptLenientDates=false
\ No newline at end of file
diff --git a/src/test/resources/esapi/antisamy-esapi.xml b/src/test/resources/esapi/antisamy-esapi.xml
new file mode 100644
index 0000000..500ab59
--- /dev/null
+++ b/src/test/resources/esapi/antisamy-esapi.xml
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+	
+<!-- 
+W3C rules retrieved from:
+http://www.w3.org/TR/html401/struct/global.html
+-->
+	
+<!--
+Slashdot allowed tags taken from "Reply" page:
+<b> <i> <p> <br> <a> <ol> <ul> <li> <dl> <dt> <dd> <em> <strong> <tt> <blockquote> <div> <ecode> <quote>
+-->
+
+<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xsi:noNamespaceSchemaLocation="antisamy.xsd">
+	
+	<directives>
+		<directive name="omitXmlDeclaration" value="true"/>
+		<directive name="omitDoctypeDeclaration" value="true"/>
+		<directive name="maxInputSize" value="500000"/>
+		<directive name="embedStyleSheets" value="false"/>
+	</directives>
+	
+	
+	<common-regexps>
+		
+		<!-- 
+		From W3C:
+		This attribute assigns a class name or set of class names to an
+		element. Any number of elements may be assigned the same class
+		name or names. Multiple class names must be separated by white 
+		space characters.
+		-->
+		
+		<regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' -->
+		<regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/>
+		<regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/>
+	
+	</common-regexps>
+	
+	<!-- 
+	
+	Tag.name = a, b, div, body, etc.
+	Tag.action = filter: remove tags, but keep content, validate: keep content as long as it passes rules, remove: remove tag and contents
+	Attribute.name = id, class, href, align, width, etc.
+	Attribute.onInvalid = what to do when the attribute is invalid, e.g., remove the tag (removeTag), remove the attribute (removeAttribute), filter the tag (filterTag)
+	Attribute.description = What rules in English you want to tell the users they can have for this attribute. Include helpful things so they'll be able to tune their HTML
+	 
+	 -->
+
+	<!-- 
+	Some attributes are common to all (or most) HTML tags. There aren't many that qualify for this. You have to make sure there's no
+	collisions between any of these attribute names with attribute names of other tags that are for different purposes.
+	-->
+
+	<common-attributes>
+		
+
+		<attribute name="lang" description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
+		 	<regexp-list>
+		 		<regexp value="[a-zA-Z]{2,20}"/>
+		 	</regexp-list>
+		 </attribute>
+		 
+		 <attribute name="title" description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
+		 	<regexp-list>
+		 		<regexp name="htmlTitle"/>
+		 	</regexp-list>
+		 </attribute>
+
+		<attribute name="href" onInvalid="filterTag">
+			<regexp-list>
+				<regexp name="onsiteURL"/>
+				<regexp name="offsiteURL"/>
+			</regexp-list>
+		</attribute>
+	
+		<attribute name="align" description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
+			<literal-list>
+				<literal value="center"/>
+				<literal value="left"/>
+				<literal value="right"/>
+				<literal value="justify"/>
+				<literal value="char"/>
+			</literal-list>
+		</attribute>
+
+	</common-attributes>
+
+
+	<!--
+	This requires normal updates as browsers continue to diverge from the W3C and each other. As long as the browser wars continue
+	this is going to continue. I'm not sure war is the right word for what's going on. Doesn't somebody have to win a war after 
+	a while?
+	 -->
+	
+	<global-tag-attributes>
+		<attribute name="title"/>
+		<attribute name="lang"/>
+	</global-tag-attributes>
+
+
+	<tag-rules>
+
+		<!-- Tags related to JavaScript -->
+
+		<tag name="script" action="remove"/>
+		<tag name="noscript" action="remove"/>
+		
+		<!-- Frame & related tags -->
+		
+		<tag name="iframe" action="remove"/>
+		<tag name="frameset" action="remove"/>
+		<tag name="frame" action="remove"/>
+		<tag name="noframes" action="remove"/>
+		
+
+		<!-- All reasonable formatting tags -->
+		
+		<tag name="p" action="validate">
+			<attribute name="align"/>
+		</tag>
+
+		<tag name="div" action="validate"/>		
+		<tag name="i" action="validate"/>
+		<tag name="b" action="validate"/>
+		<tag name="em" action="validate"/>
+		<tag name="blockquote" action="validate"/>
+		<tag name="tt" action="validate"/>
+		
+		<tag name="br" action="truncate"/>
+
+		<!-- Custom Slashdot tags, though we're trimming the idea of having a possible mismatching end tag with the endtag="" attribute -->
+		
+		<tag name="quote" action="validate"/>
+		<tag name="ecode" action="validate"/> 
+		
+						
+		<!-- Anchor and anchor related tags -->
+		
+		<tag name="a" action="validate">
+
+			<attribute name="href" onInvalid="filterTag"/>
+			<attribute name="nohref">
+				<literal-list>
+					<literal value="nohref"/>
+					<literal value=""/>
+				</literal-list>
+			</attribute>
+			<attribute name="rel">
+				<literal-list>
+					<literal value="nofollow"/>
+				</literal-list>
+			</attribute>
+		</tag>
+
+		<!-- List tags -->
+
+		<tag name="ul" action="validate"/>
+		<tag name="ol" action="validate"/>
+		<tag name="li" action="validate"/>
+		
+	</tag-rules>
+
+
+
+	<!--  No CSS on Slashdot posts -->
+
+	<css-rules>
+	</css-rules>
+
+
+	<html-entities>
+		<entity name="amp" cdata="&"/>
+		<entity name="nbsp" cdata="&#160;"/>
+		
+		<entity name="iexcl" cdata="&#161;"/> <!--inverted exclamation mark, U+00A1 ISOnum -->
+		<entity name="cent" cdata="&#162;"/> <!--cent sign, U+00A2 ISOnum -->
+		<entity name="pound" cdata="&#163;"/> <!--pound sign, U+00A3 ISOnum -->
+		<entity name="curren" cdata="&#164;"/> <!--currency sign, U+00A4 ISOnum -->
+		<entity name="yen" cdata="&#165;"/> <!--yen sign = yuan sign, U+00A5 ISOnum -->
+		<entity name="brvbar" cdata="&#166;"/> <!--broken bar = broken vertical bar, U+00A6 ISOnum -->
+		<entity name="sect" cdata="&#167;"/> <!--section sign, U+00A7 ISOnum -->
+		<entity name="uml" cdata="&#168;"/> <!--diaeresis = spacing diaeresis, U+00A8 ISOdia -->
+		<entity name="copy" cdata="&#169;"/> <!--copyright sign, U+00A9 ISOnum -->
+		<entity name="ordf" cdata="&#170;"/> <!--feminine ordinal indicator, U+00AA ISOnum -->
+		<entity name="laquo" cdata="&#171;"/> <!--left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum -->
+		<entity name="not" cdata="&#172;"/> <!--not sign, U+00AC ISOnum -->
+		<entity name="shy" cdata="&#173;"/> <!--soft hyphen = discretionary hyphen,U+00AD ISOnum -->
+		<entity name="reg" cdata="&#174;"/> <!--registered sign = registered trade mark sign, U+00AE ISOnum -->
+		<entity name="macr" cdata="&#175;"/> <!--macron = spacing macron = overline = APL overbar, U+00AF ISOdia -->
+		<entity name="deg" cdata="&#176;"/> <!--degree sign, U+00B0 ISOnum -->
+		<entity name="plusmn" cdata="&#177;"/> <!--plus-minus sign = plus-or-minus sign, U+00B1 ISOnum -->
+		<entity name="sup2" cdata="&#178;"/> <!--superscript two = superscript digit two = squared, U+00B2 ISOnum -->
+		<entity name="sup3" cdata="&#179;"/> <!--superscript three = superscript digit three= cubed, U+00B3 ISOnum -->
+		<entity name="acute" cdata="&#180;"/> <!--acute accent = spacing acute, U+00B4 ISOdia -->
+		<entity name="micro" cdata="&#181;"/> <!--micro sign, U+00B5 ISOnum -->
+		<entity name="para" cdata="&#182;"/> <!--pilcrow sign = paragraph sign, U+00B6 ISOnum -->
+		<entity name="middot" cdata="&#183;"/> <!--middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum -->
+		<entity name="cedil" cdata="&#184;"/> <!--cedilla = spacing cedilla, U+00B8 ISOdia -->
+		<entity name="sup1" cdata="&#185;"/> <!--superscript one = superscript digit one,U+00B9 ISOnum -->
+		<entity name="ordm" cdata="&#186;"/> <!--masculine ordinal indicator, U+00BA ISOnum -->
+		<entity name="raquo" cdata="&#187;"/> <!--right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum -->
+		<entity name="frac14" cdata="&#188;"/> <!--vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum -->
+		<entity name="frac12" cdata="&#189;"/> <!--vulgar fraction one half = fraction one half, U+00BD ISOnum -->
+		<entity name="frac34" cdata="&#190;"/> <!--vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum -->
+		<entity name="iquest" cdata="&#191;"/> <!--inverted question mark = turned question mark, U+00BF ISOnum -->
+		<entity name="Agrave" cdata="&#192;"/> <!--latin capital letter A with grave = latin capital letter A grave,U+00C0 ISOlat1 -->
+		<entity name="Aacute" cdata="&#193;"/> <!--latin capital letter A with acute,U+00C1 ISOlat1 -->
+		<entity name="Acirc" cdata="&#194;"/> <!--latin capital letter A with circumflex,U+00C2 ISOlat1 -->
+		<entity name="Atilde" cdata="&#195;"/> <!--latin capital letter A with tilde,U+00C3 ISOlat1 -->
+		<entity name="Auml" cdata="&#196;"/> <!--latin capital letter A with diaeresis,U+00C4 ISOlat1 -->
+		<entity name="Aring" cdata="&#197;"/> <!--latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 -->
+		<entity name="AElig" cdata="&#198;"/> <!--latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 -->
+		<entity name="Ccedil" cdata="&#199;"/> <!--latin capital letter C with cedilla, U+00C7 ISOlat1 -->
+		<entity name="Egrave" cdata="&#200;"/> <!--latin capital letter E with grave, U+00C8 ISOlat1 -->
+		<entity name="Eacute" cdata="&#201;"/> <!--latin capital letter E with acute,U+00C9 ISOlat1 -->
+		<entity name="Ecirc" cdata="&#202;"/> <!--latin capital letter E with circumflex,U+00CA ISOlat1 -->
+		<entity name="Euml" cdata="&#203;"/> <!--latin capital letter E with diaeresis, U+00CB ISOlat1 -->
+		<entity name="Igrave" cdata="&#204;"/> <!--latin capital letter I with grave, U+00CC ISOlat1 -->
+		<entity name="Iacute" cdata="&#205;"/> <!--latin capital letter I with acute, U+00CD ISOlat1 -->
+		<entity name="Icirc" cdata="&#206;"/> <!--latin capital letter I with circumflex, U+00CE ISOlat1 -->
+		<entity name="Iuml" cdata="&#207;"/> <!--latin capital letter I with diaeresis, U+00CF ISOlat1 -->
+		<entity name="ETH" cdata="&#208;"/> <!--latin capital letter ETH, U+00D0 ISOlat1 -->
+		<entity name="Ntilde" cdata="&#209;"/> <!--latin capital letter N with tilde, U+00D1 ISOlat1 -->
+		<entity name="Ograve" cdata="&#210;"/> <!--latin capital letter O with grave, U+00D2 ISOlat1 -->
+		<entity name="Oacute" cdata="&#211;"/> <!--latin capital letter O with acute, U+00D3 ISOlat1 -->
+		<entity name="Ocirc" cdata="&#212;"/> <!--latin capital letter O with circumflex, U+00D4 ISOlat1 -->
+		<entity name="Otilde" cdata="&#213;"/> <!--latin capital letter O with tilde, U+00D5 ISOlat1 -->
+		<entity name="Ouml" cdata="&#214;"/> <!--latin capital letter O with diaeresis, U+00D6 ISOlat1 -->
+		<entity name="times" cdata="&#215;"/> <!--multiplication sign, U+00D7 ISOnum -->
+		<entity name="Oslash" cdata="&#216;"/> <!--latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 -->
+		<entity name="Ugrave" cdata="&#217;"/> <!--latin capital letter U with grave, U+00D9 ISOlat1 -->
+		<entity name="Uacute" cdata="&#218;"/> <!--latin capital letter U with acute, U+00DA ISOlat1 -->
+		<entity name="Ucirc" cdata="&#219;"/> <!--latin capital letter U with circumflex, U+00DB ISOlat1 -->
+		<entity name="Uuml" cdata="&#220;"/> <!--latin capital letter U with diaeresis, U+00DC ISOlat1 -->
+		<entity name="Yacute" cdata="&#221;"/> <!--latin capital letter Y with acute, U+00DD ISOlat1 -->
+		<entity name="THORN" cdata="&#222;"/> <!--latin capital letter THORN, U+00DE ISOlat1 -->
+		<entity name="szlig" cdata="&#223;"/> <!--latin small letter sharp s = ess-zed, U+00DF ISOlat1 -->
+		<entity name="agrave" cdata="&#224;"/> <!--latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 -->
+		<entity name="aacute" cdata="&#225;"/> <!--latin small letter a with acute, U+00E1 ISOlat1 -->
+		<entity name="acirc" cdata="&#226;"/> <!--latin small letter a with circumflex, U+00E2 ISOlat1 -->
+		<entity name="atilde" cdata="&#227;"/> <!--latin small letter a with tilde, U+00E3 ISOlat1 -->
+		<entity name="auml" cdata="&#228;"/> <!--latin small letter a with diaeresis, U+00E4 ISOlat1 -->
+		<entity name="aring" cdata="&#229;"/> <!--latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 -->
+		<entity name="aelig" cdata="&#230;"/> <!--latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 -->
+		<entity name="ccedil" cdata="&#231;"/> <!--latin small letter c with cedilla, U+00E7 ISOlat1 -->
+		<entity name="egrave" cdata="&#232;"/> <!--latin small letter e with grave, U+00E8 ISOlat1 -->
+		<entity name="eacute" cdata="&#233;"/> <!--latin small letter e with acute, U+00E9 ISOlat1 -->
+		<entity name="ecirc" cdata="&#234;"/> <!--latin small letter e with circumflex, U+00EA ISOlat1 -->
+		<entity name="euml" cdata="&#235;"/> <!--latin small letter e with diaeresis, U+00EB ISOlat1 -->
+		<entity name="igrave" cdata="&#236;"/> <!--latin small letter i with grave, U+00EC ISOlat1 -->
+		<entity name="iacute" cdata="&#237;"/> <!--latin small letter i with acute, U+00ED ISOlat1 -->
+		<entity name="icirc" cdata="&#238;"/> <!--latin small letter i with circumflex, U+00EE ISOlat1 -->
+		<entity name="iuml" cdata="&#239;"/> <!--latin small letter i with diaeresis, U+00EF ISOlat1 -->
+		<entity name="eth" cdata="&#240;"/> <!--latin small letter eth, U+00F0 ISOlat1 -->
+		<entity name="ntilde" cdata="&#241;"/> <!--latin small letter n with tilde, U+00F1 ISOlat1 -->
+		<entity name="ograve" cdata="&#242;"/> <!--latin small letter o with grave, U+00F2 ISOlat1 -->
+		<entity name="oacute" cdata="&#243;"/> <!--latin small letter o with acute, U+00F3 ISOlat1 -->
+		<entity name="ocirc " cdata="&#244;"/> <!--latin small letter o with circumflex, U+00F4 ISOlat1 -->
+		<entity name="otilde" cdata="&#245;"/> <!--latin small letter o with tilde, U+00F5 ISOlat1 -->
+		<entity name="ouml" cdata="&#246;"/> <!--latin small letter o with diaeresis, U+00F6 ISOlat1 -->
+		<entity name="divide" cdata="&#247;"/> <!--division sign, U+00F7 ISOnum -->
+		<entity name="oslash" cdata="&#248;"/> <!--latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 -->
+		<entity name="ugrave" cdata="&#249;"/> <!--latin small letter u with grave, U+00F9 ISOlat1 -->
+		<entity name="uacute" cdata="&#250;"/> <!--latin small letter u with acute, U+00FA ISOlat1 -->
+		<entity name="ucirc" cdata="&#251;"/> <!--latin small letter u with circumflex, U+00FB ISOlat1 -->
+		<entity name="uuml" cdata="&#252;"/> <!--latin small letter u with diaeresis, U+00FC ISOlat1 -->
+		<entity name="yacute" cdata="&#253;"/> <!--latin small letter y with acute, U+00FD ISOlat1 -->
+		<entity name="thorn" cdata="&#254;"/> <!--latin small letter thorn, U+00FE ISOlat1 -->
+		<entity name="yuml" cdata="&#255;"/> <!--latin small letter y with diaeresis, U+00FF ISOlat1 -->
+		                                  
+		<entity name="fnof" cdata="&#402;"/> <!--latin small f with hook = function = florin, U+0192 ISOtech -->
+		
+		<!-- Greek -->
+		<entity name="Alpha" cdata="&#913;"/> <!--greek capital letter alpha, U+0391 -->
+		<entity name="Beta" cdata="&#914;"/> <!--greek capital letter beta, U+0392 -->
+		<entity name="Gamma" cdata="&#915;"/> <!--greek capital letter gamma, U+0393 ISOgrk3 -->
+		<entity name="Delta" cdata="&#916;"/> <!--greek capital letter delta, U+0394 ISOgrk3 -->
+		<entity name="Epsilon" cdata="&#917;"/> <!--greek capital letter epsilon, U+0395 -->
+		<entity name="Zeta" cdata="&#918;"/> <!--greek capital letter zeta, U+0396 -->
+		<entity name="Eta" cdata="&#919;"/> <!--greek capital letter eta, U+0397 -->
+		<entity name="Theta" cdata="&#920;"/> <!--greek capital letter theta, U+0398 ISOgrk3 -->
+		<entity name="Iota" cdata="&#921;"/> <!--greek capital letter iota, U+0399 -->
+		<entity name="Kappa" cdata="&#922;"/> <!--greek capital letter kappa, U+039A -->
+		<entity name="Lambda" cdata="&#923;"/> <!--greek capital letter lambda, U+039B ISOgrk3 -->
+		<entity name="Mu" cdata="&#924;"/> <!--greek capital letter mu, U+039C -->
+		<entity name="Nu" cdata="&#925;"/> <!--greek capital letter nu, U+039D -->
+		<entity name="Xi" cdata="&#926;"/> <!--greek capital letter xi, U+039E ISOgrk3 -->
+		<entity name="Omicron" cdata="&#927;"/> <!--greek capital letter omicron, U+039F -->
+		<entity name="Pi" cdata="&#928;"/> <!--greek capital letter pi, U+03A0 ISOgrk3 -->
+		<entity name="Rho" cdata="&#929;"/> <!--greek capital letter rho, U+03A1 -->
+		<!-- there is no Sigmaf, and no U+03A2 character either -->
+		<entity name="Sigma" cdata="&#931;"/> <!--greek capital letter sigma, U+03A3 ISOgrk3 -->
+		<entity name="Tau" cdata="&#932;"/> <!--greek capital letter tau, U+03A4 -->
+		<entity name="Upsilon" cdata="&#933;"/> <!--greek capital letter upsilon,U+03A5 ISOgrk3 -->
+		<entity name="Phi" cdata="&#934;"/> <!--greek capital letter phi,U+03A6 ISOgrk3 -->
+		<entity name="Chi" cdata="&#935;"/> <!--greek capital letter chi, U+03A7 -->
+		<entity name="Psi" cdata="&#936;"/> <!--greek capital letter psi,U+03A8 ISOgrk3 -->
+		<entity name="Omega" cdata="&#937;"/> <!--greek capital letter omega,U+03A9 ISOgrk3 -->
+		
+		<entity name="alpha" cdata="&#945;"/> <!--greek small letter alpha,U+03B1 ISOgrk3 -->
+		<entity name="beta" cdata="&#946;"/> <!--greek small letter beta, U+03B2 ISOgrk3 -->
+		<entity name="gamma" cdata="&#947;"/> <!--greek small letter gamma,U+03B3 ISOgrk3 -->
+		<entity name="delta" cdata="&#948;"/> <!--greek small letter delta,U+03B4 ISOgrk3 -->
+		<entity name="epsilon" cdata="&#949;"/> <!--greek small letter epsilon,U+03B5 ISOgrk3 -->
+		<entity name="zeta" cdata="&#950;"/> <!--greek small letter zeta, U+03B6 ISOgrk3 -->
+		<entity name="eta" cdata="&#951;"/> <!--greek small letter eta, U+03B7 ISOgrk3 -->
+		<entity name="theta" cdata="&#952;"/> <!--greek small letter theta, U+03B8 ISOgrk3 -->
+		<entity name="iota" cdata="&#953;"/> <!--greek small letter iota, U+03B9 ISOgrk3 -->
+		<entity name="kappa" cdata="&#954;"/> <!--greek small letter kappa,U+03BA ISOgrk3 -->
+		<entity name="lambda" cdata="&#955;"/> <!--greek small letter lambda, U+03BB ISOgrk3 -->
+		<entity name="mu" cdata="&#956;"/> <!--greek small letter mu, U+03BC ISOgrk3 -->
+		<entity name="nu" cdata="&#957;"/> <!--greek small letter nu, U+03BD ISOgrk3 -->
+		<entity name="xi" cdata="&#958;"/> <!--greek small letter xi, U+03BE ISOgrk3 -->
+		<entity name="omicron" cdata="&#959;"/> <!--greek small letter omicron, U+03BF NEW -->
+		<entity name="pi" cdata="&#960;"/> <!--greek small letter pi, U+03C0 ISOgrk3 -->
+		<entity name="rho" cdata="&#961;"/> <!--greek small letter rho, U+03C1 ISOgrk3 -->
+		<entity name="sigmaf" cdata="&#962;"/> <!--greek small letter final sigma, U+03C2 ISOgrk3 -->
+		<entity name="sigma" cdata="&#963;"/> <!--greek small letter sigma, U+03C3 ISOgrk3 -->
+		<entity name="tau" cdata="&#964;"/> <!--greek small letter tau, U+03C4 ISOgrk3 -->
+		<entity name="upsilon" cdata="&#965;"/> <!--greek small letter upsilon, U+03C5 ISOgrk3 -->
+		<entity name="phi" cdata="&#966;"/> <!--greek small letter phi, U+03C6 ISOgrk3 -->
+		<entity name="chi" cdata="&#967;"/> <!--greek small letter chi, U+03C7 ISOgrk3 -->
+		<entity name="psi" cdata="&#968;"/> <!--greek small letter psi, U+03C8 ISOgrk3 -->
+		<entity name="omega" cdata="&#969;"/> <!--greek small letter omega, U+03C9 ISOgrk3 -->
+		<entity name="thetasym" cdata="&#977;"/> <!--greek small letter theta symbol, U+03D1 NEW -->
+		<entity name="upsih" cdata="&#978;"/> <!--greek upsilon with hook symbol, U+03D2 NEW -->
+		<entity name="piv" cdata="&#982;"/> <!--greek pi symbol, U+03D6 ISOgrk3 -->
+		
+		<!-- General Punctuation -->
+		<entity name="bull" cdata="&#8226;"/> <!--bullet = black small circle, U+2022 ISOpub  -->
+		<!-- bullet is NOT the same as bullet operator, U+2219 -->
+		<entity name="hellip" cdata="&#8230;"/> <!--horizontal ellipsis = three dot leader, U+2026 ISOpub  -->
+		<entity name="prime" cdata="&#8242;"/> <!--prime = minutes = feet, U+2032 ISOtech -->
+		<entity name="Prime" cdata="&#8243;"/> <!--double prime = seconds = inches, U+2033 ISOtech -->
+		<entity name="oline" cdata="&#8254;"/> <!--overline = spacing overscore, U+203E NEW -->
+		<entity name="frasl" cdata="&#8260;"/> <!--fraction slash, U+2044 NEW -->
+		
+		<!-- Letterlike Symbols -->
+		<entity name="weierp" cdata="&#8472;"/> <!--script capital P = power set = Weierstrass p, U+2118 ISOamso -->
+		<entity name="image" cdata="&#8465;"/> <!--blackletter capital I = imaginary part, U+2111 ISOamso -->
+		<entity name="real" cdata="&#8476;"/> <!--blackletter capital R = real part symbol, U+211C ISOamso -->
+		<entity name="trade" cdata="&#8482;"/> <!--trade mark sign, U+2122 ISOnum -->
+		<entity name="alefsym" cdata="&#8501;"/> <!--alef symbol = first transfinite cardinal, U+2135 NEW -->
+		<!-- alef symbol is NOT the same as hebrew letter alef,
+		     U+05D0 although the same glyph could be used to depict both characters -->
+		
+		<!-- Arrows -->
+		<entity name="larr" cdata="&#8592;"/> <!--leftwards arrow, U+2190 ISOnum -->
+		<entity name="uarr" cdata="&#8593;"/> <!--upwards arrow, U+2191 ISOnum-->
+		<entity name="rarr" cdata="&#8594;"/> <!--rightwards arrow, U+2192 ISOnum -->
+		<entity name="darr" cdata="&#8595;"/> <!--downwards arrow, U+2193 ISOnum -->
+		<entity name="harr" cdata="&#8596;"/> <!--left right arrow, U+2194 ISOamsa -->
+		<entity name="crarr" cdata="&#8629;"/> <!--downwards arrow with corner leftwards
+		                                     = carriage return, U+21B5 NEW -->
+		<entity name="lArr" cdata="&#8656;"/> <!--leftwards double arrow, U+21D0 ISOtech -->
+		
+		<!-- ISO 10646 does not say that lArr is the same as the 'is implied by' arrow
+		    but also does not have any other character for that function. So ? lArr can
+		    be used for 'is implied by' as ISOtech suggests -->
+		    
+		<entity name="uArr" cdata="&#8657;"/> <!--upwards double arrow, U+21D1 ISOamsa -->
+		<entity name="rArr" cdata="&#8658;"/> <!--rightwards double arrow, U+21D2 ISOtech -->
+		
+		<!-- ISO 10646 does not say this is the 'implies' character but does not have 
+		     another character with this function so ?
+		     rArr can be used for 'implies' as ISOtech suggests -->
+		     
+		<entity name="dArr" cdata="&#8659;"/> <!--downwards double arrow, U+21D3 ISOamsa -->
+		<entity name="hArr" cdata="&#8660;"/> <!--left right double arrow, U+21D4 ISOamsa -->
+		
+		<!-- Mathematical Operators -->
+		<entity name="forall" cdata="&#8704;"/> <!--for all, U+2200 ISOtech -->
+		<entity name="part" cdata="&#8706;"/> <!--partial differential, U+2202 ISOtech  -->
+		<entity name="exist" cdata="&#8707;"/> <!--there exists, U+2203 ISOtech -->
+		<entity name="empty" cdata="&#8709;"/> <!--empty set = null set = diameter,U+2205 ISOamso -->
+		<entity name="nabla" cdata="&#8711;"/> <!--nabla = backward difference, U+2207 ISOtech -->
+		<entity name="isin" cdata="&#8712;"/> <!--element of, U+2208 ISOtech -->
+		<entity name="notin" cdata="&#8713;"/> <!--not an element of, U+2209 ISOtech -->
+		<entity name="ni" cdata="&#8715;"/> <!--contains as member, U+220B ISOtech -->
+	
+		<!-- should there be a more memorable name than 'ni'? -->
+		<entity name="prod" cdata="&#8719;"/> <!--n-ary product = product sign, U+220F ISOamsb -->
+		
+		<!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though
+		     the same glyph might be used for both -->
+		     
+		<entity name="sum" cdata="&#8721;"/> <!--n-ary sumation, U+2211 ISOamsb -->
+		
+		<!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+		     though the same glyph might be used for both -->
+		
+		<entity name="minus" cdata="&#8722;"/> <!--minus sign, U+2212 ISOtech -->
+		<entity name="lowast" cdata="&#8727;"/> <!--asterisk operator, U+2217 ISOtech -->
+		<entity name="radic" cdata="&#8730;"/> <!--square root = radical sign, U+221A ISOtech -->
+		<entity name="prop" cdata="&#8733;"/> <!--proportional to, U+221D ISOtech -->
+		<entity name="infin" cdata="&#8734;"/> <!--infinity, U+221E ISOtech -->
+		<entity name="ang" cdata="&#8736;"/> <!--angle, U+2220 ISOamso -->
+		<entity name="and" cdata="&#8743;"/> <!--logical and = wedge, U+2227 ISOtech -->
+		<entity name="or" cdata="&#8744;"/> <!--logical or = vee, U+2228 ISOtech -->
+		<entity name="cap" cdata="&#8745;"/> <!--intersection = cap, U+2229 ISOtech -->
+		<entity name="cup" cdata="&#8746;"/> <!--union = cup, U+222A ISOtech -->
+		<entity name="int" cdata="&#8747;"/> <!--integral, U+222B ISOtech -->
+		<entity name="there4" cdata="&#8756;"/> <!--therefore, U+2234 ISOtech -->
+		<entity name="sim" cdata="&#8764;"/> <!--tilde operator = varies with = similar to, U+223C ISOtech -->
+		
+		<!-- tilde operator is NOT the same character as the tilde, U+007E,
+		     although the same glyph might be used to represent both  -->
+		     
+		<entity name="cong" cdata="&#8773;"/> <!--approximately equal to, U+2245 ISOtech -->
+		<entity name="asymp" cdata="&#8776;"/> <!--almost equal to = asymptotic to, U+2248 ISOamsr -->
+		<entity name="ne" cdata="&#8800;"/> <!--not equal to, U+2260 ISOtech -->
+		<entity name="equiv" cdata="&#8801;"/> <!--identical to, U+2261 ISOtech -->
+		<entity name="le" cdata="&#8804;"/> <!--less-than or equal to, U+2264 ISOtech -->
+		<entity name="ge" cdata="&#8805;"/> <!--greater-than or equal to, U+2265 ISOtech -->
+		<entity name="sub" cdata="&#8834;"/> <!--subset of, U+2282 ISOtech -->
+		<entity name="sup" cdata="&#8835;"/> <!--superset of, U+2283 ISOtech -->
+		
+		<!-- note that nsup, 'not a superset of, U+2283' is not covered by the Symbol 
+		     font encoding and is not included. Should it be, for symmetry?
+		     It is in ISOamsn  --> 
+		
+		<entity name="nsub" cdata="&#8836;"/> <!--not a subset of, U+2284 ISOamsn -->
+		<entity name="sube" cdata="&#8838;"/> <!--subset of or equal to, U+2286 ISOtech -->
+		<entity name="supe" cdata="&#8839;"/> <!--superset of or equal to, U+2287 ISOtech -->
+		<entity name="oplus" cdata="&#8853;"/> <!--circled plus = direct sum, U+2295 ISOamsb -->
+		<entity name="otimes" cdata="&#8855;"/> <!--circled times = vector product, U+2297 ISOamsb -->
+		<entity name="perp" cdata="&#8869;"/> <!--up tack = orthogonal to = perpendicular, U+22A5 ISOtech -->
+		<entity name="sdot" cdata="&#8901;"/> <!--dot operator, U+22C5 ISOamsb -->
+		<!-- dot operator is NOT the same character as U+00B7 middle dot -->
+		
+		<!-- Miscellaneous Technical -->
+		<entity name="lceil" cdata="&#8968;"/> <!--left ceiling = apl upstile, U+2308 ISOamsc  -->
+		<entity name="rceil" cdata="&#8969;"/> <!--right ceiling, U+2309 ISOamsc  -->
+		<entity name="lfloor" cdata="&#8970;"/> <!--left floor = apl downstile, U+230A ISOamsc  -->
+		<entity name="rfloor" cdata="&#8971;"/> <!--right floor, U+230B ISOamsc  -->
+		<entity name="lang" cdata="&#9001;"/> <!--left-pointing angle bracket = bra, U+2329 ISOtech -->
+		<!-- lang is NOT the same character as U+003C 'less than' 
+		     or U+2039 'single left-pointing angle quotation mark' -->
+		<entity name="rang" cdata="&#9002;"/> <!--right-pointing angle bracket = ket, U+232A ISOtech -->
+		<!-- rang is NOT the same character as U+003E 'greater than' or U+203A 'single right-pointing angle quotation mark' -->
+		
+		<!-- Geometric Shapes -->
+		<entity name="loz" cdata="&#9674;"/> <!--lozenge, U+25CA ISOpub -->
+		
+		<!-- Miscellaneous Symbols -->
+		<entity name="spades" cdata="&#9824;"/> <!--black spade suit, U+2660 ISOpub -->
+		<!-- black here seems to mean filled as opposed to hollow -->
+		<entity name="clubs" cdata="&#9827;"/> <!--black club suit = shamrock, U+2663 ISOpub -->
+		<entity name="hearts" cdata="&#9829;"/> <!--black heart suit = valentine, U+2665 ISOpub -->
+		<entity name="diams" cdata="&#9830;"/> <!--black diamond suit, U+2666 ISOpub -->
+		
+		<entity name="quot" cdata="&#34;"  /> <!--quotation mark = APL quote, U+0022 ISOnum -->
+		<!-- Latin Extended-A -->
+		<entity name="OElig" cdata="&#338;" /> <!--latin capital ligature OE, U+0152 ISOlat2 -->
+		<entity name="oelig" cdata="&#339;" /> <!--latin small ligature oe, U+0153 ISOlat2 -->
+		<!-- ligature is a misnomer, this is a separate character in some languages -->
+		<entity name="Scaron" cdata="&#352;" /> <!--latin capital letter S with caron, U+0160 ISOlat2 -->
+		<entity name="scaron" cdata="&#353;" /> <!--latin small letter s with caron, U+0161 ISOlat2 -->
+		<entity name="Yuml" cdata="&#376;" /> <!--latin capital letter Y with diaeresis, U+0178 ISOlat2 -->
+		
+		<!-- Spacing Modifier Letters -->
+		<entity name="circ" cdata="&#710;" /> <!--modifier letter circumflex accent, U+02C6 ISOpub -->
+		<entity name="tilde" cdata="&#732;" /> <!--small tilde, U+02DC ISOdia -->
+		
+		<!-- General Punctuation -->
+		<entity name="ensp" cdata="&#8194;"/> <!--en space, U+2002 ISOpub -->
+		<entity name="emsp" cdata="&#8195;"/> <!--em space, U+2003 ISOpub -->
+		<entity name="thinsp" cdata="&#8201;"/> <!--thin space, U+2009 ISOpub -->
+		<entity name="zwnj" cdata="&#8204;"/> <!--zero width non-joiner, U+200C NEW RFC 2070 -->
+		<entity name="zwj" cdata="&#8205;"/> <!--zero width joiner, U+200D NEW RFC 2070 -->
+		<entity name="lrm" cdata="&#8206;"/> <!--left-to-right mark, U+200E NEW RFC 2070 -->
+		<entity name="rlm" cdata="&#8207;"/> <!--right-to-left mark, U+200F NEW RFC 2070 -->
+		<entity name="ndash" cdata="&#8211;"/> <!--en dash, U+2013 ISOpub -->
+		<entity name="mdash" cdata="&#8212;"/> <!--em dash, U+2014 ISOpub -->
+		<entity name="lsquo" cdata="&#8216;"/> <!--left single quotation mark, U+2018 ISOnum -->
+		<entity name="rsquo" cdata="&#8217;"/> <!--right single quotation mark, U+2019 ISOnum -->
+		<entity name="sbquo" cdata="&#8218;"/> <!--single low-9 quotation mark, U+201A NEW -->
+		<entity name="ldquo" cdata="&#8220;"/> <!--left double quotation mark, U+201C ISOnum -->
+		<entity name="rdquo" cdata="&#8221;"/> <!--right double quotation mark, U+201D ISOnum -->
+		<entity name="bdquo" cdata="&#8222;"/> <!--double low-9 quotation mark, U+201E NEW -->
+		<entity name="dagger" cdata="&#8224;"/> <!--dagger, U+2020 ISOpub -->
+		<entity name="Dagger" cdata="&#8225;"/> <!--double dagger, U+2021 ISOpub -->
+		<entity name="permil" cdata="&#8240;"/> <!--per mille sign, U+2030 ISOtech -->
+		<entity name="lsaquo" cdata="&#8249;"/> <!--single left-pointing angle quotation mark, U+2039 ISO proposed -->
+		<!-- lsaquo is proposed but not yet ISO standardized -->
+		<entity name="rsaquo" cdata="&#8250;"/> <!--single right-pointing angle quotation mark, U+203A ISO proposed -->
+		<!-- rsaquo is proposed but not yet ISO standardized -->
+		<entity name="euro" cdata="&#8364;" /> <!--euro sign, U+20AC NEW -->
+	</html-entities>
+
+</anti-samy-rules>
diff --git a/src/test/resources/esapi/fbac-policies/.svn/all-wcprops b/src/test/resources/esapi/fbac-policies/.svn/all-wcprops
new file mode 100644
index 0000000..f8eecf2
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 74
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/fbac-policies
+END
+DataAccessRules.txt
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/fbac-policies/DataAccessRules.txt
+END
+FileAccessRules.txt
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/fbac-policies/FileAccessRules.txt
+END
+ServiceAccessRules.txt
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/fbac-policies/ServiceAccessRules.txt
+END
+URLAccessRules.txt
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/fbac-policies/URLAccessRules.txt
+END
+FunctionAccessRules.txt
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/fbac-policies/FunctionAccessRules.txt
+END
diff --git a/src/test/resources/esapi/fbac-policies/.svn/entries b/src/test/resources/esapi/fbac-policies/.svn/entries
new file mode 100644
index 0000000..436d7b5
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/.svn/entries
@@ -0,0 +1,198 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/resources/esapi/fbac-policies
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+DataAccessRules.txt
+file
+
+
+
+
+2014-02-18T16:19:52.489957Z
+5411f79258256dcc6b781b711412051f
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+489
+

+FileAccessRules.txt
+file
+
+
+
+
+2014-02-18T16:19:52.489957Z
+119f13387381b443bea4900bd6997d3b
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+400
+

+ServiceAccessRules.txt
+file
+
+
+
+
+2014-02-18T16:19:52.489957Z
+021906345b425dc568946301cc0b55e9
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+384
+

+URLAccessRules.txt
+file
+
+
+
+
+2014-02-18T16:19:52.489957Z
+26b12cc4c3d6827a674465681c45f49e
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+649
+

+FunctionAccessRules.txt
+file
+
+
+
+
+2014-02-18T16:19:52.489957Z
+ba5edb8af172a71da2b118ab2a9d80b5
+2009-05-28T04:23:26.589881Z
+526
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+405
+

diff --git a/src/test/resources/esapi/fbac-policies/.svn/text-base/DataAccessRules.txt.svn-base b/src/test/resources/esapi/fbac-policies/.svn/text-base/DataAccessRules.txt.svn-base
new file mode 100644
index 0000000..f21e8c8
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/.svn/text-base/DataAccessRules.txt.svn-base
@@ -0,0 +1,9 @@
+# Data Access Rules
+#
+java.io.BufferedReader             | any         | read        | default deny
+java.lang.String                   | User        | read, write |
+java.lang.Math                     | Admin       | read, write |
+java.util.ArrayList                | Admin       | read        |
+java.awt.event.MouseWheelEvent     | Admin, User | write, read |
+java.util.Date                     | User        | write       |
+java.util.Random                   | User, Admin | read        |
\ No newline at end of file
diff --git a/src/test/resources/esapi/fbac-policies/.svn/text-base/FileAccessRules.txt.svn-base b/src/test/resources/esapi/fbac-policies/.svn/text-base/FileAccessRules.txt.svn-base
new file mode 100644
index 0000000..cf83671
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/.svn/text-base/FileAccessRules.txt.svn-base
@@ -0,0 +1,12 @@
+# Data Access Rules
+#
+
+/                | any         | deny  | default deny
+/Dir/File1       | User        | allow |
+/Dir/File2       | Admin       | allow |
+/Dir/File8       | M at ry        | allow |
+/Dir/File3		 | Admin, User | allow |
+/Dir/File4       | User, Admin | allow |
+/Dir/File5       | Admin, User | deny  |
+/Dir/File6       | User, Admin | deny  |
+/Dir/File7       | J3ff        | allow |
\ No newline at end of file
diff --git a/src/test/resources/esapi/fbac-policies/.svn/text-base/FunctionAccessRules.txt.svn-base b/src/test/resources/esapi/fbac-policies/.svn/text-base/FunctionAccessRules.txt.svn-base
new file mode 100644
index 0000000..de0d81b
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/.svn/text-base/FunctionAccessRules.txt.svn-base
@@ -0,0 +1,13 @@
+# Function Access Rules
+#
+
+/                | any      | deny  | default deny
+/FunctionA       | User     | allow |
+/FunctionAdeny   | User     | deny  |
+/FunctionB       | Admin    | allow |
+/FunctionBdeny   | Admin    | deny  |
+/FunctionC       | Admin,User    | allow |
+/FunctionCdeny   | Admin, User    | deny  |
+/FunctionD       | User, Admin    | allow |
+/FunctionDdeny   | User,Admin    | deny  |
+
diff --git a/src/test/resources/esapi/fbac-policies/.svn/text-base/ServiceAccessRules.txt.svn-base b/src/test/resources/esapi/fbac-policies/.svn/text-base/ServiceAccessRules.txt.svn-base
new file mode 100644
index 0000000..a6170f6
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/.svn/text-base/ServiceAccessRules.txt.svn-base
@@ -0,0 +1,12 @@
+# Service Access Rules
+#
+
+/                  | any         | deny  | default deny
+/services          | any         | deny  |
+/services/ServiceA | User        | allow |
+/services/ServiceB | Admin       | allow |
+/services/ServiceC | Admin, User | allow |
+/services/ServiceD | User, Admin | allow |
+/services/ServiceE | Admin, User | deny  |
+/services/ServiceF | User, Admin | deny  |
+
diff --git a/src/test/resources/esapi/fbac-policies/.svn/text-base/URLAccessRules.txt.svn-base b/src/test/resources/esapi/fbac-policies/.svn/text-base/URLAccessRules.txt.svn-base
new file mode 100644
index 0000000..760cd57
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/.svn/text-base/URLAccessRules.txt.svn-base
@@ -0,0 +1,18 @@
+# URL Access Rules
+#
+
+/                | any         | deny  | default deny
+/nobody          | any         | deny  |
+/ESAPITest       | any         | allow |
+/ESAPITest/admin | admin       | allow |
+/AspectUpload/*  | any         | allow |
+/test/admin      | admin       | allow |
+/test/user       | user        | allow |
+/test/none       | any         | deny  |
+/test/all        | any         | allow |
+*.gif            | any         | allow |
+*.exe            | any         | deny  |
+/test/profile    | User, Admin | allow |
+/test/moderator  | User, Admin | deny  |
+/upload          | Admin, User | deny  |
+*.png            | Admin, User | allow |
\ No newline at end of file
diff --git a/src/test/resources/esapi/fbac-policies/DataAccessRules.txt b/src/test/resources/esapi/fbac-policies/DataAccessRules.txt
new file mode 100644
index 0000000..f21e8c8
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/DataAccessRules.txt
@@ -0,0 +1,9 @@
+# Data Access Rules
+#
+java.io.BufferedReader             | any         | read        | default deny
+java.lang.String                   | User        | read, write |
+java.lang.Math                     | Admin       | read, write |
+java.util.ArrayList                | Admin       | read        |
+java.awt.event.MouseWheelEvent     | Admin, User | write, read |
+java.util.Date                     | User        | write       |
+java.util.Random                   | User, Admin | read        |
\ No newline at end of file
diff --git a/src/test/resources/esapi/fbac-policies/FileAccessRules.txt b/src/test/resources/esapi/fbac-policies/FileAccessRules.txt
new file mode 100644
index 0000000..cf83671
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/FileAccessRules.txt
@@ -0,0 +1,12 @@
+# Data Access Rules
+#
+
+/                | any         | deny  | default deny
+/Dir/File1       | User        | allow |
+/Dir/File2       | Admin       | allow |
+/Dir/File8       | M at ry        | allow |
+/Dir/File3		 | Admin, User | allow |
+/Dir/File4       | User, Admin | allow |
+/Dir/File5       | Admin, User | deny  |
+/Dir/File6       | User, Admin | deny  |
+/Dir/File7       | J3ff        | allow |
\ No newline at end of file
diff --git a/src/test/resources/esapi/fbac-policies/FunctionAccessRules.txt b/src/test/resources/esapi/fbac-policies/FunctionAccessRules.txt
new file mode 100644
index 0000000..de0d81b
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/FunctionAccessRules.txt
@@ -0,0 +1,13 @@
+# Function Access Rules
+#
+
+/                | any      | deny  | default deny
+/FunctionA       | User     | allow |
+/FunctionAdeny   | User     | deny  |
+/FunctionB       | Admin    | allow |
+/FunctionBdeny   | Admin    | deny  |
+/FunctionC       | Admin,User    | allow |
+/FunctionCdeny   | Admin, User    | deny  |
+/FunctionD       | User, Admin    | allow |
+/FunctionDdeny   | User,Admin    | deny  |
+
diff --git a/src/test/resources/esapi/fbac-policies/ServiceAccessRules.txt b/src/test/resources/esapi/fbac-policies/ServiceAccessRules.txt
new file mode 100644
index 0000000..a6170f6
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/ServiceAccessRules.txt
@@ -0,0 +1,12 @@
+# Service Access Rules
+#
+
+/                  | any         | deny  | default deny
+/services          | any         | deny  |
+/services/ServiceA | User        | allow |
+/services/ServiceB | Admin       | allow |
+/services/ServiceC | Admin, User | allow |
+/services/ServiceD | User, Admin | allow |
+/services/ServiceE | Admin, User | deny  |
+/services/ServiceF | User, Admin | deny  |
+
diff --git a/src/test/resources/esapi/fbac-policies/URLAccessRules.txt b/src/test/resources/esapi/fbac-policies/URLAccessRules.txt
new file mode 100644
index 0000000..760cd57
--- /dev/null
+++ b/src/test/resources/esapi/fbac-policies/URLAccessRules.txt
@@ -0,0 +1,18 @@
+# URL Access Rules
+#
+
+/                | any         | deny  | default deny
+/nobody          | any         | deny  |
+/ESAPITest       | any         | allow |
+/ESAPITest/admin | admin       | allow |
+/AspectUpload/*  | any         | allow |
+/test/admin      | admin       | allow |
+/test/user       | user        | allow |
+/test/none       | any         | deny  |
+/test/all        | any         | allow |
+*.gif            | any         | allow |
+*.exe            | any         | deny  |
+/test/profile    | User, Admin | allow |
+/test/moderator  | User, Admin | deny  |
+/upload          | Admin, User | deny  |
+*.png            | Admin, User | allow |
\ No newline at end of file
diff --git a/src/test/resources/esapi/users.txt b/src/test/resources/esapi/users.txt
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/esapi/validation.properties b/src/test/resources/esapi/validation.properties
new file mode 100644
index 0000000..18e037f
--- /dev/null
+++ b/src/test/resources/esapi/validation.properties
@@ -0,0 +1,29 @@
+# The ESAPI validator does many security checks on input, such as canonicalization
+# and whitelist validation. Note that all of these validation rules are applied *after*
+# canonicalization. Double-encoded characters (even with different encodings involved,
+# are never allowed.
+#
+# To use:
+#
+# First set up a pattern below. You can choose any name you want, prefixed by the word
+# "Validation." For example:
+#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+# 
+# Then you can validate in your code against the pattern like this:
+#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
+# Where maxLength and allowNull are set for you needs, respectively.
+#
+# But note, when you use boolean variants of validation functions, you lose critical 
+# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
+# and use the returned user input which is in canonical form. Consider the following:
+#  
+# try {
+#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
+#
+Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
+Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
+Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
+Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
+Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
+
diff --git a/src/test/resources/esapi/waf-policies/.svn/all-wcprops b/src/test/resources/esapi/waf-policies/.svn/all-wcprops
new file mode 100644
index 0000000..c5be345
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/all-wcprops
@@ -0,0 +1,107 @@
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies
+END
+bean-shell-rule.bsh
+K 25
+svn:wc:ra_dav:version-url
+V 93
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh
+END
+restrict-source-ip-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml
+END
+detect-outbound-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml
+END
+restrict-user-agent-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml
+END
+add-httponly-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/add-httponly-policy.xml
+END
+replace-outbound-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 101
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml
+END
+restrict-extension-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml
+END
+bean-shell-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/bean-shell-policy.xml
+END
+add-secure-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/add-secure-policy.xml
+END
+restrict-method-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/restrict-method-policy.xml
+END
+add-header-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/add-header-policy.xml
+END
+authentication-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 99
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/authentication-policy.xml
+END
+enforce-https-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/enforce-https-policy.xml
+END
+must-match-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/must-match-policy.xml
+END
+virtual-patch-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml
+END
+restrict-content-type-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml
+END
+dynamic-insertion-policy.xml
+K 25
+svn:wc:ra_dav:version-url
+V 102
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml
+END
diff --git a/src/test/resources/esapi/waf-policies/.svn/entries b/src/test/resources/esapi/waf-policies/.svn/entries
new file mode 100644
index 0000000..cf308c3
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/entries
@@ -0,0 +1,606 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/resources/esapi/waf-policies
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-03-23T14:39:22.287535Z
+1741
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+dynamic-insertion-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+b32463cce0d1be245de1d47201d1b6a1
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+631
+

+bean-shell-rule.bsh
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+c79bade58fc528b189e4cdc5a1a7d385
+2009-09-18T17:08:45.533862Z
+675
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+123
+

+restrict-source-ip-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+2ff4f060683f25cffc45cee91aec8600
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+598
+

+detect-outbound-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+1e4da014d4f1f4b3f40008797088e8ae
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+662
+

+restrict-user-agent-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+34d64e18254f6cb9af74b668e63c174b
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+695
+

+add-httponly-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+82bc5d19bb17b3343aae63e47f8416a0
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+632
+

+replace-outbound-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+b32463cce0d1be245de1d47201d1b6a1
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+631
+

+restrict-extension-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+6828cc33d0100ce1b8126f54cf174bb4
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+572
+

+bean-shell-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+9a0da18dfe8ebe34a219e39a0a2c7f86
+2011-03-23T14:39:22.287535Z
+1741
+chrisisbeef
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+553
+

+add-secure-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+bd19de30cd91a27d31523079c40db308
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+627
+

+restrict-method-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+351bfc3d8ed41387ccff7f1225e2b718
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+568
+

+add-header-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+4bd7604af016a4a51b22bed3ad0fa5c3
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+721
+

+authentication-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+7769322f7f92994216757d0a7d86a3fb
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+841
+

+enforce-https-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+6f0371a3e8f38aa91fd783c22f8024a6
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+703
+

+must-match-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+bf74ebd18e27431c6955709b9235a61b
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+906
+

+virtual-patch-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+05b950be72e41b9a168b4aeb1376f42a
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+746
+

+restrict-content-type-policy.xml
+file
+
+
+
+
+2014-02-18T16:19:52.497957Z
+e633612947422491bf67c9a72fc95b40
+2009-10-15T22:52:53.680904Z
+691
+arshan.dabirsiaghi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+715
+

diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/add-header-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/add-header-policy.xml.svn-base
new file mode 100644
index 0000000..b2d8efc
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/add-header-policy.xml.svn-base
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-header rule.
+
+	Protection #1: Any response for a resource will contain a header "FOO" with
+	               a value "BAR".
+
+	Exception #1:  Any request for /marketing/* will not have any such header
+	               returned.
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-header name="FOO" value="BAR" path="/.*">
+			<path-exception type="regex">/marketing/.*</path-exception>
+		</add-header>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/add-httponly-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/add-httponly-policy.xml.svn-base
new file mode 100644
index 0000000..1232f59
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/add-httponly-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-http-flag rule.
+
+	Protection #1: Any cookie set by the application will have the HttpOnly flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<add-http-only-flag>
+			<cookie name=".*"/>
+		</add-http-only-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/add-secure-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/add-secure-policy.xml.svn-base
new file mode 100644
index 0000000..12298a4
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/add-secure-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-secure-flag rule.
+
+	Protection #1: Any cookie set by the application will have the secure flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-secure-flag>
+			<cookie name=".*"/>
+		</add-secure-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/authentication-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/authentication-policy.xml.svn-base
new file mode 100644
index 0000000..1c1a485
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/authentication-policy.xml.svn-base
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an authentication rule.
+
+	Authentication applies to: /.*
+	Session variable whose existence implies authentication: sessionUserObjKey
+	Static exception:   <path-exception>/index.html</path-exception>
+	Pattern exception:  <path-exception type="regex">/images/.*</path-exception>
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>>
+
+	<!--
+		Set authentication rules by path.
+	-->
+	<authentication-rules path="/.*"  key="sessionUserObjKey" >
+		<path-exception>/index.html</path-exception>
+		<path-exception type="regex">/images/.*</path-exception>
+	</authentication-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/bean-shell-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/bean-shell-policy.xml.svn-base
new file mode 100644
index 0000000..5d2f927
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/bean-shell-policy.xml.svn-base
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies different custom bean shell rules.
+	
+	
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<!--
+		Import the rules.
+	-->
+	<bean-shell-rules>	
+		<bean-shell-script 
+			id="example1" 
+			file="waf-policies/bean-shell-rule.bsh"
+			stage="before-request-body"/>
+	</bean-shell-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/bean-shell-rule.bsh.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/bean-shell-rule.bsh.svn-base
new file mode 100644
index 0000000..2a1f423
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/bean-shell-rule.bsh.svn-base
@@ -0,0 +1,5 @@
+import org.owasp.esapi.waf.actions.*;
+
+session.setAttribute("simple_waf_test", "true");
+
+action = new RedirectAction();
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/detect-outbound-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/detect-outbound-policy.xml.svn-base
new file mode 100644
index 0000000..8312be9
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/detect-outbound-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a detect-content rule.
+
+	Protection #1: The rule should fire whenever the string "2008" appears in
+	               a response body.
+	Exception #1:  The rule should -not- fire when the content type is anything
+	               but text/*.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<detect-content content-type=".*text/.*" pattern=".*2008.*" />
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/dynamic-insertion-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/dynamic-insertion-policy.xml.svn-base
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/dynamic-insertion-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/enforce-https-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/enforce-https-policy.xml.svn-base
new file mode 100644
index 0000000..ab123b7
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/enforce-https-policy.xml.svn-base
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an enforce-https rule.
+
+	Protection #1: a request of any kind for /foo should be redirected to secure request with 302.
+	Exception #1: Static path /index.html
+	Exception #2: Pattern path /images/.*
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<enforce-https path="/.*">
+			<path-exception>/index.html</path-exception>
+			<path-exception type="regex">/images/.*</path-exception>
+		</enforce-https>
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/must-match-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/must-match-policy.xml.svn-base
new file mode 100644
index 0000000..721fa4c
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/must-match-policy.xml.svn-base
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a must-match rule.
+
+	Protection #1:
+	Protection applies to: /admin/.*
+	Header name needed to access: x-roles
+	Header value needs to *contain* the string: admin
+
+	Protection #2:
+	Protection applies to: /superadmin/.*
+	Header name needed to access: x-roles
+	Header value needs to *equal* the string: superadmin
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<must-match path="^/admin/.*" variable="request.header.x-roles"
+			operator="contains" value="admin" />
+		<must-match path="^/superadmin/.*" variable="request.header.x-roles"
+			operator="equals" value="superadmin" />
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/replace-outbound-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/replace-outbound-policy.xml.svn-base
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/replace-outbound-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-content-type-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-content-type-policy.xml.svn-base
new file mode 100644
index 0000000..a9b8a4a
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-content-type-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-content-type rule.
+
+	Protection #1: any request with a content-type containing the word 'multipart' will be rejected
+	Exception  #1: requests for /fileupload.jsp are allowed to have 'multipart' in content-type
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-content-type deny=".*multipart.*">
+			<path-exception type="regex">/fileupload.jsp</path-exception>
+		</restrict-content-type>
+
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-extension-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-extension-policy.xml.svn-base
new file mode 100644
index 0000000..fe1dbe1
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-extension-policy.xml.svn-base
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny=".*\.log$" />
+		<restrict-extension allow=".*\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-method-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-method-policy.xml.svn-base
new file mode 100644
index 0000000..5a2337c
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-method-policy.xml.svn-base
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny="\.log$" />
+		<restrict-extension allow="\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-source-ip-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-source-ip-policy.xml.svn-base
new file mode 100644
index 0000000..d23d87a
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-source-ip-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-source-ip rule.
+
+	The restriction applies to: /admin/.*
+	Pattern exception:  (192\.168\.1\\..*|127.0.0.1)
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<restrict-source-ip
+			type="regex"
+			ip-regex="(192\.168\.1\\..*|127.0.0.1)">/admin/.*</restrict-source-ip>
+
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-user-agent-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-user-agent-policy.xml.svn-base
new file mode 100644
index 0000000..7cdebd5
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/restrict-user-agent-policy.xml.svn-base
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-user-agent rule.
+
+	Protection #1: any request with a user agent containing the word 'GoogleBot' will be rejected
+	Exception  #1: requests for /index.html are allowed to have 'GoogleBot' in user agent
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-user-agent deny=".*GoogleBot.*">
+			<path-exception type="regex">/index.html</path-exception>
+		</restrict-user-agent>
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/.svn/text-base/virtual-patch-policy.xml.svn-base b/src/test/resources/esapi/waf-policies/.svn/text-base/virtual-patch-policy.xml.svn-base
new file mode 100644
index 0000000..61c93fa
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/.svn/text-base/virtual-patch-policy.xml.svn-base
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a virtual-patch rule.
+
+	Protection #1: Any request whose URI is /foo.jsp (despite content-type) will have
+	               the 'bar' parameter checked to see if its alphanumeric. If a parameter
+				   fails validation, the message "zomg attax" will be logged.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<virtual-patches>
+		<virtual-patch id="1234" path="/foo.jsp" variable="request.parameters.bar"
+			pattern="[0-9a-zA-Z]" message="zomg attax" />
+	</virtual-patches>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/add-header-policy.xml b/src/test/resources/esapi/waf-policies/add-header-policy.xml
new file mode 100644
index 0000000..b2d8efc
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/add-header-policy.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-header rule.
+
+	Protection #1: Any response for a resource will contain a header "FOO" with
+	               a value "BAR".
+
+	Exception #1:  Any request for /marketing/* will not have any such header
+	               returned.
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-header name="FOO" value="BAR" path="/.*">
+			<path-exception type="regex">/marketing/.*</path-exception>
+		</add-header>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/add-httponly-policy.xml b/src/test/resources/esapi/waf-policies/add-httponly-policy.xml
new file mode 100644
index 0000000..1232f59
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/add-httponly-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-http-flag rule.
+
+	Protection #1: Any cookie set by the application will have the HttpOnly flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<add-http-only-flag>
+			<cookie name=".*"/>
+		</add-http-only-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/add-secure-policy.xml b/src/test/resources/esapi/waf-policies/add-secure-policy.xml
new file mode 100644
index 0000000..12298a4
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/add-secure-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an add-secure-flag rule.
+
+	Protection #1: Any cookie set by the application will have the secure flag set. This
+	               should apply to the application session ID as well as any custom cookies.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+	
+	<outbound-rules>
+		<add-secure-flag>
+			<cookie name=".*"/>
+		</add-secure-flag>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/authentication-policy.xml b/src/test/resources/esapi/waf-policies/authentication-policy.xml
new file mode 100644
index 0000000..1c1a485
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/authentication-policy.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an authentication rule.
+
+	Authentication applies to: /.*
+	Session variable whose existence implies authentication: sessionUserObjKey
+	Static exception:   <path-exception>/index.html</path-exception>
+	Pattern exception:  <path-exception type="regex">/images/.*</path-exception>
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>>
+
+	<!--
+		Set authentication rules by path.
+	-->
+	<authentication-rules path="/.*"  key="sessionUserObjKey" >
+		<path-exception>/index.html</path-exception>
+		<path-exception type="regex">/images/.*</path-exception>
+	</authentication-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/bean-shell-policy.xml b/src/test/resources/esapi/waf-policies/bean-shell-policy.xml
new file mode 100644
index 0000000..5d2f927
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/bean-shell-policy.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies different custom bean shell rules.
+	
+	
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<!--
+		Import the rules.
+	-->
+	<bean-shell-rules>	
+		<bean-shell-script 
+			id="example1" 
+			file="waf-policies/bean-shell-rule.bsh"
+			stage="before-request-body"/>
+	</bean-shell-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh b/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh
new file mode 100644
index 0000000..2a1f423
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh
@@ -0,0 +1,5 @@
+import org.owasp.esapi.waf.actions.*;
+
+session.setAttribute("simple_waf_test", "true");
+
+action = new RedirectAction();
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml b/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml
new file mode 100644
index 0000000..8312be9
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a detect-content rule.
+
+	Protection #1: The rule should fire whenever the string "2008" appears in
+	               a response body.
+	Exception #1:  The rule should -not- fire when the content type is anything
+	               but text/*.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<detect-content content-type=".*text/.*" pattern=".*2008.*" />
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml b/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/enforce-https-policy.xml b/src/test/resources/esapi/waf-policies/enforce-https-policy.xml
new file mode 100644
index 0000000..ab123b7
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/enforce-https-policy.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies an enforce-https rule.
+
+	Protection #1: a request of any kind for /foo should be redirected to secure request with 302.
+	Exception #1: Static path /index.html
+	Exception #2: Pattern path /images/.*
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<enforce-https path="/.*">
+			<path-exception>/index.html</path-exception>
+			<path-exception type="regex">/images/.*</path-exception>
+		</enforce-https>
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/must-match-policy.xml b/src/test/resources/esapi/waf-policies/must-match-policy.xml
new file mode 100644
index 0000000..721fa4c
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/must-match-policy.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a must-match rule.
+
+	Protection #1:
+	Protection applies to: /admin/.*
+	Header name needed to access: x-roles
+	Header value needs to *contain* the string: admin
+
+	Protection #2:
+	Protection applies to: /superadmin/.*
+	Header name needed to access: x-roles
+	Header value needs to *equal* the string: superadmin
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<must-match path="^/admin/.*" variable="request.header.x-roles"
+			operator="contains" value="admin" />
+		<must-match path="^/superadmin/.*" variable="request.header.x-roles"
+			operator="equals" value="superadmin" />
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml b/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml
new file mode 100644
index 0000000..9c1aa40
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a dynamic-insertion rule.
+
+	Protection #1: All instances of "</body>" in the response body will be replaced by
+	               "this is a test".
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<outbound-rules>
+		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+	</outbound-rules>
+	
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml b/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml
new file mode 100644
index 0000000..a9b8a4a
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-content-type rule.
+
+	Protection #1: any request with a content-type containing the word 'multipart' will be rejected
+	Exception  #1: requests for /fileupload.jsp are allowed to have 'multipart' in content-type
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-content-type deny=".*multipart.*">
+			<path-exception type="regex">/fileupload.jsp</path-exception>
+		</restrict-content-type>
+
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml b/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml
new file mode 100644
index 0000000..fe1dbe1
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny=".*\.log$" />
+		<restrict-extension allow=".*\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/restrict-method-policy.xml b/src/test/resources/esapi/waf-policies/restrict-method-policy.xml
new file mode 100644
index 0000000..5a2337c
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/restrict-method-policy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-extension rule.
+
+	Protection #1: any URI ending with .log will be rejected
+	Protection #2: any URI ending with .jsp will be allowed
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<url-rules>
+		<restrict-extension deny="\.log$" />
+		<restrict-extension allow="\.jsp$" />
+	</url-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml b/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml
new file mode 100644
index 0000000..d23d87a
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-source-ip rule.
+
+	The restriction applies to: /admin/.*
+	Pattern exception:  (192\.168\.1\\..*|127.0.0.1)
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<authorization-rules>
+		<restrict-source-ip
+			type="regex"
+			ip-regex="(192\.168\.1\\..*|127.0.0.1)">/admin/.*</restrict-source-ip>
+
+	</authorization-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml b/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml
new file mode 100644
index 0000000..7cdebd5
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a restrict-user-agent rule.
+
+	Protection #1: any request with a user agent containing the word 'GoogleBot' will be rejected
+	Exception  #1: requests for /index.html are allowed to have 'GoogleBot' in user agent
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<header-rules>
+		<restrict-user-agent deny=".*GoogleBot.*">
+			<path-exception type="regex">/index.html</path-exception>
+		</restrict-user-agent>
+	</header-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml b/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml
new file mode 100644
index 0000000..61c93fa
--- /dev/null
+++ b/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+	This test file exemplifies a virtual-patch rule.
+
+	Protection #1: Any request whose URI is /foo.jsp (despite content-type) will have
+	               the 'bar' parameter checked to see if its alphanumeric. If a parameter
+				   fails validation, the message "zomg attax" will be logged.
+
+	-->
+
+<policy>
+
+	<settings>
+		<mode>redirect</mode>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+	<virtual-patches>
+		<virtual-patch id="1234" path="/foo.jsp" variable="request.parameters.bar"
+			pattern="[0-9a-zA-Z]" message="zomg attax" />
+	</virtual-patches>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/esapi/waf-policy.xml b/src/test/resources/esapi/waf-policy.xml
new file mode 100644
index 0000000..05d75b2
--- /dev/null
+++ b/src/test/resources/esapi/waf-policy.xml
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+	<!--
+		This file defines a set of "web application firewall" rules that
+		defend a web application against certain types of attacks. These rules
+		are loaded and enforced by a filter that sits in front of a web
+		application and has access to both request and response on both the
+		way in and the way out.
+	-->
+
+<policy>
+
+
+	<!--
+		Setup some simple aliases to use elsewhere in the WAF policy. Alias
+		types are: string (default), regex. String is a literal string, regex
+		is a pattern.
+	-->
+	<aliases>
+		<alias name="INPUT_VALIDATION_ERROR">/security/input.jsp</alias>
+		<alias name="ADMIN_PATH" type="regex">^/admin/.*</alias>
+	</aliases>
+
+
+
+	<!--
+		Set the overall WAF mode of operation. The mode can be either "block"
+		or "log". "block" mode will send all errors to the web page defined in
+		the error-handling configuration. "log" mode will not change HTTP
+		requests at all, but will simply log errors.
+	-->
+	<settings>
+		<mode>redirect</mode>
+		<session-cookie-name>JSESSIONID</session-cookie-name>
+		<error-handling>
+			<default-redirect-page>/security/error.jsp</default-redirect-page>
+			<block-status>403</block-status>
+		</error-handling>
+	</settings>
+
+
+
+	<!--
+		Set authentication rules by path.
+	-->
+	<authentication-rules path="/.*"  key="ESAPIUserSessionKey" >
+		<path-exception>/</path-exception>
+		<path-exception>/index.html</path-exception>
+		<path-exception>/login.jsp</path-exception>
+		<path-exception>/index.jsp</path-exception>
+		<path-exception type="regex">/images/.*</path-exception>
+		<path-exception type="regex">/css/.*</path-exception>
+		<path-exception type="regex">/help/.*</path-exception>
+	</authentication-rules>
+
+
+	<bean-shell-rules>	
+		<!--  	<bean-shell-script 
+			id="example1" 
+			file="waf-policies/bean-shell-rule.bsh" 
+			stage="before-request-body"/>
+		-->
+	</bean-shell-rules>
+	
+	<!--
+		Set authorization rules by path. Types are: regex Operators for
+		must-match are: contains,equals,inList,exists
+	-->
+	<authorization-rules>
+		<restrict-source-ip type="regex"
+			ip-regex="(192\.168\.1\..*|127.0.0.1)">/admin/.*</restrict-source-ip>
+		<must-match path="^/admin/.*" variable="request.headers.x-roles"
+			operator="contains" value="admin" />
+	</authorization-rules>
+
+
+
+	<!--
+		Set rules for incoming URLs.
+	-->
+	<url-rules>
+		<restrict-extension deny=".jpg" />
+
+		<restrict-method deny="GET" path=".*\.do$" />
+		<restrict-method allow="^(GET|POST|TRACE)$" />
+
+		<enforce-https path="/.*">
+			<path-exception>/index.html</path-exception>
+			<path-exception>/index.jsp</path-exception>
+			<path-exception type="regex">/images/.*</path-exception>
+			<path-exception type="regex">/css/.*</path-exception>
+			<path-exception type="regex">/help/.*</path-exception>
+		</enforce-https>
+	</url-rules>
+
+
+
+	<!--
+		Set rules for incoming headers and parameters.
+	-->
+	<header-rules>
+		<restrict-content-type deny=".*multipart.*" />
+		<restrict-content-type allow="text/plain" />
+		<restrict-content-type allow="x-www-form-urlencoded" />
+		<restrict-user-agent deny=".*GoogleBot.*" />
+		<restrict-user-agent allow=".*" />
+	</header-rules>
+
+
+	<!--
+		Set virtual patches to match specific vulnerability patterns.
+	-->
+	<virtual-patches>
+		<virtual-patch id="1234" path="/foo.jsp" variable="request.parameters.bar"
+			pattern="[0-9a-zA-Z]" message="zomg attax" />
+	</virtual-patches>
+
+
+
+	<!-- Set rules for outbound headers and data -->
+
+	<outbound-rules>
+
+		<add-header name="FOO" value="BAR" path="/.*">
+			<path-exception type="regex">/foobar/.*</path-exception>
+		</add-header>
+
+		<add-http-only-flag>
+			<cookie name=".*" />
+		</add-http-only-flag>
+
+		<add-secure-flag>
+			<cookie name=".*" />
+		</add-secure-flag>
+
+ 		<dynamic-insertion pattern="</body>">
+			<replacement><![CDATA[this is a test]]></replacement>
+		</dynamic-insertion>
+
+		<dynamic-insertion
+			pattern="(<input.*)type\s+=\s+"+hidden"+(.*/>)">
+			<replacement>\1\2</replacement>
+		</dynamic-insertion>
+
+		<detect-content content-type=".*text/.*" pattern=".*2008.*" />
+
+	</outbound-rules>
+
+</policy>
\ No newline at end of file
diff --git a/src/test/resources/log4j.dtd b/src/test/resources/log4j.dtd
new file mode 100644
index 0000000..1aabd96
--- /dev/null
+++ b/src/test/resources/log4j.dtd
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Authors: Chris Taylor, Ceki Gulcu. -->
+
+<!-- Version: 1.2 -->
+
+<!-- A configuration element consists of optional renderer
+elements,appender elements, categories and an optional root
+element. -->
+
+<!ELEMENT log4j:configuration (renderer*, appender*,plugin*, (category|logger)*,root?,
+                               (categoryFactory|loggerFactory)?)>
+
+<!-- The "threshold" attribute takes a level value below which -->
+<!-- all logging statements are disabled. -->
+
+<!-- Setting the "debug" enable the printing of internal log4j logging   -->
+<!-- statements.                                                         -->
+
+<!-- By default, debug attribute is "null", meaning that we not do touch -->
+<!-- internal log4j logging settings. The "null" value for the threshold -->
+<!-- attribute can be misleading. The threshold field of a repository	 -->
+<!-- cannot be set to null. The "null" value for the threshold attribute -->
+<!-- simply means don't touch the threshold field, the threshold field   --> 
+<!-- keeps its old value.                                                -->
+     
+<!ATTLIST log4j:configuration
+  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  threshold                (all|trace|debug|info|warn|error|fatal|off|null) "null"
+  debug                    (true|false|null)  "null"
+  reset                    (true|false) "false"
+>
+
+<!-- renderer elements allow the user to customize the conversion of  -->
+<!-- message objects to String.                                       -->
+
+<!ELEMENT renderer EMPTY>
+<!ATTLIST renderer
+  renderedClass  CDATA #REQUIRED
+  renderingClass CDATA #REQUIRED
+>
+
+<!-- Appenders must have a name and a class. -->
+<!-- Appenders may contain an error handler, a layout, optional parameters -->
+<!-- and filters. They may also reference (or include) other appenders. -->
+<!ELEMENT appender (errorHandler?, param*,
+      rollingPolicy?, triggeringPolicy?, connectionSource?,
+      layout?, filter*, appender-ref*)>
+<!ATTLIST appender
+  name 		CDATA 	#REQUIRED
+  class 	CDATA	#REQUIRED
+>
+
+<!ELEMENT layout (param*)>
+<!ATTLIST layout
+  class		CDATA	#REQUIRED
+>
+
+<!ELEMENT filter (param*)>
+<!ATTLIST filter
+  class		CDATA	#REQUIRED
+>
+
+<!-- ErrorHandlers can be of any class. They can admit any number of -->
+<!-- parameters. -->
+
+<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)> 
+<!ATTLIST errorHandler
+   class        CDATA   #REQUIRED 
+>
+
+<!ELEMENT root-ref EMPTY>
+
+<!ELEMENT logger-ref EMPTY>
+<!ATTLIST logger-ref
+  ref CDATA #REQUIRED
+>
+
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+  name		CDATA   #REQUIRED
+  value		CDATA	#REQUIRED
+>
+
+
+<!-- The priority class is org.apache.log4j.Level by default -->
+<!ELEMENT priority (param*)>
+<!ATTLIST priority
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+<!-- The level class is org.apache.log4j.Level by default -->
+<!ELEMENT level (param*)>
+<!ATTLIST level
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named category. -->
+<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
+<!ATTLIST category
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named logger. -->
+<!ELEMENT logger (level?,appender-ref*)>
+<!ATTLIST logger
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+
+<!ELEMENT categoryFactory (param*)>
+<!ATTLIST categoryFactory 
+   class        CDATA #REQUIRED>
+
+<!ELEMENT loggerFactory (param*)>
+<!ATTLIST loggerFactory
+   class        CDATA #REQUIRED>
+
+<!ELEMENT appender-ref EMPTY>
+<!ATTLIST appender-ref
+  ref CDATA #REQUIRED
+>
+
+<!-- plugins must have a name and class and can have optional parameters -->
+<!ELEMENT plugin (param*, connectionSource?)>
+<!ATTLIST plugin
+  name 		CDATA 	   #REQUIRED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT connectionSource (dataSource?, param*)>
+<!ATTLIST connectionSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT dataSource (param*)>
+<!ATTLIST dataSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT triggeringPolicy ((param|filter)*)>
+<!ATTLIST triggeringPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT rollingPolicy (param*)>
+<!ATTLIST rollingPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+
+<!-- If no priority element is specified, then the configurator MUST not -->
+<!-- touch the priority of root. -->
+<!-- The root category always exists and cannot be subclassed. -->
+<!ELEMENT root (param*, (priority|level)?, appender-ref*)>
+
+
+<!-- ==================================================================== -->
+<!--                       A logging event                                -->
+<!-- ==================================================================== -->
+<!ELEMENT log4j:eventSet (log4j:event*)>
+<!ATTLIST log4j:eventSet
+  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  version                (1.1|1.2) "1.2" 
+  includesLocationInfo   (true|false) "true"
+>
+
+
+
+<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?, 
+                       log4j:locationInfo?, log4j:properties?) >
+
+<!-- The timestamp format is application dependent. -->
+<!ATTLIST log4j:event
+    logger     CDATA #REQUIRED
+    level      CDATA #REQUIRED
+    thread     CDATA #REQUIRED
+    timestamp  CDATA #REQUIRED
+    time       CDATA #IMPLIED
+>
+
+<!ELEMENT log4j:message (#PCDATA)>
+<!ELEMENT log4j:NDC (#PCDATA)>
+
+<!ELEMENT log4j:throwable (#PCDATA)>
+
+<!ELEMENT log4j:locationInfo EMPTY>
+<!ATTLIST log4j:locationInfo
+  class  CDATA	#REQUIRED
+  method CDATA	#REQUIRED
+  file   CDATA	#REQUIRED
+  line   CDATA	#REQUIRED
+>
+
+<!ELEMENT log4j:properties (log4j:data*)>
+
+<!ELEMENT log4j:data EMPTY>
+<!ATTLIST log4j:data
+  name   CDATA	#REQUIRED
+  value  CDATA	#REQUIRED
+>
diff --git a/src/test/resources/log4j.xml b/src/test/resources/log4j.xml
new file mode 100644
index 0000000..04c2b43
--- /dev/null
+++ b/src/test/resources/log4j.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!-- main resources -->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+  <appender name="console" class="org.apache.log4j.ConsoleAppender"> 
+    <param name="Target" value="System.out"/> 
+    <layout class="org.apache.log4j.PatternLayout"> 
+      <param name="ConversionPattern" value="%-5p %m%n"/> 
+    </layout> 
+  </appender>
+
+    <appender name="file" class="org.apache.log4j.FileAppender">
+        <param name="File" value="target/unit-tests.log"/>
+        <layout class="org.apache.log4j.PatternLayout">
+          <param name="ConversionPattern" value="%-5p %m%n"/>
+        </layout>
+    </appender>
+
+  <logger name="org.owasp.esapi.reference.TestTrace">
+    <level value="trace"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestDebug">
+    <level value="debug"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestInfo">
+    <level value="info"/>
+ </logger>
+
+  <logger name="org.owasp.esapi.reference.TestWarning">
+    <level value="warn"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestError">
+    <level value="error"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference.TestFatal">
+    <level value="fatal"/>
+  </logger>
+
+  <logger name="org.owasp.esapi.reference">
+    <level value="info"/>
+  </logger>
+
+  <root>
+    <priority value="debug" />
+    <appender-ref ref="file" />
+  </root>
+
+  <loggerFactory class="org.owasp.esapi.reference.Log4JLoggerFactory"/>
+
+</log4j:configuration>
diff --git a/src/test/resources/properties/.svn/all-wcprops b/src/test/resources/properties/.svn/all-wcprops
new file mode 100644
index 0000000..c39925b
--- /dev/null
+++ b/src/test/resources/properties/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/properties
+END
+ESAPI_en_US.properties
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/properties/ESAPI_en_US.properties
+END
+ESAPI_fr_FR.properties
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/properties/ESAPI_fr_FR.properties
+END
+ESAPI_zhs_CN.properties
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/test/resources/properties/ESAPI_zhs_CN.properties
+END
diff --git a/src/test/resources/properties/.svn/entries b/src/test/resources/properties/.svn/entries
new file mode 100644
index 0000000..6b0a7e1
--- /dev/null
+++ b/src/test/resources/properties/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/test/resources/properties
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2009-05-28T01:33:43.860464Z
+525
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+ESAPI_zhs_CN.properties
+file
+
+
+
+
+2014-02-18T16:19:52.505957Z
+7270334fb9959d18b8d28d296955c1c5
+2009-05-21T18:46:29.121433Z
+522
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+40
+

+ESAPI_en_US.properties
+file
+
+
+
+
+2014-02-18T16:19:52.505957Z
+ad9bd570860eda9a26787bf0827e5ff7
+2009-05-21T18:46:29.121433Z
+522
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+174
+

+ESAPI_fr_FR.properties
+file
+
+
+
+
+2014-02-18T16:19:52.505957Z
+9e399d84513a2118654a27916f97f5d6
+2009-05-21T18:46:29.121433Z
+522
+planetlevel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+192
+

diff --git a/src/test/resources/properties/.svn/text-base/ESAPI_en_US.properties.svn-base b/src/test/resources/properties/.svn/text-base/ESAPI_en_US.properties.svn-base
new file mode 100644
index 0000000..9b43d1a
--- /dev/null
+++ b/src/test/resources/properties/.svn/text-base/ESAPI_en_US.properties.svn-base
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Error creating randomizer
+
+This.is.test.message=This {0} is {1} a test {2} message
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/src/test/resources/properties/.svn/text-base/ESAPI_fr_FR.properties.svn-base b/src/test/resources/properties/.svn/text-base/ESAPI_fr_FR.properties.svn-base
new file mode 100644
index 0000000..89ebcba
--- /dev/null
+++ b/src/test/resources/properties/.svn/text-base/ESAPI_fr_FR.properties.svn-base
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Erreur lors de la cr�ation al�atoire
+
+This.is.test.message=Ceci est un message de test message singh
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/src/test/resources/properties/.svn/text-base/ESAPI_zhs_CN.properties.svn-base b/src/test/resources/properties/.svn/text-base/ESAPI_zhs_CN.properties.svn-base
new file mode 100644
index 0000000..2287405
--- /dev/null
+++ b/src/test/resources/properties/.svn/text-base/ESAPI_zhs_CN.properties.svn-base
@@ -0,0 +1 @@
+Back_error_popup.tpl.13=��������˻�����
\ No newline at end of file
diff --git a/src/test/resources/properties/ESAPI_en_US.properties b/src/test/resources/properties/ESAPI_en_US.properties
new file mode 100644
index 0000000..9b43d1a
--- /dev/null
+++ b/src/test/resources/properties/ESAPI_en_US.properties
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Error creating randomizer
+
+This.is.test.message=This {0} is {1} a test {2} message
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/src/test/resources/properties/ESAPI_fr_FR.properties b/src/test/resources/properties/ESAPI_fr_FR.properties
new file mode 100644
index 0000000..89ebcba
--- /dev/null
+++ b/src/test/resources/properties/ESAPI_fr_FR.properties
@@ -0,0 +1,9 @@
+# User Messages
+Error.creating.randomizer=Erreur lors de la cr�ation al�atoire
+
+This.is.test.message=Ceci est un message de test message singh
+
+# Validation Messages
+
+# Log Messages
+
diff --git a/src/test/resources/properties/ESAPI_zhs_CN.properties b/src/test/resources/properties/ESAPI_zhs_CN.properties
new file mode 100644
index 0000000..2287405
--- /dev/null
+++ b/src/test/resources/properties/ESAPI_zhs_CN.properties
@@ -0,0 +1 @@
+Back_error_popup.tpl.13=��������˻�����
\ No newline at end of file
diff --git a/src/util/.svn/all-wcprops b/src/util/.svn/all-wcprops
new file mode 100644
index 0000000..75aaf10
--- /dev/null
+++ b/src/util/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/util
+END
+esapi-release.sh
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/util/esapi-release.sh
+END
+README.txt
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/1899/tags/esapi-2.1.0/src/util/README.txt
+END
diff --git a/src/util/.svn/entries b/src/util/.svn/entries
new file mode 100644
index 0000000..e0ff1a8
--- /dev/null
+++ b/src/util/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/src/util
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2011-01-01T01:38:53.536531Z
+1670
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+

+esapi-release.sh
+file
+
+
+
+
+2014-02-18T16:19:53.497977Z
+332ef4bd8a984b7f4eb52b2444a296fa
+2011-01-01T01:38:53.536531Z
+1670
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9556
+

+README.txt
+file
+
+
+
+
+2014-02-18T16:19:53.497977Z
+d1908c413f7173a93ee4bb0739c3b5b4
+2010-08-26T04:40:25.831707Z
+1489
+kevin.w.wall
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+501
+

diff --git a/src/util/.svn/prop-base/README.txt.svn-base b/src/util/.svn/prop-base/README.txt.svn-base
new file mode 100644
index 0000000..138f983
--- /dev/null
+++ b/src/util/.svn/prop-base/README.txt.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 10
+text/plain
+END
diff --git a/src/util/.svn/prop-base/esapi-release.sh.svn-base b/src/util/.svn/prop-base/esapi-release.sh.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/src/util/.svn/prop-base/esapi-release.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/src/util/.svn/text-base/README.txt.svn-base b/src/util/.svn/text-base/README.txt.svn-base
new file mode 100644
index 0000000..8fe3ea3
--- /dev/null
+++ b/src/util/.svn/text-base/README.txt.svn-base
@@ -0,0 +1,22 @@
+This directory is for utilities used for building / packaging / releasing
+ESAPI.
+
+========================
+
+Jim Manico's instructions for creating changelog.txt:
+
+This is an ECLIPSE plug-in feature, not a SVN feature.
+
+I use the SUBCLIPSE plugin.
+
+Right click project root
+
+TEAM -> SHOW HISTORY
+
+Then from the history table, I see a list of history entries that represent
+our checking.
+
+I select a few rows, right click, then pick CHANGELOG.
+
+
+(Note: The SVN Subversive plug-in does NOT support this.)
\ No newline at end of file
diff --git a/src/util/.svn/text-base/esapi-release.sh.svn-base b/src/util/.svn/text-base/esapi-release.sh.svn-base
new file mode 100644
index 0000000..d89d3b3
--- /dev/null
+++ b/src/util/.svn/text-base/esapi-release.sh.svn-base
@@ -0,0 +1,215 @@
+#!/bin/sh
+# Purpose: Prepare an ESAPI release.
+#
+# Usage: esapi-release.sh esapi_svn_dir
+#   where,  esapi_svn_dir   is the directory where you retrieved the ESAPI
+#                           SVN tree to and built ESAPI via Maven or Eclipse.
+#                           This directory _must_ already exist.
+#                           There should be a 'src' and 'target' directories
+#                           under this directory and the 'target' directory
+#                           is where we will build the ESAPI zip file that
+#                           you then will be placed into the owasp-esapi-java
+#                           project hosting on Google Code.
+#
+# Assumptions:  Maven (mvn) is available in $PATH. If not, modify PATH
+#               in script (see 'Tunable Parameters') accordingly.
+#
+#               The correct version has been set / updated in pom.xml for
+#               the 'esapi' <artifactId> and this has been committed to SVN.
+#
+#               All tests pass. We skip the running of all the JUnit tests.
+#               (See the call to mvn and the -Dmaven.test.skip=true argument.)
+#
+# Bugs: This is going to be a bitch to write as a DOS .bat script. I should
+#       have my head examined for termites! What *was* I thinking???
+#
+#       Need to figure out how to create changelog.txt using 'svn log' command.
+#
+#############################################################################
+#
+# This file is part of the Open Web Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2010 - The OWASP Foundation
+#
+# ESAPI is published by OWASP under the BSD license. You should read and
+# accept the LICENSE before you use, modify, and/or redistribute this software.
+#
+# Author: kevin.w.wall at gmail.com
+############################################################################
+
+#
+# Tunable parameters
+#
+#PATH=$PATH:/path/to/maven
+zipcmd=zip          # Is there something better for Linux?
+                    # This doesn't seem to have very good compression.
+unzipcmd=unzip
+# clean=clean         # Comment out if you don't Maven to do 'clean'. Will speed
+                    # things up a little bit.
+#esapiConfig=.esapi	# Sub-directory where ESAPI.properties resides
+esapiConfig=esapi	# Sub-directory where ESAPI.properties resides
+
+#
+# Non-tunable parameters
+#
+PROG="${0##*/}"
+USAGE="Usage: $PROG esapi_svn_dir"
+tmpdir="/tmp/$PROG.$RANDOM-$$"
+esapi_release_dir="$tmpdir/esapi_release_dir"
+
+    # This is the directory under esapi_svn_dir where the log4j and ESAPI
+    # properties files are located as well as the $esapiConfig/* config files.
+    # Note that formerly used to be under src/main/resources, but it since
+    # has been moved because where it was previously was causing problems with
+    # Sonatype's Nexus. That particular problem may have been resolved, but it
+    # it was, the ESAPI configuration stuff has never been moved back.
+configDir="configuration"
+
+# Cause the 'echo' builtin to interpret backslash-escaped characters.
+# If KornShell is installed as /bin/sh, this command won't be available,
+# but for ksh, 'echo' already works the way we want it to anyhow.
+shopt -s xpg_echo 2>/dev/null
+
+if [[ $# -eq 1 ]]
+then
+    esapi_svn_dir="$1"
+else
+    echo >&2 "$USAGE"
+    # Note: exit code '2' is standard for a simple usage error, w/ no other
+    #       error message. Unfortunaely, no one (at least the GNU folks)
+    #       seem to follow this convention any longer and all use 1. We're
+    #       sticking with old school, 'cuz I'm an old guy. ;-)
+    exit 2
+fi
+
+# A few simple directory sanity checks. The 1st check is VERY unlikely to fail.
+[[ $esapi_svn_dir == $esapi_release_dir ]] &&
+    { echo >&2 "$PROG: ESAPI SVN directory same as tmp dir!\n$USAGE"; exit 1; }
+[[ ! -d $esapi_svn_dir ]] &&
+    { echo >&2 "$PROG: ESAPI SVN directory, $esapi_svn_dir, does not exist or not a directory."; exit 1; }
+[[ ! -d $esapi_svn_dir/src/main ]] &&
+    { echo >&2 "$PROG: Wrong directory specified??? Missing 'src/main' directory: $esapi_svn_dir/src/main - does not exist or not a directory."; exit 1; }
+[[ -f "$esapi_svn_dir"/pom.xml ]] ||
+    { echo 2>&1 "$PROG: missing pom.xml. Looks like $esapi_svn_dir is not the SVN dir.";
+      echo 2>&1 "USAGE"; exit 1; }
+
+mkdir $tmpdir || exit 1 # Exit if it already exists.
+trap "rm -fr $tmpdir" EXIT  # We probably want this skipped if the mkdir fails
+umask 022
+mkdir $tmpdir/esapi_release_dir || exit 1
+
+# Create an intermediate distribution zip file. The zip file will be
+# left in the 'target' directory and named according to what <version>
+# is in the pom.xml file for the 'esapi' <artifactId>. For release
+# candidates, it will be something like this:
+#       esapi-2.0_RC7-SNAPSHOT-dist.zip
+# and inside of it, the ESAPI jar would be named 'esapi-2.0_RC7-SNAPSHOT.jar'.
+cd "$esapi_svn_dir"
+tmpout=$tmpdir/mvn.out
+echo "Running mvn to create intermediate zip file.\nPlease wait; this probably will take awhile..."
+rm -f target/esapi-*.zip target/esapi-*.jar
+mvn $clean site -Pdist -Dmaven.test.skip=true >$tmpout 2>&1
+if [[ $? != 0 ]]
+then    echo >&2 "$PROG: Maven failed to build distribution zip file"
+        echo >&2 "\tSee results in: $tmpout"
+        trap - EXIT    # Clear exit trap so stuff not removed.
+        exit 1
+else    rm $tmpout
+        echo "Maven completed successfully."
+fi
+
+jarfile=$(ls target/esapi-*.jar 2>&-)
+if [[ -n "$jarfile" && -r $jarfile ]]
+then    jarfile=$PWD/$jarfile
+else    echo >&2 "$PROG: Can't find ESAPI jar file created by Maven."
+        exit 1
+fi
+# OK, now we need to adjust the jar file. We don't want the properties in
+# the ESAPI jar as too many people have complained about the ESAPI.properties
+# and other stuff there. Also, we want to delete settings.xml and
+# owasp-esapi-dev.jks. We might add the latter once we start signing the
+# jar.
+jartmpdir=$tmpdir/esapi-jar
+mkdir $jartmpdir
+cd $jartmpdir || exit
+jar xf "$jarfile"
+rm -fr ${esapiConfig:?}
+rm -f properties/* log4j.*
+rm -f settings.xml owasp-esapi-dev.jks
+# TODO: This part would need some work if we sign or seal the ESAPI jar as
+#       that creates a special MANIFEST.MF file and other special files and
+#       it's not clear they will be merely copied by the simply jar command
+#       below.
+jar cf "$jarfile" .
+
+# Now work on the zip file.
+cd "$esapi_svn_dir"
+zipfile=$(ls target/esapi-*.zip 2>&-)
+if [[ -n "$zipfile" && -r $zipfile ]]
+then    zipfile="$esapi_svn_dir"/$zipfile   # 'target/' already included.
+else    echo >&2 "$PROG: Can't find ESAPI zip file created by Maven."
+        exit 1
+fi
+[[ -s $zipfile ]] ||
+    { echo 2>&1 "$PROG: zip file $zipfile has 0 size."; exit 1; }
+$unzipcmd -q "$zipfile" -d "$esapi_release_dir" || exit 1
+cd "$esapi_release_dir" || exit 1
+
+# 1) Combine the two license files into one and make it a DOS .txt
+#    file so those do don't have real editors (i.e., notepad newbs) can
+#    read it just by clicking on it. Generally reading DOS text files on *nix
+#    is never a problem.
+( echo "\t\tESAPI Source Code:\n\n"; cat LICENSE; echo "\n\n=========================\n\n\t\tESAPI Documentation:\n\n"; cat LICENSE-CONTENT ) >LICENSE.txt
+rm LICENSE LICENSE-CONTENT
+unix2dos -q LICENSE.txt
+
+# 2) Patch up the 'configuration' directory. Need to copy owasp-esapi-dev.jks
+#    here as well as the $esapiConfig directory. Also need to populate the
+#    properties subdirectory. Not sure where the settings.xml file should
+#    go, or even if we should leave it here; will copy it to 'configuration'
+#    directory.
+
+# Note: Not sure why this is now needed. Something must have changed in the
+# pom.xml that requires this, but have recently found that even the
+# configuration directory does not exist.
+if [[ ! -d "$configDir" ]]
+then    mkdir -p "$configDir"/properties ||
+    { echo >&2 "$PROG: Missing '$configDir' directory and cannot create it!"; exit 1; }
+fi
+cp -p "$esapi_svn_dir"/resources/owasp-esapi-dev.jks configuration/
+cp -p "$esapi_svn_dir"/resources/settings.xml configuration/
+cp -r -p "$esapi_svn_dir"/"$configDir"/$esapiConfig configuration/$esapiConfig/
+cp -p "$esapi_svn_dir"/"$configDir"/properties/* configuration/properties/
+
+# 3) Create the changelog.txt which should be the changes since the
+#    last release.
+##TODO  - Not sure how to do this, but their must be a way since the Subclipse
+#         Eclipse plugin is able to do it somehow. We can use 'svn log' if
+#         we can figure out the starting and ending SVN revisions. (See
+#         http://www.bernzilla.com/item.php?id=613 for details.)
+echo "$PROG: Skipping creation of changelog.txt in zip file."
+echo "\tManually create changelog.txt and add it to the final zip file."
+
+# 4) Copy the pom.xml there. It doesn't get created by the Maven goal.
+cp -p "$esapi_svn_dir"/pom.xml .
+
+# 5) Update zip file w/ new, updated ESAPI jar file.
+cp -p "$jarfile" .
+
+# 6) Fix up permissions so when zip is extracted, it comes out sane.
+chmod -R a+r,go-w .
+
+# Now we take the contents of the ESAPI release directory and re-zip it.
+# We can't use the 'freshen' option here because that has to be run
+# from the same directory (which would be the ESAPI SVN directory).
+rm "$zipfile"
+cd "$esapi_release_dir"
+$zipcmd -q -r $zipfile .
+
+cd /    # In case some weird 'rm' command (from EXIT trap) prevents us from
+        # removing directory that we are under. I could see something like
+        # that happen with Cygwin and Windows.
+echo "Zip file at: $zipfile\nPlease check it for accuracy before releasing."
+exit 0
diff --git a/src/util/README.txt b/src/util/README.txt
new file mode 100644
index 0000000..8fe3ea3
--- /dev/null
+++ b/src/util/README.txt
@@ -0,0 +1,22 @@
+This directory is for utilities used for building / packaging / releasing
+ESAPI.
+
+========================
+
+Jim Manico's instructions for creating changelog.txt:
+
+This is an ECLIPSE plug-in feature, not a SVN feature.
+
+I use the SUBCLIPSE plugin.
+
+Right click project root
+
+TEAM -> SHOW HISTORY
+
+Then from the history table, I see a list of history entries that represent
+our checking.
+
+I select a few rows, right click, then pick CHANGELOG.
+
+
+(Note: The SVN Subversive plug-in does NOT support this.)
\ No newline at end of file
diff --git a/src/util/esapi-release.sh b/src/util/esapi-release.sh
new file mode 100755
index 0000000..d89d3b3
--- /dev/null
+++ b/src/util/esapi-release.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+# Purpose: Prepare an ESAPI release.
+#
+# Usage: esapi-release.sh esapi_svn_dir
+#   where,  esapi_svn_dir   is the directory where you retrieved the ESAPI
+#                           SVN tree to and built ESAPI via Maven or Eclipse.
+#                           This directory _must_ already exist.
+#                           There should be a 'src' and 'target' directories
+#                           under this directory and the 'target' directory
+#                           is where we will build the ESAPI zip file that
+#                           you then will be placed into the owasp-esapi-java
+#                           project hosting on Google Code.
+#
+# Assumptions:  Maven (mvn) is available in $PATH. If not, modify PATH
+#               in script (see 'Tunable Parameters') accordingly.
+#
+#               The correct version has been set / updated in pom.xml for
+#               the 'esapi' <artifactId> and this has been committed to SVN.
+#
+#               All tests pass. We skip the running of all the JUnit tests.
+#               (See the call to mvn and the -Dmaven.test.skip=true argument.)
+#
+# Bugs: This is going to be a bitch to write as a DOS .bat script. I should
+#       have my head examined for termites! What *was* I thinking???
+#
+#       Need to figure out how to create changelog.txt using 'svn log' command.
+#
+#############################################################################
+#
+# This file is part of the Open Web Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2010 - The OWASP Foundation
+#
+# ESAPI is published by OWASP under the BSD license. You should read and
+# accept the LICENSE before you use, modify, and/or redistribute this software.
+#
+# Author: kevin.w.wall at gmail.com
+############################################################################
+
+#
+# Tunable parameters
+#
+#PATH=$PATH:/path/to/maven
+zipcmd=zip          # Is there something better for Linux?
+                    # This doesn't seem to have very good compression.
+unzipcmd=unzip
+# clean=clean         # Comment out if you don't Maven to do 'clean'. Will speed
+                    # things up a little bit.
+#esapiConfig=.esapi	# Sub-directory where ESAPI.properties resides
+esapiConfig=esapi	# Sub-directory where ESAPI.properties resides
+
+#
+# Non-tunable parameters
+#
+PROG="${0##*/}"
+USAGE="Usage: $PROG esapi_svn_dir"
+tmpdir="/tmp/$PROG.$RANDOM-$$"
+esapi_release_dir="$tmpdir/esapi_release_dir"
+
+    # This is the directory under esapi_svn_dir where the log4j and ESAPI
+    # properties files are located as well as the $esapiConfig/* config files.
+    # Note that formerly used to be under src/main/resources, but it since
+    # has been moved because where it was previously was causing problems with
+    # Sonatype's Nexus. That particular problem may have been resolved, but it
+    # it was, the ESAPI configuration stuff has never been moved back.
+configDir="configuration"
+
+# Cause the 'echo' builtin to interpret backslash-escaped characters.
+# If KornShell is installed as /bin/sh, this command won't be available,
+# but for ksh, 'echo' already works the way we want it to anyhow.
+shopt -s xpg_echo 2>/dev/null
+
+if [[ $# -eq 1 ]]
+then
+    esapi_svn_dir="$1"
+else
+    echo >&2 "$USAGE"
+    # Note: exit code '2' is standard for a simple usage error, w/ no other
+    #       error message. Unfortunaely, no one (at least the GNU folks)
+    #       seem to follow this convention any longer and all use 1. We're
+    #       sticking with old school, 'cuz I'm an old guy. ;-)
+    exit 2
+fi
+
+# A few simple directory sanity checks. The 1st check is VERY unlikely to fail.
+[[ $esapi_svn_dir == $esapi_release_dir ]] &&
+    { echo >&2 "$PROG: ESAPI SVN directory same as tmp dir!\n$USAGE"; exit 1; }
+[[ ! -d $esapi_svn_dir ]] &&
+    { echo >&2 "$PROG: ESAPI SVN directory, $esapi_svn_dir, does not exist or not a directory."; exit 1; }
+[[ ! -d $esapi_svn_dir/src/main ]] &&
+    { echo >&2 "$PROG: Wrong directory specified??? Missing 'src/main' directory: $esapi_svn_dir/src/main - does not exist or not a directory."; exit 1; }
+[[ -f "$esapi_svn_dir"/pom.xml ]] ||
+    { echo 2>&1 "$PROG: missing pom.xml. Looks like $esapi_svn_dir is not the SVN dir.";
+      echo 2>&1 "USAGE"; exit 1; }
+
+mkdir $tmpdir || exit 1 # Exit if it already exists.
+trap "rm -fr $tmpdir" EXIT  # We probably want this skipped if the mkdir fails
+umask 022
+mkdir $tmpdir/esapi_release_dir || exit 1
+
+# Create an intermediate distribution zip file. The zip file will be
+# left in the 'target' directory and named according to what <version>
+# is in the pom.xml file for the 'esapi' <artifactId>. For release
+# candidates, it will be something like this:
+#       esapi-2.0_RC7-SNAPSHOT-dist.zip
+# and inside of it, the ESAPI jar would be named 'esapi-2.0_RC7-SNAPSHOT.jar'.
+cd "$esapi_svn_dir"
+tmpout=$tmpdir/mvn.out
+echo "Running mvn to create intermediate zip file.\nPlease wait; this probably will take awhile..."
+rm -f target/esapi-*.zip target/esapi-*.jar
+mvn $clean site -Pdist -Dmaven.test.skip=true >$tmpout 2>&1
+if [[ $? != 0 ]]
+then    echo >&2 "$PROG: Maven failed to build distribution zip file"
+        echo >&2 "\tSee results in: $tmpout"
+        trap - EXIT    # Clear exit trap so stuff not removed.
+        exit 1
+else    rm $tmpout
+        echo "Maven completed successfully."
+fi
+
+jarfile=$(ls target/esapi-*.jar 2>&-)
+if [[ -n "$jarfile" && -r $jarfile ]]
+then    jarfile=$PWD/$jarfile
+else    echo >&2 "$PROG: Can't find ESAPI jar file created by Maven."
+        exit 1
+fi
+# OK, now we need to adjust the jar file. We don't want the properties in
+# the ESAPI jar as too many people have complained about the ESAPI.properties
+# and other stuff there. Also, we want to delete settings.xml and
+# owasp-esapi-dev.jks. We might add the latter once we start signing the
+# jar.
+jartmpdir=$tmpdir/esapi-jar
+mkdir $jartmpdir
+cd $jartmpdir || exit
+jar xf "$jarfile"
+rm -fr ${esapiConfig:?}
+rm -f properties/* log4j.*
+rm -f settings.xml owasp-esapi-dev.jks
+# TODO: This part would need some work if we sign or seal the ESAPI jar as
+#       that creates a special MANIFEST.MF file and other special files and
+#       it's not clear they will be merely copied by the simply jar command
+#       below.
+jar cf "$jarfile" .
+
+# Now work on the zip file.
+cd "$esapi_svn_dir"
+zipfile=$(ls target/esapi-*.zip 2>&-)
+if [[ -n "$zipfile" && -r $zipfile ]]
+then    zipfile="$esapi_svn_dir"/$zipfile   # 'target/' already included.
+else    echo >&2 "$PROG: Can't find ESAPI zip file created by Maven."
+        exit 1
+fi
+[[ -s $zipfile ]] ||
+    { echo 2>&1 "$PROG: zip file $zipfile has 0 size."; exit 1; }
+$unzipcmd -q "$zipfile" -d "$esapi_release_dir" || exit 1
+cd "$esapi_release_dir" || exit 1
+
+# 1) Combine the two license files into one and make it a DOS .txt
+#    file so those do don't have real editors (i.e., notepad newbs) can
+#    read it just by clicking on it. Generally reading DOS text files on *nix
+#    is never a problem.
+( echo "\t\tESAPI Source Code:\n\n"; cat LICENSE; echo "\n\n=========================\n\n\t\tESAPI Documentation:\n\n"; cat LICENSE-CONTENT ) >LICENSE.txt
+rm LICENSE LICENSE-CONTENT
+unix2dos -q LICENSE.txt
+
+# 2) Patch up the 'configuration' directory. Need to copy owasp-esapi-dev.jks
+#    here as well as the $esapiConfig directory. Also need to populate the
+#    properties subdirectory. Not sure where the settings.xml file should
+#    go, or even if we should leave it here; will copy it to 'configuration'
+#    directory.
+
+# Note: Not sure why this is now needed. Something must have changed in the
+# pom.xml that requires this, but have recently found that even the
+# configuration directory does not exist.
+if [[ ! -d "$configDir" ]]
+then    mkdir -p "$configDir"/properties ||
+    { echo >&2 "$PROG: Missing '$configDir' directory and cannot create it!"; exit 1; }
+fi
+cp -p "$esapi_svn_dir"/resources/owasp-esapi-dev.jks configuration/
+cp -p "$esapi_svn_dir"/resources/settings.xml configuration/
+cp -r -p "$esapi_svn_dir"/"$configDir"/$esapiConfig configuration/$esapiConfig/
+cp -p "$esapi_svn_dir"/"$configDir"/properties/* configuration/properties/
+
+# 3) Create the changelog.txt which should be the changes since the
+#    last release.
+##TODO  - Not sure how to do this, but their must be a way since the Subclipse
+#         Eclipse plugin is able to do it somehow. We can use 'svn log' if
+#         we can figure out the starting and ending SVN revisions. (See
+#         http://www.bernzilla.com/item.php?id=613 for details.)
+echo "$PROG: Skipping creation of changelog.txt in zip file."
+echo "\tManually create changelog.txt and add it to the final zip file."
+
+# 4) Copy the pom.xml there. It doesn't get created by the Maven goal.
+cp -p "$esapi_svn_dir"/pom.xml .
+
+# 5) Update zip file w/ new, updated ESAPI jar file.
+cp -p "$jarfile" .
+
+# 6) Fix up permissions so when zip is extracted, it comes out sane.
+chmod -R a+r,go-w .
+
+# Now we take the contents of the ESAPI release directory and re-zip it.
+# We can't use the 'freshen' option here because that has to be run
+# from the same directory (which would be the ESAPI SVN directory).
+rm "$zipfile"
+cd "$esapi_release_dir"
+$zipcmd -q -r $zipfile .
+
+cd /    # In case some weird 'rm' command (from EXIT trap) prevents us from
+        # removing directory that we are under. I could see something like
+        # that happen with Cygwin and Windows.
+echo "Zip file at: $zipfile\nPlease check it for accuracy before releasing."
+exit 0
diff --git a/target/.svn/all-wcprops b/target/.svn/all-wcprops
new file mode 100644
index 0000000..9aaca66
--- /dev/null
+++ b/target/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 42
+/svn/!svn/ver/1899/tags/esapi-2.1.0/target
+END
diff --git a/target/.svn/entries b/target/.svn/entries
new file mode 100644
index 0000000..5794c7a
--- /dev/null
+++ b/target/.svn/entries
@@ -0,0 +1,28 @@
+10
+
+dir
+1941
+http://owasp-esapi-java.googlecode.com/svn/tags/esapi-2.1.0/target
+http://owasp-esapi-java.googlecode.com/svn
+
+
+
+2013-08-29T00:51:29.662812Z
+1881
+kevin.w.wall
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+69e6d614-4441-0410-9a8b-65af957f44db
+


-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/libowasp-esapi-java.git



More information about the pkg-java-commits mailing list