diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..106bef3e2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' +--- + +[**NOTE:** Please do NOT just ask general questions here as they will _not_ be answered. Instead, please use the GitHub Discussions and create a new topic under 'Questions and Answers". Also, please delete the instructions and replace them with actual text and delete the sections that are not relevant.] + +#### Describe the bug +A clear and concise description of what the bug is. + +#### Specify what ESAPI version(s) you are experiencing this bug in +This is especially important if it is not the latest version of ESAPI. Also, if you are using the Jakarta version (e.g., 'jakarta'), then please note that as well. + +#### To Reproduce +List the steps to reproduce the behavior or (ideally) attach a small JUnit test to reproduce the problem. Please _be specific_. +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error +Note also any specific configuration changes that are needed to replicate the problem. That is especially important if you are not using the default configuration files (ESAPI.properties, validation.properties, antisamy-esapi.xml, etc.) + +#### Expected behavior +A clear and concise description of what you expected to happen. + +#### Screenshots +If applicable, add screenshots to help explain your problem. +[**NOTE:** Please do NOT just ask general questions here as they will _not_ be answered. Instead, please use the GitHub Discussions and create a new topic under 'Questions and Answers". +Please delete any irrelevant portion of this template before submitting your GitHub issue. Thanks.] + +#### Platform environment (please complete the following information) + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - JDK version used with ESAPI + +#### Additional context +Add any other context about the problem here. +If known, please select the label corresponding to the affected ESAPI component. diff --git a/.github/ISSUE_TEMPLATE/enhancement-request.md b/.github/ISSUE_TEMPLATE/enhancement-request.md new file mode 100644 index 000000000..c525b5a48 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement-request.md @@ -0,0 +1,21 @@ +--- +name: Enhancement request +about: Suggest an enhancment for this project +title: '' +labels: enhancement +assignees: '' +--- + +[**NOTE:** Please do NOT just ask general questions here as they will _not_ be answered. Instead, please use the GitHub Discussions and create a new topic under 'Questions and Answers". Please delete any irrelevant portion of this template before submitting your GitHub issue. Thanks.] + +#### Is your feature request related to a problem? Please describe. +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +#### Describe the solution you'd like +A clear and concise description of what you want to happen. Note that this may include some appropriate type of documentation that is lacking or unclear. + +#### Describe alternatives you've considered including other security libraries +A clear and concise description of any alternative solutions or features you've considered. + +#### Additional context +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 000000000..a81c84710 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,24 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ develop ] + pull_request: + branches: [ develop ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Maven + run: mvn -B package --file pom.xml diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml new file mode 100644 index 000000000..b263fddf9 --- /dev/null +++ b/.github/workflows/superlinter.yml @@ -0,0 +1,26 @@ +name: Super-Linter + +# Run this workflow every time a new commit pushed to your repository +on: push + +jobs: + # Set the job key. The key is displayed as the job name + # when a job name is not provided + super-lint: + # Name the Job + name: Lint code base + # Set the type of machine to run on + runs-on: ubuntu-latest + + steps: + # Checks out a copy of your repository on the ubuntu-latest machine + - name: Checkout code + uses: actions/checkout@v2 + + # Runs the Super-Linter action and ignore errors + - name: Run Super-Linter + uses: github/super-linter@v4 + env: + DEFAULT_BRANCH: develop + DISABLE_ERRORS: true + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 0a3094a24..a9b3cec31 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /target /.settings/** .classpath +.java-version .project *.swp *~ @@ -16,3 +17,4 @@ bin/ ciphertext-portable.ser ReferenceEncryptedProperties.test.txt test.out +.DS_Store diff --git a/.snyk b/.snyk new file mode 100644 index 000000000..79fa52da9 --- /dev/null +++ b/.snyk @@ -0,0 +1,2 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.14.0 diff --git a/CONTRIBUTING-TO-ESAPI.txt b/CONTRIBUTING-TO-ESAPI.txt index 5e5ec846e..970efcac1 100644 --- a/CONTRIBUTING-TO-ESAPI.txt +++ b/CONTRIBUTING-TO-ESAPI.txt @@ -3,7 +3,24 @@ Getting Started: If you have not already done so, go back and read the section "Contributing to ESAPI legacy" in ESAPI's README.md file. It - make contain updates and advice not contained herein. + may contain updates and advice not contained herein. + +A Special Note on GitHub Authentication: + GitHub has announced that they are deprecating password based authentication + using username / password and beginning 2021-08-13, you will no longer be + able to your password to authenticate to 'git' operations on GitHub.com. + Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ + for details and plan accordingly. + +A Special Note Regarding Making Commits for PRs + Shortly after the 2.5.1.0 ESAPI release in late November 2022, the ESAPI + team decided to lock down the 'develop' amd 'main' branches. Merges from + PRs are done to the 'develop' branch. That means that if you intend to + contribute to ESAPI, you must be signing your commits. Please see the + GitHub instructions at + https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits + for details. + Finding Something Interesting to Work on: @@ -13,22 +30,23 @@ Finding Something Interesting to Work on: or "help wanted", those are good places to start for someone not yet familiar with the ESAPI code base. - You will need a account on GitHub though. Once you create one, let me know + You will need a account on GitHub though. Once you create one, let us know what it is. Then if you want to work on a particular issue, we can assign it to you so someone else won't take it. - If you have questions, email me or Matt Seil (xeno6696@gmail.com). + If you have questions, email Kevin Wall (Kevin.W.Wall@gmail.com) or Matt + Seil (xeno6696@gmail.com). Overview: We are following the branching model described in https://nvie.com/posts/a-successful-git-branching-model - If you are unfamiliar with it, you would be advised to give it a - quick perusal. The major point is that the 'master' branch is reserved for - official releases (which will be tagged), the 'develop' branch is used for - ongoing development work and is the default branch, and we generally work - off 'issue' branches named 'issue-#' where # is the GitHub issue number. - (The last is not an absolute requirement, but rather a suggested - approach.) + If you are unfamiliar with it, you would be advised to give it a quick + perusal. The major point is that the 'main' (formerly 'master') branch is + reserved for official releases (which will be tagged), the 'develop' branch + is used for ongoing development work and is the default branch, and we + generally work off 'issue' branches named 'issue-#' where # is the GitHub + issue number. (The last is not an absolute requirement, but rather a + suggested approach.) Finally, we recommend setting the git property 'core.autocrlf' to 'input' in your $HOME/.gitconfig file; e.g., that file should contain something @@ -38,13 +56,14 @@ Overview: autocrlf = input Required Software: - We use Maven for building. Maven 3.1 or later is required. You also need - JDK 7 or later. (We generally use JDK 8, but compile ESAPI only to require - JDK 7, which means our code can't yet use any features exclusive to Java 8 - or later.) + We use Maven for building. Maven 3.3.9 or later is required. You also need + JDK 8 or later. [Note: If you use JDK 9 or later, there will be multiple + failures when you try to run 'mvn test' as well as some general warnings. + See ESAPI GitHub issue #496 for details. We welcome volunteers to address + this.] Building ESAPI: - https://www.owasp.org/index.php/ESAPI-Building briefly discusses how to + https://github.com/ESAPI/esapi-java-legacy/wiki/Building-ESAPI briefly discusses how to build ESAPI via Maven. Also https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-for-Java-with-Eclipse @@ -64,19 +83,25 @@ Steps to work with ESAPI: But the basic high level steps are: 1. Fork https://github.com/ESAPI/esapi-java-legacy to your own GitHub - repository. + repository using the GitHub web site. 2. On your local laptop, clone your own GitHub ESAPI repo (i.e, the forked repo created in previous step) 3. Create a new branch to work on an issue. I usually name the branch 'issue-#' where '#' is the GitHub issue # is will be working on, but you can call it whatever. E.g., git checkout -b issue-# - 4. Work on the GitHub issue on this newly created issue-# branch. + 4. Work on the GitHub issue on this newly created issue-# branch. Be sure + that you also create new JUnit tests as required that confirm that the + issue is corrected, or if you are introducing new functionality, ensure + that functionality is sufficiently covered. 5. Make sure everything builds correctly and all the JUnit tests pass - ('mvn test'). [Note: On occasion, there may be a failure in - AuthenticatorTest.testGetCurrentUser(). If there is, run 'mvn test' - again. It seems to be sporadic and it is hypothesized that it is - related to some sort of race condition in the JUnit tests.] + ('mvn test'). [Note: There are some known issues with test failures if + your are running under Windows and your local ESAPI Git repo located + anywhere other than the C: drive, where the test + ValidatorTest.testIsValidDirectoryPath() fails. Also, if you are using + JDK 7 on Mac-OS, there is one know test failure in + SecurityProviderLoaderTest.testWithBouncyCastle(). That same test works + with JDK 8.] 6. If you have added any dependencies, please also run mvn org.owasp:dependency-check-maven:check to run OWASP Dependency-Check and look at the generated report @@ -91,13 +116,19 @@ Steps to work with ESAPI: $ git remote -v | grep origin # Confirm 'origin' refers to YOUR PERSONAL GitHub repo $ git push origin issue-444 # Push the committed changes on the 'issue-444' branch 9. Go to your personal, forked ESAPI GitHub repo (web interface) and create a - 'Pull Request' from your 'issue-#' branch. + 'Pull Request' (PR) from your 'issue-#' branch. 10. Back on your local personal laptop / desktop, merge your issue branch with - your local 'develop' branch. I.e. + your local 'develop' branch. I.e., $ git checkout develop $ git merge issue-444 + 11. Do not remove your branch on your forked repository until your PR from your + branch has been merged into the ESAPI/esapi-java/legacy 'develop' branch. + Note at least one the 3 main contributors on will review your commits before + merging them and they may do a formal code review and request further changes. + Once they are satisfied, they will merge your PR. In theory, you can do all this 'git' magic from Eclipse and presumably other -IDEs like NetBeans or IntelliJ). From Eclipse, it is right-click on the -project and then select 'Team' to do the commits, etc. If you choose that -route, you're pretty much on your own because none of us use that. +IDEs like Oracle NetBeans or IntelliJ IDEA). From Eclipse, it is right-click +on the project and then select 'Team' to do the commits, etc. If you choose that +route, you're pretty much on your own because none of us use that for Git +interactions. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..4c095cb35 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,118 @@ +# Contributing to ESAPI -- Details + +## Getting Started +If you have not already done so, go back and read the section +"[Contributing to ESAPI legacy](https://github.com/ESAPI/esapi-java-legacy/blob/develop/README.md#contributing-to-esapi-legacy)" in ESAPI's README.md file. It +may contain updates and advice not contained herein. + +### A Special Note on GitHub Authentication +GitHub has announced that they are deprecating password based authentication +using username / password and beginning 2021-08-13, you will no longer be +able to your password to authenticate to 'git' operations on GitHub.com. +Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ +for details and plan accordingly. + +### A Special Note Regarding Making Commits for PRs +Shortly after the 2.5.1.0 ESAPI release in late November 2022, the ESAPI +team decided to lock down the 'develop' amd 'main' branches. Merges from +PRs are done to the 'develop' branch. That means that if you intend to +contribute to ESAPI, you must be signing your commits. Please see the +GitHub instructions at + https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits +for details. + +### Git Branching Model +We are following the branching model described in + https://nvie.com/posts/a-successful-git-branching-model +If you are unfamiliar with it, you would be advised to give it a quick +perusal. The major point is that the 'main' (formerly 'master') branch is +reserved for official releases (which will be tagged), the 'develop' branch +is used for ongoing development work and is the default branch, and we +generally work off 'issue' branches named 'issue-#' where # is the GitHub +issue number. (The last is not an absolute requirement, but rather a +suggested approach.) + +Finally, we recommend setting the git property 'core.autocrlf' to 'input' +in your $HOME/.gitconfig file; e.g., that file should contain something +like this: + + [core] + autocrlf = input + + +### Required Software +We use Maven for building. Maven 3.6.3 or later is required. You also need +JDK 8 or later. [Note: If you use JDK 9 or later, there will be multiple +failures when you try to run 'mvn test' as well as some general warnings. +See [ESAPI GitHub issue #496](https://github.com/ESAPI/esapi-java-legacy/issues/496) for details. We welcome volunteers to address +this.] +## Finding Something Interesting to Work on + +See the section [Contributing to ESAPI Legacy](https://github.com/ESAPI/esapi-java-legacy/blob/develop/README.md#contributing-to-esapi-legacy) +in the ESAPI README for suggestions. While you don't *have* to work on something labeled "good first issue" +or "help wanted", those are good places to start for someone not yet familiar with the ESAPI code base. + +You will need a account on GitHub though. Once you create one, let us know +what it is. Then if you want to work on a particular issue, we can assign +it to you so someone else won't take it. + +If you have questions, email Kevin Wall (Kevin.W.Wall@gmail.com) or Matt +Seil (xeno6696@gmail.com). + + +## Building ESAPI +See our local GitHub wiki page, [Building ESAPI](https://github.com/ESAPI/esapi-java-legacy/wiki/Building-ESAPI), +which briefly discusses how to build ESAPI via Maven. + +You can also refer to [Using ESAPI for Java with Eclipse](https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-for-Java-with-Eclipse) +if you prefer working from IDEs. There is also a much older ESAPI wiki page, +[Building with Eclipse](https://www.owasp.org/index.php/ESAPI-BuildingWithEclipse) +that might be useful. + +As always, any contributions to ESAPI's admittedly skimpy documentation in this area is welcome. +In particular, contributing some hints about debugging applications using ESAPI +would be very useful to our ESAPI clients. + +## Steps to work with ESAPI +I usually do everything from the bash command prompt in Linux Mint, +but other people use Windows. If you prefer an IDE, I can't help you +much, but I can help with at least modest problems. If you have more +difficult problems, I will probably refer you to my project co-leader, +Matt who groks git a lot better than I. + +But the basic high level steps are: + +1. Fork https://github.com/ESAPI/esapi-java-legacy to your own GitHub repository using the GitHub web site. +2. On your local laptop, clone your own GitHub ESAPI repo (i.e, the forked repo created in previous step) +3. Create a new branch to work on an issue. I usually name the branch 'issue-#' where '#' is the GitHub issue # is will be working on, but you can call it whatever. E.g., + ```bash + $ git checkout -b issue-# + ``` +4. Work on the GitHub issue on this newly created issue-# branch. Be sure that you also create new JUnit tests as required that confirm that the issue is corrected, or if you are introducing new functionality, ensure + that functionality is sufficiently covered. +5. Make sure everything builds correctly and all the JUnit tests pass ('mvn test'). [Note: There are some known issues with test failures if your are running under Windows and your local ESAPI Git repo located anywhere other than the C: drive, where the test `ValidatorTest.testIsValidDirectoryPath()` fails. +6. If you have added any dependencies, please also run OWASP Dependency-Check and look at the generated report left in 'target/dependency-check-report.html' to make sure there were not any CVEs introduced. (Alternately you can run 'mvn verify' which will first run the tests and then run Dependency-Check.) Note if this is the first time you have run Dependency-Check for ESAPI, expect it to take a while (often 30 minutes or so!). To execute Dependency Check from Maven, run: + ```bash + $ mvn org.owasp:dependency-check-maven:check + ``` +7. Commit your changes locally. +8. Push your 'issue-#' branch to your personal, forked ESAPI GitHub repo. E.g., + ```bash + $ git checkout issue-444 + $ git remote -v | grep origin # Confirm 'origin' refers to YOUR PERSONAL GitHub repo + $ git push origin issue-444 # Push the committed changes on the 'issue-444' branch + ``` +9. Go to your personal, forked ESAPI GitHub repo (web interface) and create a 'Pull Request' (PR) from your 'issue-#' branch. +10. Back on your local personal laptop / desktop, merge your issue branch with your local 'develop' branch. I.e., + $ git checkout develop + $ git merge issue-444 +11. Do not remove your branch on your forked repository until your PR from your branch has been merged into the ESAPI/esapi-java/legacy 'develop' branch. + Note at least one the 3 main contributors on will review your commits before + merging them and they may do a formal code review and request further changes. + Once they are satisfied, they will merge your PR. + +In theory, you can do all this 'git' magic from Eclipse and presumably other +IDEs like Oracle NetBeans or JetBrains IntelliJ IDEA. From Eclipse, it is right-click +on the project and then select 'Team' to do the commits, etc. If you choose that +route, you're pretty much on your own because none of us use that for Git +interactions. diff --git a/README.md b/README.md index 172027862..0c31f1de1 100644 --- a/README.md +++ b/README.md @@ -4,72 +4,259 @@ Enterprise Security API for Java (Legacy) [![Build Status](https://travis-ci.org/bkimminich/esapi-java-legacy.svg?branch=master)](https://travis-ci.org/bkimminich/esapi-java-legacy) [![Coverage Status](https://coveralls.io/repos/github/bkimminich/esapi-java-legacy/badge.svg?branch=develop)](https://coveralls.io/github/bkimminich/esapi-java-legacy?branch=develop) [![Coverity Status](https://scan.coverity.com/projects/8517/badge.svg)](https://scan.coverity.com/projects/bkimminich-esapi-java-legacy) -[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/137/badge)](https://bestpractices.coreinfrastructure.org/projects/137) +[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/137/badge)](https://bestpractices.coreinfrastructure.org/projects/137) - +
-OWASP ESAPI (The OWASP Enterprise Security API) is a free, open source, web application security control library that makes it easier for programmers to write lower-risk applications. The ESAPI for Java library is designed to make it easier for programmers to retrofit security into existing applications. ESAPI for Java also serves as a solid foundation for new development. +OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web application security control library that makes it easier for programmers to write lower-risk applications. The ESAPI for Java library is designed to make it easier for programmers to retrofit security into existing applications. ESAPI for Java also serves as a solid foundation for new development.
+# Jakarta EE Support +**IMPORTANT:** +ESAPI has supported the Jakarta Servlet API (i.e., **jakarta.servlet.api**) since release +2.5.3.0. (Unfortunately, this information was previously missing in this **README** file.) + +Therefore, for release 2.5.3.0 and later versions of ESAPI, ESAPI ought to be able to support Spring Boot 3, Spring 6, Tomcat 10, +and other applications or libraries requiring Jarkata EE. (If you find a case where it does +not, please file a GitHub issue for it.) + +The ESAPI jar file supporting Jakarta will be named esapi-_version_-jakarta.jar. To use that +specific Jakarta version of ESAPI, in Maven, you would specify your ESAPI dependency in your +**pom.xml** as: +```xml + + org.owasp.esapi + esapi + 2.7.0.0 + jakarta + +``` +(or any other version later than 2.5.3.0). Thanks to Jonathon Putney for creating a PR to +fix this. There is a long discussion in GitHub Discussion [#768](https://github.com/ESAPI/esapi-java-legacy/discussions/768) +where this was first announced, for those of you have insomnia or really long attention +spans and are interested in the approaches that were tried. + +Of course, ESAPI also still continues to support the older Java EE Servlet API (i.e., **javax.servlet** namespace) as well. In +fact, without the +```xml +jakarta +``` +that's the version that will be used by default. + + +# A word about ESAPI vulnerabilities +A summary of all the vulnerabilities that we have written about in either the +ESAPI Security Bulletins or in the GitHub Security Advisories may be found +in this [Vulnerability Summary](https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md). +It is too lengthy, and if you are using the latest available ESAPI version--generally not relevant--to +place in this **README** file. + +# Where are the OWASP ESAPI wiki pages? +You can find the official OWASP ESAPI Project wiki pages at +[https://owasp.org/www-project-enterprise-security-api/](https://owasp.org/www-project-enterprise-security-api/). +The ESAPI legacy GitHub repo also has several useful [wiki pages](https://github.com/ESAPI/esapi-java-legacy/wiki). + # What does Legacy mean? -

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. +This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however significant *new* **feature development** for this branch will *not* be done. Features that have already been scheduled for the 2.x branch will move forward. +Development for the "next generation" of ESAPI (starting with ESAPI 3.0), will be done at the +GitHub repository at [https://github.com/ESAPI/esapi-java](https://github.com/ESAPI/esapi-java). -IMPORTANT NOTE: -The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. +**IMPORTANT NOTES:** +* The default branch for ESAPI legacy is the 'develop' branch (rather than the 'main' (formerly 'master') branch), where future development, bug fixes, etc. are now being done. The 'main' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.5.3.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. +* Also, the *minimal* baseline Java version to use ESAPI is now Java 8. (This was changed from Java 7 during the 2.4.0.0 release.) +* Support was dropped for Log4J 1 during ESAPI 2.5.0.0 release. If you need it, configure it via SLF4J. See the + [2.5.0.0 release notes](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.5.0.0-release-notes.txt) +for details. # Where can I find ESAPI 3.x? -https://github.com/ESAPI/esapi-java +As mentioned above, you can find it at [https://github.com/ESAPI/esapi-java](https://github.com/ESAPI/esapi-java). -# Contributing to ESAPI legacy -## How can I contribute or help with fix bugs? -Fork and submit a pull request! Simple as pi! We generally only accept bug fixes, not new features because as a legacy project, we don't intend on adding new features, although we may make exceptions. If you wish to propose a new feature, the best place to discuss it is via the ESAPI-DEV mailing list mentioned below. Note that we vet all pull requests, including coding style of any contributions; use the same coding style found in the files you are already editing. +Note however that work on ESAPI 3 has not yet begun in earnest and is only +in its earliest planning stages. Even the code that is presently there +will likely change. + +# ESAPI Release Notes +The ESAPI release notes may be found in ESAPI's "documentation" directory. They are generally named "esapi4java-core-*2.#.#.#*-release-notes.txt", where "*2.#.#.#*" refers to the ESAPI release number (which uses semantic versioning). -If you are new to ESAPI, a good place to start is to look for GitHub issues labled as 'good first issue'. (E.g., to find all open issues with that label, use https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22.) +See the GitHub [Releases](https://github.com/ESAPI/esapi-java-legacy/releases) information for a list of releases which generally +link to the specific release notes. -You can find additional details in the file '[CONTRIBUTING-TO-ESAPI.txt](https://raw.githubusercontent.com/ESAPI/esapi-java-legacy/develop/CONTRIBUTING-TO-ESAPI.txt)'. +### Really IMPORTANT information in release notes - Ignore at your peril +* Starting with ESAPI 2.2.1.0, important details changed reading the ESAPI + Logger. If you have are getting things like ClassNotFoundException, you + probably have not read it. Please be sure to read this specific section + of the + [2.2.1.0 release notes](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.0-release-notes.txt#L128-L155) +* Starting with ESAPI 2.2.3.0, ESAPI is using a version of AntiSamy that by default includes 'slf4j-simple' and + does XML schema validation on the AntiSamy policy files. Please **READ** this + section from the + [2.2.3.0 release notes](https://github.com/ESAPI/esapi-java-legacy/blob/1312102e79d4ed98d1396f5c56e12f437534d62b/documentation/esapi4java-core-2.2.3.0-release-notes.txt#L22-L34) + (at least the beginning portion) for some important notes that likely will affect your use of ESAPI! You have been warned!!! +* ESAPI 2.3.0.0 is the last release to support Java 7 as the minimal JDK. + Starting with release 2.4.0.0, Java 8 or later is required. +* Starting with ESAPI 2.5.4.0, if you were using ESAPI's default logger, JUL + (i.e., you had the property **ESAPI.Logger** set to "org.owasp.esapi.logging.java.JavaLogFactory"), + then you must remove (or rename) the old ESAPI configuration file **esapi-java-logger.properties**. + Failure to do so will cause ESAPI to throw a `ConfigurationException`, thereby + preventing your application from starting. For important additional details, please see + the ESAPI GitHub Discussion https://github.com/ESAPI/esapi-java-legacy/discussions/841. -### What happened to Google code? -In mid-2014 ESAPI Migrated all code to GitHub. This migration was completed in November 2014. +# Locating ESAPI Jar files +The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.7.0.0. +All the *regular* ESAPI jars, with the exception of the ESAPI configuration +jar (i.e., esapi-2.#.#.#-configuration.jar) and its associated detached +GPG signature, are available from Maven Central. The ESAPI configuration +jars are linked under the 'Assets' section to each of the specific +ESAPI releases under the +GitHub [Releases page](https://github.com/ESAPI/esapi-java-legacy/releases). + + +However, **before** you start a *new* project using ESAPI, but sure to read "[Should I use ESAPI?](https://owasp.org/www-project-enterprise-security-api/#div-shouldiuseesapi)". + +# ESAPI Deprecation Policy +Unless we unintentionally screw-up, our intent is to keep classes, methods, +and/or fields which have been annotated as "@deprecated" for a +minimum of two (2) years or until the next major release number (e.g., +3.x as of now), which ever comes first, before we remove them. Note +that this policy does not apply to classes under +the **org.owasp.esapi.reference** package. You generally are not expected +to be using such classes directly in your code. At the ESAPI team's discretion, +it will also not apply for any known exploitable vulnerabilities for which +no available workaround exists. + +## Exceptions to Deprecation Policy +We will make some exceptions to the normal 2 year period. In particular, in the +cases were we believe that keeping a specific deprecated class or method around +can introduce security issues (generally because many of you have a habit of +completely ignoring deprecation warnings), we sometimes will shorten that 2 year +period. When we decide to do that, we will announce that as part of the +deprecation message. + +## Log4J 1.x Removal +**IMPORTANT NOTES:** As of ESAPI 2.5.0.0, all the Log4J 1.x related code +has been removed from the ESAPI code base (with the exception of some +references in documentation). If you must, you still should be able to +use Log4J 1.x logging via ESAPI SLF4J support. See the ESAPI 2.5.0.0 release +notes for further details. + +# Quickstart - Maven Example +### Step 1: Add the required dependencies. +See https://mvnrepository.com/artifact/org.owasp.esapi/esapi/latest, the tab for +whatever build tool you are using. If you need the Jakarta version, make sure to +add +```xml + jakarta +``` +and include whatever jakara.servlet:jakarta.servlet-api version you are using with +```xml + provided +``` +### Step 2: Obtain the 2 properties files ESAPI.properties and validation.properties +1. Download these 2 files from the ESAPI release that you are using from https://github.com/ESAPI/esapi-java-legacy/releases + and download the esapi--configuration.jar file (and the .asc file if you wish to confirm the GPG signature). +2. Unjar that configuration file that you just downloaded and find the 2 + properties files under the "configuration/esapi" subdirectory where you + unjarred the config jar. +3. Read through Javadoc for [DefaultSecurityConfiguration](https://javadoc.io/static/org.owasp.esapi/esapi/2.5.4.0/org/owasp/esapi/reference/DefaultSecurityConfiguration.html) + to understand the ways that ESAPI locates these files and then use the mechanism that works best for you. Copy the 2 properties + files from the 'configuration/esapi' directory to the directory where you + choose to have them reside. Note that you may also edit them to customize + them according to your needs. + +# Contributing to ESAPI legacy +### How can I contribute or help with fix bugs? +Fork and submit a pull request! Easy as pi! (How's that for an irrational +statement, you math nerds? :) We generally only accept bug fixes, not +new features because as a legacy project, we don't intend on adding new +features that we will have to maintain long term (although we may make +exceptions; see the 'New Features' section in this **README**). If +you are interesting in doing bug fixes though, the best place to start is the +[CONTRIBUTING-TO-ESAPI.txt](https://github.com/ESAPI/esapi-java-legacy/blob/develop/CONTRIBUTING-TO-ESAPI.txt) -### What about the issues still located on Google Code? -All issues from Google Code have been migrated to GitHub issues. We have a JIRA/Confluence instance allocated to us, but it has not be configured to synchronize with the GitHub issues, and thus is should not be used. JIRA is fine, but if we can't have it synchronized with GitHub issues (which is where the majority of our users report issues), it is not usuable. As developers, we do not want to spent time having to close issues from multiple bug-tracking sites. Therefore, until this synchronization happens (see GitHub issue #371), please ONLY use GitHub for reporting bugs. +If you are new to ESAPI, a good place to start is to look for GitHub issues labled as 'good first issue'. (E.g., to find all open issues with that label, use [https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22](https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).) -When reporting an issue, please be clear and try to ensure that the ESAPI development team has sufficient information to be able to reproduce your results. If you have not already done so, this might be a good time to read Eric S. Raymond's classic "How to Ask Questions the Smart Way", at http://www.catb.org/esr/faqs/smart-questions.html before posting your issue. +Again, please find additional important details in the file +'[CONTRIBUTING-TO-ESAPI.txt](https://github.com/ESAPI/esapi-java-legacy/blob/develop/CONTRIBUTING-TO-ESAPI.txt)', +which will also describe the tool requirements. -### Find an Issue? -If you have found a bug, then create an issue on the esapi-legacy-java repo: https://github.com/ESAPI/esapi-java-legacy/issues +#### Want to report an issue? +If you have found a bug, then create an issue on the esapi-legacy-java repo at [https://github.com/ESAPI/esapi-java-legacy/issues](https://github.com/ESAPI/esapi-java-legacy/issues) +As of May 11, 2022, we switched back to using (GitHub) issue templates. (We previously used issue templates when our source code repository was still on Google Code.) You can read more about our issue templates in this brief +[announcement](https://github.com/ESAPI/esapi-java-legacy/discussions/700). -NOTE: Please do NOT use GitHub issues to ask questions about ESAPI. If you wish to do this, post to either of the 2 mailing lists (now on Google Groups) found at the bottom of this page. If we find questions as GitHub issues, we simply will close them and direct you to do this anyhow. +NOTE: Please do **NOT** use GitHub issues to ask questions about ESAPI. +If you wish to ask questions, instead, post to either of the 2 mailing +lists (now on Google Groups) found the References section at the bottom +of this page. If we find questions posted as GitHub issues, we simply will +close them and direct you to do this anyhow. Alternately you may use the new +[Q&A](https://github.com/ESAPI/esapi-java-legacy/discussions/categories/q-a) section of our GitHub +[Discussions](https://github.com/ESAPI/esapi-java-legacy/discussions) page to ask questions. -### Find a Vulnerability? -If you have found a vulnerability in ESAPI legacy, first search the issues list (see above) to see if it has already been reported. If it has not, then please contact both Kevin W. Wall (kevin.w.wall at gmail.com) and Matt Seil (matt.seil at owasp.org) directly. Please do not report vulnerabilities via GitHub issues or via the ESAPI mailing lists as we wish to keep our users secure while a patch is implemented and deployed. If you wish to be acknowledged for finding the vulnerability, then please follow this process. (Eventually, we would like to have BugCrowd handle this, but that's still a ways off.) Also, when you post the email describing the vulnerability, please do so from an email address that you usually monitor. +When reporting an issue or just asking a question, please be clear and try +to ensure that the ESAPI development team has sufficient information to be +able to reproduce your results or to understand your question. If you have +not already done so, this might be a good time to read Eric S. Raymond's classic +"[How to Ask Questions the Smart Way](http://www.catb.org/esr/faqs/smart-questions.html)" +before posting your issue. -## Where to Find More Information on ESAPI +#### Find a Vulnerability? +If believe you have found a vulnerability in ESAPI legacy, for the sake of the +ESAPI community, please practice Responsible Disclosure. (Note: We will be sure +you get credit and will work with you to create a GitHub Security Advisory, and +if you so choose, to pursue filing a CVE via the GitHub CNA.) -*Wiki:* https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API +You are of course encouraged to first search our GitHub issues list (see above) +to see if it has already been reported. If it has not, then please contact +both Kevin W. Wall (kevin.w.wall at gmail.com) and +Matt Seil (matt.seil at owasp.org) directly. Please do not report +vulnerabilities via GitHub issues or via the ESAPI mailing lists as +we wish to keep our users secure while a patch is implemented and +deployed. If you wish to be acknowledged for finding the vulnerability, +then please follow this process. Also, when you post the email describing +the vulnerability, please do so from an email address that you usually +monitor. -*Nightly Build:* Travis CI - https://travis-ci.org/bkimminich/esapi-java-legacy +More detail is available in the file +'[SECURITY.md](https://github.com/ESAPI/esapi-java-legacy/blob/develop/SECURITY.md)'. +https://raw.githubusercontent.com/ESAPI/esapi-java-legacy/blob/develop/SECURITY.md)'. -~~JIRA: https://owasp-esapi.atlassian.net/browse/ESAPILEG~~
Issues: Until further notice, use the GitHub issues for reporting bugs and enhancement requests. +### New Features +If you wish to propose a new feature, the best place to discuss it is via +new 'Discussions' board, probably under +'[Ideas](https://github.com/ESAPI/esapi-java-legacy/discussions/categories/ideas)', +or on the ESAPI-DEV mailing list mentioned below under the References section. +As mentioned previously, we generally are not considering new features +for ESAPI 2.x. This is because: +- ESAPI is already too monolithic and has too many dependencies for its size. +- We are trying to wind down support of ESAPI 2.x and get ESAPI 3.0 going so any + resources we throw at ESAPI 2.x will slow down that goal. +That said, if you believe you have an idea for an additional simple feature that +does not pull in any additional 3rd party libraries, toss it out there for +discussion or even show us how it works with a PR. (Note that we vet all pull +requests, including coding style of any contributions, so please use the same +coding style found in the files you are already editing.) -*Documentation:* https://owasp-esapi.atlassian.net/wiki/display/ESAPILEG/ESAPI+Legacy (Coming Soon), for now find general documentation under the 'documentation/' directory, and the latest Javadoc under https://www.javadoc.io/doc/org.owasp.esapi/esapi/ +# References: Where to Find More Information on ESAPI +**OWASP Wiki:** https://owasp.org/www-project-enterprise-security-api/ -*Realtime Support available on our IRC Channel:*
-Server: irc.freenode.net
-Channel: #esapi
-Webchat http://webchat.freenode.net/ +**GitHub ESAPI Wiki:** https://github.com/ESAPI/esapi-java-legacy/wiki -*Mailing lists:* -As of 2019-03-25, ESAPI's 2 mailing lists were officially moved OFF of their Mailman mailing lists to a new home on Google Groups. +**General Documentation:** Under the '[documentation](https://github.com/ESAPI/esapi-java-legacy/tree/develop/documentation)' folder. -The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to after you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/forum/#!forum/esapi-project-users/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/forum/#!forum/esapi-project-dev/join)" respectively. +**OWASP Slack Channel:** [#owasp-esapi](https://owasp.slack.com/archives/CQ2ET27AN) -Old archives for the old Mailman mailing lists for ESAPI-Users and ESAPI-Dev are still available at https://lists.owasp.org/pipermail/esapi-users/ and https://lists.owasp.org/pipermail/esapi-dev/ respectively. +**GitHub Discussions:** [Discussions](https://github.com/ESAPI/esapi-java-legacy/discussions) - Not a lot there yet, but we only started this on May 11, 2022. -For a general overview of Google Groups and its web interface, see https://groups.google.com/forum/#!overview +**Mailing lists:** +* As of 2019-03-25, ESAPI's 2 mailing lists were officially moved OFF of their Mailman mailing lists to a new home on Google Groups. +* The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to *after* you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-users/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-dev/join)" respectively. +* Old archives for the old Mailman mailing lists for ESAPI-Users and ESAPI-Dev are still available at https://lists.owasp.org/pipermail/esapi-users/ and https://lists.owasp.org/pipermail/esapi-dev/ respectively. +* For a general overview of Google Groups and its web interface, see [https://groups.google.com/forum/#!overview](https://groups.google.com/forum/#!overview) +* For assistance subscribing and unsubscribing to Google Groups, see [https://webapps.stackexchange.com/questions/13508/how-can-i-subscribe-to-a-google-mailing-list-with-a-non-google-e-mail-address/15593#15593](https://webapps.stackexchange.com/questions/13508/how-can-i-subscribe-to-a-google-mailing-list-with-a-non-google-e-mail-address/15593#15593). -For assistance subscribing and unsubscribing to Google Groups, see https://webapps.stackexchange.com/questions/13508/how-can-i-subscribe-to-a-google-mailing-list-with-a-non-google-e-mail-address/15593#15593 +---------- +OWASP is a registered trademark of the OWASP Foundation, Inc. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..c551662fc --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,57 @@ +# Security Policy + +In general, because the ESAPI core development is so small (3 people, all +working full time jobs), we can only support the latest version of ESAPI. +If you are locked in to some previous version and are unable to upgrade +to the latest version, perhaps one or more of us might consider back-porting +a patch (especially if it is the only way to address an ESAPI vulnerability), +but if it is anything but trivial, we would charge a TBD consulting fee. + +## Supported Versions + + +| Version | Supported | +| ------- | ------------------ | +| 2.7.0.0 (latest) | :white_check_mark: | +| 2.1.0.1-2.6.2.0 | :x:, upgrade to latest release | +| <= 1.4.x | :x:, no longer supported AT ALL | + +## Reporting a Vulnerability + +If you believe that you have found a vulnerability in ESAPI, first please search the +GitHut issues list (for both open and closed issues) to see if it has already been reported. + +If it has not, then please contact **both** of the project leaders, Kevin W. Wall +(kevin.w.wall at gmail.com) and Matt Seil (matt.seil at owasp.org) _directly_. +Please do **not** report any suspected vulnerabilities via GitHub issues +or via the ESAPI mailing lists as we wish to keep our users secure while a patch +is implemented and deployed. This is because if this is reported as a GitHub +issue or posted to either ESAPI mailing list, it more or less is equivalent to +dropping a 0-day on all applications using ESAPI. Instead, we encourage +responsible disclosure. + +If you wish to be acknowledged for finding the vulnerability, then please follow +this process. One of the 2 ESAPI project leaders will try to contact you within +at least 5 business days, so when you post the email describing the +vulnerability, please do so from an email address that you usually monitor. +If you eventually wish to have it published as a CVE, we will also work with you +to ensure that you are given proper credit with MITRE and NIST. Even if you do +not wish to report the vulnerability as a CVE, we will acknowledge you when we +create a GitHub issue (once the issue is patched) as well as acknowledging you +in any security bulletin that we may write up and use to notify our users. (If you wish +to have your identity remain unknown, or perhaps you email address, we can work +with you on that as well.) + +If possible, provide a working proof-of-concept or at least minimally describe +how it can be exploited in sufficient details that the ESAPI development team +can understand what needs to be done to fix it. Unfortunately at this time, we +are not in a position to pay out bug bounties for vulnerabilities. + +Eventually, we would like to have BugCrowd handle this, but that's still a ways off. + +## ESAPI Security Bulletins and GitHub Security Advisories + +There are some ESAPI security bulletins published in the "documentation" directory on GitHub. +GitHub also has published some Security Advisories for ESAPI. +For details, see [Vulnerability Summary](https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md). + diff --git a/Vulnerability-Summary.md b/Vulnerability-Summary.md new file mode 100644 index 000000000..e7eacb6d0 --- /dev/null +++ b/Vulnerability-Summary.md @@ -0,0 +1,30 @@ +# Summary of ESAPI Security Bulletins and GitHub Security Advisories +This page attempts to summarize all the ESAPI Security Bulletins and GitHub Security Advisories in a table format. This started out as a lengthy email to the ESAPI User's Google group which you can find at +"[A word about Log4J vulnerabilities in ESAPI - the TL;DR version](https://groups.google.com/a/owasp.org/g/esapi-project-users/c/_CR8d-dpvMU)", +but then morphed into this current format as more and more Log4J 1.x vulnerabilities were discovered as well as one in ESAPI itself that we felt compelled to detail. + +Note that not all CVEs for ESAPI are reflected here as we only wrote ESAPI +Security Bulletins for CVEs that we believed were either not exploitable via +standard ESAPI configurations or that required special explanation above and beyond +was provided in the description of the CVE. + +--- + + +||||||| +|--- |--- |--- |--- |--- |--- | +|**Relevant ESAPI Security Bulletin / GitHub Security Advisory**|**Summary**|**Relevant CWEs**|**Relevant Vuln ID**|**Notes regarding potential impact**|**ESAPI versions where default configuration is impacted**| +|[1](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin1.pdf)|MAC bypass in ESAPI symmetric encryption|[CWE-310](https://cwe.mitre.org/data/definitions/310.html)|[CVE-2013-5679](https://nvd.nist.gov/vuln/detail/CVE-2013-5679)|MAC check may be bypassed thus not assuring the authenticity of the received ciphertext.|ESAPI 2.x versions before 2.1.0| +|[2](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf)|Java deserialization vulnerability in Log4J 1 (via SocketServer) for ESAPI logging may lead to code injection|[CWE-502](https://cwe.mitre.org/data/definitions/502.html)|[CVE-2019-17571](https://nvd.nist.gov/vuln/detail/CVE-2019-17571)|SocketServer is a class presumably intended for aggregating Log4J log events. It is a server-side class. ESAPI does not use it, nor any Log4J 1 classes that use it.|None.ESAPI 2.x versions 2.2.1.0 and later default to use JUL (java.util.logging)| +|[3](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin3.pdf)|This flaw allows a specially-crafted XML file to manipulate the validation process in processed by Xerces’ XMLSchemaValidation class in certain cases.|[CWE-20](https://cwe.mitre.org/data/definitions/20.html)|[SNYK-JAVA-XERCES-608891](https://security.snyk.io/vuln/SNYK-JAVA-XERCES-608891) (related to [CVE-2020-14621](https://nvd.nist.gov/vuln/detail/CVE-2020-14621))|An analysis of the ESAPI and Xerces code shows that ESAPI does not use the vulnerable Xerces class either directly or indirectly.|None, but fixed even with respect to SCA tools for ESAPI 2.2.3.0 and later which AntiSamy 1.6.2, which uses Xerces 2.12.1, where this vulnerability is fixed.| +|[4](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin4.pdf)|SMTPS (SMTP over SSL/TLS) can allow MITM attack if SMTPAppender is used with Log4J 1 ESAPI logging.|[CWE-295](https://cwe.mitre.org/data/definitions/295.html)|[CVE-2020-9488](https://nvd.nist.gov/vuln/detail/CVE-2020-9488)|If you are using Log4J 1’s SMTPAppender in your code, you already have a direct dependency that makes it exploitable. ESAPI does nothing to cause or prevent that.|None. ESAPI uses ConsoleAppender as the default appender even if ESAPI logging is configured to use Log4J 1.| +|[5](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin5.pdf)|Invoking the method Commons IO method, FileNameUtils.normalize() with an improper input string could allow a limited path traversal.|[CWE-22](https://cwe.mitre.org/data/definitions/22.html)|[CVE-2021-29425](https://nvd.nist.gov/vuln/detail/CVE-2021-29425)|Commons IO is being pulled in via AntiSamy, which pulls in Apache Batik-CSS. Batik-CSS is part of a larger Apache Xmlgraphics Batik family.Nothing in the Batik family of libraries uses org.apache.commons.io.FileNameUtils and neither ESAPI nor AntiSamy use Commons IO directly. Thus ESAPI is not affected by this CVE.|None. However may still show up in SCA output as AntiSamy using latest Apache Commons IO library version (2.6) that still support Java 7. AntiSamy 1.7 and later will require Java 8 as will ESAPI versions after 2.3.| +|[6](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin6.pdf)|Flaw in Log4J 1’s JSMAppender could cause insecure deserialization potentially leading to remote code execution.|[CWE-502](https://cwe.mitre.org/data/definitions/502.html)|[CVE-2021-4104](https://nvd.nist.gov/vuln/detail/CVE-2021-4104)|All versions of ESAPI are vulnerable and impacted if your application is doing all 3 of the following:1) Using the deprecated ESAPI Log4J logging.2) You have changed your default log4j.xml (or log4j.properties) file to use JMSAppender.3) An attacker is able to overwrite the contents of your Log4J 1 configuration file.|None. ESAPI uses ConsoleAppender as the default appender even if ESAPI logging is configured to use Log4J 1.| +|[7](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin7.pdf)|Improper validation (or, specifically, not using parameterized SQL queries) of a SQL statement makes Apache Log4j JDBCAppender vulnerable to SQL Injection. This potentially could allow attackers to execute unintended SQL statements by entering data that is logged via Log4J 1.|[CWE-89](https://cwe.mitre.org/data/definitions/89.html)|[CVE-2022-23305](https://nvd.nist.gov/vuln/detail/CVE-2022-23305)|All versions of ESAPI are vulnerable and impacted if your application is doing both of the following:1) Using the deprecated ESAPI Log4J logging.2) You have changed your default log4j.xml (or log4j.properties) file to use JDBCAppender.|None. ESAPI uses ConsoleAppender as the default appender even if ESAPI logging is configured to use Log4J 1.| +|[8](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin8.pdf)
[GHSA-q77q-vx4q-xx6q](https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-q77q-vx4q-xx6q)|Improper sanitization of user-controlled input permitted by an incorrect regular expression in an ESAPI configuration file can result in that input being unintentionally executing javascript: URLs, resulting in Cross-Site Scripting (XSS).|[CWE-79](https://cwe.mitre.org/data/definitions/79.html)|[CVE-2022-24891](https://nvd.nist.gov/vuln/detail/CVE-2022-24891)|A malformed regular expression in ESAPI’s default AntiSamy policy file, “antisamy-esapi.xml”, accidentally allowed the “:” character to match as a part of the “onsiteURL” regular expression. This allowed 'javascript:' pseudo-URIs to slip past ESAPI which could result in XSS vulnerabilities. Note that this vulnerability dates back at least to the ESAPI 1.4 release.|ESAPI 1.4 and all ESAPI 2.x versions before 2.3.0.0.| +|[9](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin9.pdf)|Apache Log4j 1’s JMSSink is vulnerable to insecure deserialization of untrusted logged data when the attacker has write access to the Log4j configuration or if the configuration references an LDAP service that the attacker has access to. This may resulting in remote code execution.|[CWE-502](https://cwe.mitre.org/data/definitions/502.html)|[CVE-2022-23302](https://nvd.nist.gov/vuln/detail/CVE-2022-23302)|Remote Code Execution is possible.|None. ESAPI uses ConsoleAppender as the default appender even if ESAPI logging is configured to use Log4J 1.| +|[10](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin10.pdf)|There is an RCE flaw caused by an insecure deserialization vulnerability in Apache Chainsaw, a Java-based GUI log viewer. CVE-2020-9493 identified a deserialization issue that was present in Apache Chainsaw 2.x prior to 2.1.0. However, prior to Chainsaw V2.0, Chainsaw was a component of Apache Log4j 1.2.x where the same issue exists and remains unfixed.|[CWE-502](https://cwe.mitre.org/data/definitions/502.html)|[CVE-2022-23307](https://nvd.nist.gov/vuln/detail/CVE-2022-23307)|Remote Code Execution is possible if you are running Apache Chainsaw 1.x from the Apache Log4J 1.2.x jar.|None. ESAPI uses ConsoleAppender as the default appender even if ESAPI logging is configured to use Log4J 1.| +|[GHSA-8m5h-hrqm-pxm2](https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-8m5h-hrqm-pxm2)|The default implementation of `Validator.getValidDirectoryPath(String, String, File, boolean)` may incorrectly treat the tested input string as a child of the specified parent directory. This potentially could allow control-flow bypass checks to be defeated if an attack can specify the entire string representing the 'input' path.|[CWE-22](https://cwe.mitre.org/data/definitions/22.html)|[CVE-2022-23457](https://nvd.nist.gov/vuln/detail/CVE-2022-23457)|Control-flow bypass may be possible.|ESAPI 2.x, prior to the ESAPI 2.3.0.0 release. Version 2.3.0.0 and later are patched.| +|[11](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin11.pdf)|There is a DoS vulerablity in the FileUploadBase class of Apache Commons FileUpload for releases prior to 1.5. That DoS vulnerability is caused by not limiting the number of files that could be uploaded per single request.|[CWE-770](https://cwe.mitre.org/data/definitions/770.html)|[CVE-2023-24998](https://nvd.nist.gov/vuln/detail/CVE-2023-24998)|None. ESAPI uses a subclass of the affected FileUpladBase abstract class from Apache Commons FileUpload to which a new setFileCountMax() method was added.|Addressed in ESAPI 2.5.2.0 and later.| +|[12](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin12.pdf)
[GHSA-r68h-jhhj-9jvm](https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-r68h-jhhj-9jvm)|Decribes why ESAPI's Validator.isValidSafeHTML is being deprecated and will be removed one year after the ESAPI 2.5.3.0 release date.|[CWE-80](https://cwe.mitre.org/data/definitions/80.html)|N/A (no CVE)|XSS may be possible depending on how the method is used.|All ESAPI versions (all 1.x and 2.x versions). No patch is available until the methods are deleted one year after the ESAPI 2.5.3.0 release date.| +|[13](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin13.pdf)|There is a bypass around ESAPI's Encoder.encodeForSQL interface (a method that always carried a strong warning) that be result in SQL injection vulnerabilities in code that use it.|[CWE-138](https://cwe.mitre.org/data/definitions/138.html)|[CVE-2025-5878](https://www.cve.org/CVERecord?id=CVE-2025-5878)|May leave applications that use Encoder.encodeForSQL vulnerable to SQL injection.|ESAPI 2.x versions before 2.7.0| diff --git a/ant-javadoc.xml b/ant-javadoc.xml deleted file mode 100644 index 2995a75b0..000000000 --- a/ant-javadoc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index e11ac64fa..2df6e7804 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -1,9 +1,9 @@ # # OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version # -# This file is part of the Open Web Application Security Project (OWASP) +# This file is part of the Open Worldwide Application Security Project (OWASP) # Enterprise Security API (ESAPI) project. For details, please see -# http://www.owasp.org/index.php/ESAPI. +# https://owasp.org/www-project-enterprise-security-api/ # # Copyright (c) 2008,2009 - The OWASP Foundation # @@ -66,9 +66,7 @@ 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.logging.java.JavaLogFactory # To use the new SLF4J logger in ESAPI (see GitHub issue #129), set # ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory # and do whatever other normal SLF4J configuration that you normally would do for your application. @@ -193,9 +191,6 @@ Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC # 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 @@ -222,37 +217,27 @@ Encryptor.EncryptionKeyLength=128 Encryptor.MinEncryptionKeyLength=128 # Because 2.x 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.3 -# 'fixed' is deprecated as of 2.2 -# and will be removed in 2.3. +# (All cipher modes except ECB require an IV.) Previously there were two choices: we can either +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. The +# former was deprecated in ESAPI 2.2 and removed in ESAPI 2.3. It was not secure +# because the Encryptor (as are all the other major ESAPI components) is a +# singleton and thus the same IV would get reused each time. It was not a +# well-thought out plan. (To do it correctly means we need to add a setIV() method +# and get rid of the Encryptor singleton, thus it will not happen until 3.0.) +# However, while the IV does not need to be hidden from adversaries, it is important that the +# adversary not be allowed to choose it. Thus for now, ESAPI just chooses a random IV. +# Originally there was plans to allow a developer to provide a class and method +# name to define a custom static method to generate an IV, but that is just +# trouble waiting to happen. Thus in effect, the ONLY acceptable property value +# for this property is "random". In the not too distant future (possibly the +# next release), I will be removing it, but for now I am leaving this and +# checking for it so a ConfigurationException can be thrown if anyone using +# ESAPI ignored the deprecation warning message and still has it set to "fixed". +# +# Valid values: random 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.) -# -# @Deprecated -- fixed IVs are deprecated as of the 2.2 release and support -# will be removed in the next release (tentatively, 2.3). -# If you MUST use this, at least replace this IV with one -# that your legacy application was using. -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 @@ -290,8 +275,14 @@ Encryptor.PlainText.overwrite=true Encryptor.HashAlgorithm=SHA-512 Encryptor.HashIterations=1024 -Encryptor.DigitalSignatureAlgorithm=SHA1withDSA -Encryptor.DigitalSignatureKeyLength=1024 + +# Was 'SHA1withDSA', but that won't support 2048 key sizes. Change back for +# backward compatibility. +Encryptor.DigitalSignatureAlgorithm=SHA256withDSA + +# Was 1024. Change this back if you require backward compatibility. +Encryptor.DigitalSignatureKeyLength=2048 +# SHA1 is fine as a CSRNG; no need to use anything else. Encryptor.RandomAlgorithm=SHA1PRNG Encryptor.CharacterEncoding=UTF-8 @@ -316,6 +307,7 @@ Encryptor.KDF.PRF=HmacSHA256 # 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 @@ -350,6 +342,30 @@ HttpUtilities.httpQueryParamValueLength=500 # File upload configuration HttpUtilities.ApprovedUploadExtensions=.pdf,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.rtf,.txt,.jpg,.png HttpUtilities.MaxUploadFileBytes=500000000 +# Maximum # of files that can be uploaded per HTTP request. +# Set to -1 for no maximum. Related to CVE-2023-24998. +HttpUtilities.MaxUploadFileCount=20 + +# Allowing anonymous users to do file uploads via HTTPUtilities.getFileUploads +# can make it easier for DoS attacks via uploading files easier. (See Security Bulletin #11, +# https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin11.pdf +# for details). +# +# By default, we allow anonymous users to upload files because we can only rely on +# ESAPI.authenticator().getCurrentUser() to determine if a user associated +# with the current HTTP session is authenticated and almost no one uses the +# ESAPI Authenticator because the reference implementation is just a toy +# implementation and is not enterprise scalable. +# +# If you are using the ESAPI Authenticator (the ESAPI reference implementation +# or you've implemented your own custom one), then you can set this property value +# to 'false' to disallow anonymous (i.e., unauthenticated) users to upload +# files. However, if you are not using the ESAPI Authenticator, then you should +# probably leave this set to 'false', otherwise you will completely prevent the +# use of HTTPUtilities.getFileUploads methods. +# +HttpUtilities.FileUploadAllowAnonymousUser=true + # 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. @@ -386,12 +402,14 @@ Logger.LogEncodingRequired=false 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 +# Determines whether ESAPI should log the user info. +Logger.UserInfo=true +# Determines whether ESAPI should log the session id and client IP. +Logger.ClientInfo=true +# Determines whether ESAPI should log the prefix of [EVENT_TYPE - APPLICATION NAME]. +# If all above Logger entries are set to false, as well as LogPrefix, then the output would be the same as if no ESAPI was used +Logger.LogPrefix=true #=========================================================================== # ESAPI Intrusion Detection @@ -470,7 +488,7 @@ Validator.Redirect=^\\/test.*$ 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.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]{0,1024}$ # Note that headerName and Value length is also configured in the HTTPUtilities section Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,256}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ @@ -483,7 +501,7 @@ Validator.HTTPJSESSIONID=^[A-Z0-9]{10,32}$ # Contributed by Fraenku@gmx.ch # Github Issue 126 https://github.com/ESAPI/esapi-java-legacy/issues/126 Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$ -Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$ +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.\\-_]*/?)*$ @@ -496,3 +514,72 @@ 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 + +# ~~~~~ Important Note ~~~~~ +# This is a workaround to make sure that a commit to address GitHub issue #509 +# doesn't accidentally break someone's production code. So essentially what we +# are doing is to reverting back to the previous possibly buggy (by +# documentation intent at least), but, by now, expected legacy behavior. +# Prior to the code changes for issue #509, if invalid / malicious HTML input was +# observed, AntiSamy would simply attempt to sanitize (cleanse) it and it would +# only be logged. However, the code change made ESAPI comply with its +# documentation, which stated that a ValidationException should be thrown in +# such cases. Unfortunately, changing this behavior--especially when no one is +# 100% certain that the documentation was correct--could break existing code +# using ESAPI so after a lot of debate, issue #521 was created to restore the +# previous behavior, but still allow the documented behavior. (We did this +# because it wasn't really causing an security issues since AntiSamy would clean +# it up anyway and we value backward compatibility as long as it doesn't clearly +# present security vulnerabilities.) +# More defaults about this are written up under GitHub issue #521 and +# the pull request it references. Future major releases of ESAPI (e.g., ESAPI 3.x) +# will not support this previous behavior, but it will remain for ESAPI 2.x. +# Set this to 'throw' if you want the originally intended behavior of throwing +# that was fixed via issue #509. Set to 'clean' if you want want the HTML input +# sanitized instead. +# +# Possible values: +# clean -- Use the legacy behavior where unsafe HTML input is logged and the +# sanitized (i.e., clean) input as determined by AntiSamy and your +# AntiSamy rules is returned. This is the default behavior if this +# new property is not found. +# throw -- The new, presumably correct and originally intended behavior where +# a ValidationException is thrown when unsafe HTML input is +# encountered. +# +#Validator.HtmlValidationAction=clean +Validator.HtmlValidationAction=throw + +# With the fix for #310 to enable loading antisamy-esapi.xml from the classpath +# also an enhancement was made to be able to use a different filename for the configuration. +# You don't have to configure the filename here, but in that case the code will keep looking for antisamy-esapi.xml. +# This is the default behaviour of ESAPI. +# +#Validator.HtmlValidationConfigurationFile=antisamy-esapi.xml + +######################################################################################## +# The following methods are now disabled in the default configuration and must +# be explicity enabled. If you try to invoke a method disabled by default, ESAPI +# will thrown a NotConfiguredByDefaultException. +# +# The reason for this varies, but ranges from they are not really suitable for +# enterprise scale to that are only marginally tested (if at all) versus the are +# unsafe for general use, although them may be fine when combined with other +# security-in-depth techiques. +# +# The disabled-by-default methods are: +# org.owasp.esapi.reference.DefaultEncoder.encodeForSQL +# org.owasp.esapi.ESAPI.accessController [FUTURE; will correspond to deprecation notice] +# +# Mote details to explain this may be found in the ESAPI GitHub wiki article at +# https://github.com/ESAPI/esapi-java-legacy/wiki/Reducing-the-ESAPI-Library's-Attack-Surface +########### +# The format is a comma-separated list of fully.Qualified.ClassName.methodName; +# all class names must begin with "org.owasp.esapi.". +ESAPI.dangerouslyAllowUnsafeMethods.methodNames= +########### +# Normally you would put some text here (that will be logged) that provides some +# justification as to why you have enabled these functions. This can be +# anythuing such as a Jira or ServiceNow ticket number, a security exception +# reference, etc. If it is left empty, it will just like "Justification: none".` +ESAPI.dangerouslyAllowUnsafeMethods.justification= diff --git a/configuration/esapi/antisamy-esapi.xml b/configuration/esapi/antisamy-esapi.xml index 14880d0b5..bcb53aed1 100644 --- a/configuration/esapi/antisamy-esapi.xml +++ b/configuration/esapi/antisamy-esapi.xml @@ -18,6 +18,7 @@ Slashdot allowed tags taken from "Reply" page: + @@ -31,9 +32,9 @@ Slashdot allowed tags taken from "Reply" page: space characters. --> - - - + + + @@ -168,325 +169,4 @@ Slashdot allowed tags taken from "Reply" page: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/configuration/esapi/validation.properties b/configuration/esapi/validation.properties index dd24e46c3..6a3363b03 100644 --- a/configuration/esapi/validation.properties +++ b/configuration/esapi/validation.properties @@ -14,7 +14,7 @@ # 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 +# canonicalization. It is preferable to use the "get" methods (which throw exceptions) # and use the returned user input which is in canonical form. Consider the following: # # try { diff --git a/configuration/esapi/waf-policy.xsd b/configuration/esapi/waf-policy.xsd index 4287e792b..1ddc99039 100644 Binary files a/configuration/esapi/waf-policy.xsd and b/configuration/esapi/waf-policy.xsd differ diff --git a/configuration/log4j.dtd b/configuration/log4j.dtd deleted file mode 100644 index 1aabd96c3..000000000 --- a/configuration/log4j.dtd +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/configuration/log4j.xml b/configuration/log4j.xml deleted file mode 100644 index 758514e80..000000000 --- a/configuration/log4j.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/documentation/ESAPI-configuration-user-guide.md b/documentation/ESAPI-configuration-user-guide.md index 72694bb05..420a4fb27 100644 --- a/documentation/ESAPI-configuration-user-guide.md +++ b/documentation/ESAPI-configuration-user-guide.md @@ -41,7 +41,7 @@ until these deprecated methods are removed, but it will be a minumum of 2 years or 1 major release [e.g., 3.x], whichever comes first. Also, we may not necessarily remove all of them at once, depending on community feedback.) -DefaultSecurityConfiguration implements the new contract. New contract methods implementations work as described in +DefaultSecurityConfiguration implements the new contract. New contract methods implementations work as described in 'Multiple configuration files support' paragraph. ## Multiple configuration files support @@ -49,7 +49,7 @@ DefaultSecurityConfiguration implements the new contract. New contract methods i EsapiPropertyManager is the new implementation for getting properties, which uses prioritized property loaders (each one associated with a specific configuration file). This allows to have multiple configuration files existing with priority connected to each one. At this moment, there are two configuration files possible to use, the path to them is set through following Java system properties: - + * org.owasp.esapi.opsteam = (higher priority config) * org.owasp.esapi.devteam = (lower priority config) @@ -86,9 +86,9 @@ ESAPI.securityConfiguration().getBooleanProp("propertyXXX"); where "propertyXXX" is some property name relevant to ESAPI (and in this case, one that would hold a boolean value). See ESAPI.properties for a list of current property names known to ESAPI. - + In above example, following happens: - + 1. org.owasp.esapi.opsteam configuration is used to get propertyXXX and return it as boolean. 2. If (1) fails to find property, org.owasp.esapi.devteam is used to get propertyXXX and return it as boolean. 3. If (2) fails to find property, ESAPI.properties is used to get propertyXXX and return it as boolean. diff --git a/documentation/ESAPI-release-steps.odt b/documentation/ESAPI-release-steps.odt new file mode 100644 index 000000000..848b88dae Binary files /dev/null and b/documentation/ESAPI-release-steps.odt differ diff --git a/documentation/ESAPI-release-steps.pdf b/documentation/ESAPI-release-steps.pdf new file mode 100644 index 000000000..ea6970f89 Binary files /dev/null and b/documentation/ESAPI-release-steps.pdf differ diff --git a/documentation/ESAPI-security-bulletin1.docx b/documentation/ESAPI-security-bulletin1.docx index de1cf53da..55cc3ba39 100644 Binary files a/documentation/ESAPI-security-bulletin1.docx and b/documentation/ESAPI-security-bulletin1.docx differ diff --git a/documentation/ESAPI-security-bulletin1.pdf b/documentation/ESAPI-security-bulletin1.pdf index b1ca473b0..ba2f68a83 100644 Binary files a/documentation/ESAPI-security-bulletin1.pdf and b/documentation/ESAPI-security-bulletin1.pdf differ diff --git a/documentation/ESAPI-security-bulletin10.odt b/documentation/ESAPI-security-bulletin10.odt new file mode 100644 index 000000000..87db70d4d Binary files /dev/null and b/documentation/ESAPI-security-bulletin10.odt differ diff --git a/documentation/ESAPI-security-bulletin10.pdf b/documentation/ESAPI-security-bulletin10.pdf new file mode 100644 index 000000000..90b84b442 Binary files /dev/null and b/documentation/ESAPI-security-bulletin10.pdf differ diff --git a/documentation/ESAPI-security-bulletin11.odt b/documentation/ESAPI-security-bulletin11.odt new file mode 100644 index 000000000..b1386c80d Binary files /dev/null and b/documentation/ESAPI-security-bulletin11.odt differ diff --git a/documentation/ESAPI-security-bulletin11.pdf b/documentation/ESAPI-security-bulletin11.pdf new file mode 100644 index 000000000..b553f69b9 Binary files /dev/null and b/documentation/ESAPI-security-bulletin11.pdf differ diff --git a/documentation/ESAPI-security-bulletin12.odt b/documentation/ESAPI-security-bulletin12.odt new file mode 100644 index 000000000..d1abf5d82 Binary files /dev/null and b/documentation/ESAPI-security-bulletin12.odt differ diff --git a/documentation/ESAPI-security-bulletin12.pdf b/documentation/ESAPI-security-bulletin12.pdf new file mode 100644 index 000000000..126f06751 Binary files /dev/null and b/documentation/ESAPI-security-bulletin12.pdf differ diff --git a/documentation/ESAPI-security-bulletin13.odt b/documentation/ESAPI-security-bulletin13.odt new file mode 100644 index 000000000..ee9cb8ef8 Binary files /dev/null and b/documentation/ESAPI-security-bulletin13.odt differ diff --git a/documentation/ESAPI-security-bulletin13.pdf b/documentation/ESAPI-security-bulletin13.pdf new file mode 100644 index 000000000..8d272b042 Binary files /dev/null and b/documentation/ESAPI-security-bulletin13.pdf differ diff --git a/documentation/ESAPI-security-bulletin2.odt b/documentation/ESAPI-security-bulletin2.odt new file mode 100644 index 000000000..793f0ffbf Binary files /dev/null and b/documentation/ESAPI-security-bulletin2.odt differ diff --git a/documentation/ESAPI-security-bulletin2.pdf b/documentation/ESAPI-security-bulletin2.pdf new file mode 100644 index 000000000..6925ad568 Binary files /dev/null and b/documentation/ESAPI-security-bulletin2.pdf differ diff --git a/documentation/ESAPI-security-bulletin3.odt b/documentation/ESAPI-security-bulletin3.odt new file mode 100644 index 000000000..8026a3936 Binary files /dev/null and b/documentation/ESAPI-security-bulletin3.odt differ diff --git a/documentation/ESAPI-security-bulletin3.pdf b/documentation/ESAPI-security-bulletin3.pdf new file mode 100644 index 000000000..98af7cfd3 Binary files /dev/null and b/documentation/ESAPI-security-bulletin3.pdf differ diff --git a/documentation/ESAPI-security-bulletin4.odt b/documentation/ESAPI-security-bulletin4.odt new file mode 100644 index 000000000..59c930f27 Binary files /dev/null and b/documentation/ESAPI-security-bulletin4.odt differ diff --git a/documentation/ESAPI-security-bulletin4.pdf b/documentation/ESAPI-security-bulletin4.pdf new file mode 100644 index 000000000..7a125584d Binary files /dev/null and b/documentation/ESAPI-security-bulletin4.pdf differ diff --git a/documentation/ESAPI-security-bulletin5.odt b/documentation/ESAPI-security-bulletin5.odt new file mode 100644 index 000000000..9edbd72d1 Binary files /dev/null and b/documentation/ESAPI-security-bulletin5.odt differ diff --git a/documentation/ESAPI-security-bulletin5.pdf b/documentation/ESAPI-security-bulletin5.pdf new file mode 100644 index 000000000..b3a215f05 Binary files /dev/null and b/documentation/ESAPI-security-bulletin5.pdf differ diff --git a/documentation/ESAPI-security-bulletin6.odt b/documentation/ESAPI-security-bulletin6.odt new file mode 100644 index 000000000..88d0c98a5 Binary files /dev/null and b/documentation/ESAPI-security-bulletin6.odt differ diff --git a/documentation/ESAPI-security-bulletin6.pdf b/documentation/ESAPI-security-bulletin6.pdf new file mode 100644 index 000000000..970d79d96 Binary files /dev/null and b/documentation/ESAPI-security-bulletin6.pdf differ diff --git a/documentation/ESAPI-security-bulletin7.odt b/documentation/ESAPI-security-bulletin7.odt new file mode 100644 index 000000000..6688b3b87 Binary files /dev/null and b/documentation/ESAPI-security-bulletin7.odt differ diff --git a/documentation/ESAPI-security-bulletin7.pdf b/documentation/ESAPI-security-bulletin7.pdf new file mode 100644 index 000000000..38d60ebda Binary files /dev/null and b/documentation/ESAPI-security-bulletin7.pdf differ diff --git a/documentation/ESAPI-security-bulletin8.odt b/documentation/ESAPI-security-bulletin8.odt new file mode 100644 index 000000000..2b73a5e2b Binary files /dev/null and b/documentation/ESAPI-security-bulletin8.odt differ diff --git a/documentation/ESAPI-security-bulletin8.pdf b/documentation/ESAPI-security-bulletin8.pdf new file mode 100644 index 000000000..646050e51 Binary files /dev/null and b/documentation/ESAPI-security-bulletin8.pdf differ diff --git a/documentation/ESAPI-security-bulletin9.odt b/documentation/ESAPI-security-bulletin9.odt new file mode 100644 index 000000000..68b1ba79a Binary files /dev/null and b/documentation/ESAPI-security-bulletin9.odt differ diff --git a/documentation/ESAPI-security-bulletin9.pdf b/documentation/ESAPI-security-bulletin9.pdf new file mode 100644 index 000000000..ed46ea87a Binary files /dev/null and b/documentation/ESAPI-security-bulletin9.pdf differ diff --git a/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.md b/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.md new file mode 100644 index 000000000..bc4956ab0 --- /dev/null +++ b/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.md @@ -0,0 +1,108 @@ +# GitHub Security Lab (GHSL) Vulnerability Report: `GHSL-2022-008` + +The [GitHub Security Lab](https://securitylab.github.com) team has identified a potential security vulnerability in [The OWASP Enterprise Security API](https://github.com/ESAPI/esapi-java-legacy). + +We are committed to working with you to help resolve this issue. In this report you will find everything you need to effectively coordinate a resolution of this issue with the GHSL team. + +If at any point you have concerns or questions about this process, please do not hesitate to reach out to us at `securitylab@github.com` (please include `GHSL-2022-008` as a reference). + +If you are _NOT_ the correct point of contact for this report, please let us know! + +## Summary + +`getValidDirectoryPath` incorrectly treats sibling of a root directory as a child. + +## Product + +The OWASP Enterprise Security API + +## Tested Version + +v2.2.3.1 (The latest version of ["Legacy" 2.x branch](https://github.com/ESAPI/esapi-java-legacy#what-does-legacy-mean) as [ESAPI 3.x](https://github.com/ESAPI/esapi-java) is in early development and has no releases yet.) + +## Details + +### Issue: `getValidDirectoryPath` bypass (`GHSL-2022-008`) + +`parent` [1] - the third parameter in [`getValidDirectoryPath`](https://github.com/ESAPI/esapi-java-legacy/blob/07dd60a8cc9edf0c872d68ae8ae84c70f008d3d8/src/main/java/org/owasp/esapi/reference/DefaultValidator.java#L447-L483) is used to validate that the `input` [2] path is "inside specified parent" directory [3]. + +```java +public String getValidDirectoryPath(String context, String input /* [2] */, File parent /* [1] */, 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() ) ) { // <---------- [3] + 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 ); + } +} +``` + +If the result of `parent.getCanonicalPath()` is not slash terminated it allows for partial path traversal. + +Consider `"/usr/outnot".startsWith("/usr/out")`. The check is bypassed although `outnot` is not under the `out` directory. +The terminating slash may be removed in various places. On Linux `println(new File("/var/"))` returns `/var`, but `println(new File("/var", "/"))` - `/var/`, however `println(new File("/var", "/").getCanonicalPath())` - `/var`. + +PoC (based on a unittest): +```java +Validator instance = ESAPI.validator(); +ValidationErrorList errors = new ValidationErrorList(); +assertTrue(instance.isValidDirectoryPath("poc", "/tmp/test2", new File("/tmp/test/"), false, errors)); +assertEquals(0, errors.size()); +``` + +#### Impact + +This issue allows to break out of expected directory. + +#### Remediation + +Consider using `getCanonicalFile().toPath().startsWith` to compare `Path`: + +```java +if ( !dir.getCanonicalFile().toPath().startsWith(parent.getCanonicalFile().toPath() ) ) +``` + +## GitHub Security Advisories + +We recommend you create a private [GitHub Security Advisory](https://help.github.com/en/github/managing-security-vulnerabilities/creating-a-security-advisory) for this finding. This also allows you to invite the GHSL team to collaborate and further discuss this finding in private before it is [published](https://help.github.com/en/github/managing-security-vulnerabilities/publishing-a-security-advisory). + +## Credit + +This issue was discovered and reported by GHSL team member [@JarLob (Jaroslav Lobačevski)](https://github.com/JarLob). + +## Contact + +You can contact the GHSL team at `securitylab@github.com`, please include a reference to `GHSL-2022-008` in any communication regarding this issue. + +## Disclosure Policy + +This report is subject to our [coordinated disclosure policy](https://securitylab.github.com/advisories#policy). diff --git a/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.pdf b/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.pdf new file mode 100644 index 000000000..c77efef05 Binary files /dev/null and b/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.pdf differ diff --git a/documentation/LoggerDesignAndTesting.md b/documentation/LoggerDesignAndTesting.md new file mode 100644 index 000000000..d3542a48a --- /dev/null +++ b/documentation/LoggerDesignAndTesting.md @@ -0,0 +1,27 @@ +(Gleaned from an email from Jeremiah J. Stacey to Kevin W. Wall on 2021-03-21. Some minor alterations were made for contextual understanding by Kevin Wall because the original email thread was not included.) + +The testing (for SLF4J logging at least) is tested with Mockito and Powermock. For the SLF4J logging, the tests are in Slf4JLoggerTest. It uses mocks to assert that the slf4j logging implementation gets the data we expect in the calls we support. + +I was very deliberate in the breakout of the classes to isolate specific functionality to enable this type of testing. At a high level, there are four classes that make up the logging structure. +I tried to encapsulate a subset of functionality into each one: + +**LogFactory** - Constructs Loggers to be used by clients. Responsible for building the LogBridge and the LevelHandler. + +**Logger** - The ESAPI interface implementation which uses the LogBridge and a delegate Logger to forward events to the underlying log implementation. + +**LogBridge** - Logical handler for determining the delegate handler for a known ESAPI log event, and forwarding the Log event to that handler. Also responsible for prefixing the client/server info content and applying the newline replacement behavior. + +**LogLevelHander** - This is actually where the log event gets sent to SLF4J! The Handler enumeration is assembled as part of a map in the static block of the LogFactory, and is used by the LogBridge to route a log event at a defined ESAPI log level to the correct API of the delegate Logger. + + +The general workflow is: + + LogFactory static block creates the LogPrefixAppender, LogScrubber, and LogBridge. + + LogFactory.getLogger(...) Creates Logger with the delegate slf4j logger implementation and the LogBridge. + + Logger.info/warn/etc(message) -> forwards to LogBridgelog(logger, esapiLevel, type, message) -> forwards to LogHandler.log(...) -> forwards to slf4j Logger implementation with appropriate level and composed message. + +So each of the tests for each of the classes verifies data in -> data out based on the Logging API. The structure for JUL and SLF4J are almost identical. There are a few differences in the interaction with the underlying Logger interactions and expectations. As a result, the tests are also almost full duplications (again accounting for differences in the underlying logging API). + +-J diff --git a/documentation/esapi4java-2.0-readme.txt b/documentation/esapi4java-2.0-readme.txt index 7f568fb06..7fd25e068 100644 --- a/documentation/esapi4java-2.0-readme.txt +++ b/documentation/esapi4java-2.0-readme.txt @@ -7,7 +7,7 @@ Here are the most significant directories and files included the zip file for th File / Directory Description ========================================================================================= -/ +/ | +---configuration/ Directory of ESAPI configuration files | | diff --git a/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt b/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt index 49accdbfc..35290f5e6 100644 --- a/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt +++ b/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt @@ -1,4 +1,4 @@ -This release includes critical changes to the ESAPI Log4JLogger that will now allow you to over-ride the user specific +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: @@ -23,8 +23,8 @@ 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 +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(); @@ -40,7 +40,7 @@ if ( request != null ) { } } -In fact, here is the entire original getUserInfo() implementation (that was tied to the ESAPI request and user object) – +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() { @@ -58,14 +58,14 @@ public String getUserInfo() { } } } - + // log user information - username:session@ipaddr - User user = ESAPI.authenticator().getCurrentUser(); + 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-core-2.0-ciphertext-serialization.pdf b/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf index 9d1dc3399..37d68a467 100644 Binary files a/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf 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 index 7f3106ea0..86b7de7a0 100644 Binary files a/documentation/esapi4java-core-2.0-ciphertext-serialization.xls and b/documentation/esapi4java-core-2.0-ciphertext-serialization.xls differ diff --git a/documentation/esapi4java-core-2.0-readme-crypto-changes.html b/documentation/esapi4java-core-2.0-readme-crypto-changes.html index 8687f5ca6..db403b9c9 100644 --- a/documentation/esapi4java-core-2.0-readme-crypto-changes.html +++ b/documentation/esapi4java-core-2.0-readme-crypto-changes.html @@ -63,7 +63,7 @@

Symmetric Encryption in ESAPI 2.0rc1 and 2.0rc2

always encrypt to the same ciphertext block, thus revealing patterns in the plaintext input. For example, these images from Wikipedia's Block -cipher modes of operation illustrate this point well: +cipher modes of operation illustrate this point well:

@@ -92,7 +92,7 @@

Symmetric Encryption in ESAPI 2.0rc1 and 2.0rc2

Ciphertext encrypted with ECB cipher mode are also subject to "block replay attacks". See Bruce Schneier's Applied Cryptography: protocols, algorithms, and source code for -details. +details.

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 @@ -123,7 +123,7 @@

Problems with Symmetric Encryption in ESAPI 2.0-rc1 and 2.0-rc2

The Encryption Changes in ESAPI 2.0-rc3 and Later

Briefly speaking, the changes being implemented for ESAPI Java 2.0 -are: +are:

  1. Starting in ESAPI Java 2.0-rc3, @@ -156,7 +156,7 @@

    The Encryption Changes in ESAPI 2.0-rc3 and Later

    response was deafening. There literally was but a single response and that was to kill off LegacyJavaEncryptor. (By this time, the two symmetric encryption interfaces in Encryptor - had already been deprecated.) + had already been deprecated.)

  2. The byte-encoding has been changed from native byte encoding to UTF-8 byte-encoding throughout ESAPI 2.0 and not just for @@ -167,7 +167,7 @@

    The Encryption Changes in ESAPI 2.0-rc3 and Later

    guaranteed.

The Good, the Bad, and the Ugly

-

Or put another way, there are always trade-offs to be made... +

Or put another way, there are always trade-offs to be made...

The Good

We get improved security by encouraging the use of stronger cipher @@ -205,9 +205,9 @@

The Bad

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. +observe the results of the ensuing decryption attempt.

-

So that leaves two choices for the IV: +

So that leaves two choices for the IV:

Likewise, the use of padding is going to add some overhead to the @@ -360,7 +360,7 @@

The Bad

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. +IV would be 16 bytes for AES and 8 bytes for most other ciphers.

The Ugly

Well, so far, this "bad" news may be bad for you but @@ -370,7 +370,7 @@

The Ugly

But wait Skippy, don't go running off just quite yet. As Robert Heinlein wrote in his 1966 novel The Moon is a Harsh Mistress "There ain't no such thing as a free lunch". (Some of us -more hardened cynics know it more commonly as TANSTAAFL.) +more hardened cynics know it more commonly as TANSTAAFL.)

As mentioned earlier, backward compatibility with ESAPI 1.4 (originally planned via LegacyJavaEncryptor) has been @@ -395,11 +395,11 @@

The Ugly

complexity of handling the ciphertext result from encryption operations. And then there are new encryption and decryption methods for the Encryptor interface. Specifically, the encrypt -and decrypt methods have been generalized as: +and decrypt methods have been generalized as:

CipherText encrypt(SecretKey key, PlainText plaintext)
         throws EncryptionException;

-and +and

PlainText decrypt(SecretKey key, CipherText ciphertext)
         throws EncryptionException

@@ -409,7 +409,7 @@

The Ugly

based on Encryptor.MasterKey.)

The two existing interfaces from ESAPI 1.4 and earlier:

String encrypt(String plaintext) throws EncryptionException

-and +and

String decrypt(String ciphertext) throws EncryptionException

are still supported but have been deprecated, mainly because diff --git a/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html b/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html index 2d99ee870..19298d4c2 100644 --- a/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html +++ b/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html @@ -149,8 +149,9 @@

ESAPI.properties Properties Relevant to Symmetric Encryption

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. - NOTE: "fixed" is deprecated and will be removed by - release 2.3. + NOTE: "fixed" had been deprecated since 2.2.0.0 and finally + was removed for release 2.3.0.0. Using it in versions 2.3.0.0 or + later will result in a ConfigurationException being thrown.

CAUTION: While it is not required that the IV be kept secret, encryption relying on fixed IVs can lead to a known @@ -352,7 +353,7 @@

ESAPI.properties Properties Relevant to Symmetric Encryption

How the Old (Deprecated) Methods Were Used

To encrypt / decrypt using the String-based, deprecated methods carried over from ESAPI 1.4, code similar to the following would be -used. +used.

    String myplaintext = "My plaintext";
     try {
@@ -410,10 +411,10 @@ 

Encrypting / Decrypting with the New Methods -- The Simple Usage

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. +as to the details of the encryption.

A code snippet using the new methods that use the master -encryption key would look something like this: +encryption key would look something like this:

    String myplaintext = "My plaintext";
     try {
@@ -431,7 +432,7 @@ 

Encrypting / Decrypting with the New Methods -- The Simple Usage

mode is chosen.

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 +array with the old deprecated method, one would first have to use

    byte[] plaintextByteArray = { /* byte array to be encrypted */ };
     String plaintext = new String(plaintextByteArray, "UTF-8");

@@ -540,7 +541,7 @@

Encrypting / Decrypting with the New Methods

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. +key for both recipients.

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 @@ -552,14 +553,14 @@

Encrypting / Decrypting with the New Methods

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.)

-

The following illustrates how these new methods might be used. +

The following illustrates how these new methods might be used.

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.) +to securely email them, etc.)

    // Generate two random, 128-bit AES keys to be distributed out-of-band.
     import javax.crypto.SecretKey;
@@ -586,10 +587,10 @@ 

Encrypting / Decrypting with the New Methods

Second, these keys would be printed out and stored somewhere secure by our application, perhaps using something like ESAPI's EncryptedProperties class, where they could later be -retrieved and used. +retrieved and used.

In the following code, we assume that the SecretKey -values have already been initialized elsewhere. +values have already been initialized elsewhere.

    SecretKey bankAcctKey = ...;        // These might be read from EncryptedProperties
     SecretKey credCardKey = ...;        // or from a restricted database, etc.
@@ -801,7 +802,7 @@ 

Acknowledgments

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 - + Analysis of ESAPI 2.0's Key Derivation Function

diff --git a/documentation/esapi4java-core-2.1-release-notes.txt b/documentation/esapi4java-core-2.1-release-notes.txt index d84097d80..e97e56c93 100644 --- a/documentation/esapi4java-core-2.1-release-notes.txt +++ b/documentation/esapi4java-core-2.1-release-notes.txt @@ -8,7 +8,7 @@ ESAPI for Java - 2.1.0 Release Notes 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. @@ -64,5 +64,5 @@ NOTE: A follow-up patch release is scheduled within the next few months to 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 , 2013-08-30 diff --git a/documentation/esapi4java-core-2.2.0.0-release-notes.txt b/documentation/esapi4java-core-2.2.0.0-release-notes.txt index b13e3c192..b43efc743 100644 --- a/documentation/esapi4java-core-2.2.0.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.0.0-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.2.0.0 - Release date: 2019-MMM-DD + Release date: 2019-June-24 Project leaders: -Kevin W. Wall -Matt Seil @@ -16,6 +16,7 @@ Executive Summary: Important Things to Note for this Release * Known vulnerabilities still not addressed: - There is this critical CVE in log4j 2.x before 2.8.2: CVE-2017-5645. It is a Java deserialization vulnerability that can lead to arbitrary remote code execution. Some private vulnerability databases claim that this same vulnerability is present in log4j 1.x even though the CVE itself does not claim that. However, examination of this CVE shows that the vulnerability is associated with implementations of TcpSocketServer and UdpSocketServer, which implement fully functional socket servers that can be used to listen on network connections and record log events sent to server from various client applications. For ESAPI to be vulnerable to that, first someone would have to have an implementation of wone of those servers running and secondly, they would have to change ESAPI's log4j.xml configuration file so that it uses log4j's SocketAppender rather than the default ConsoleAppender that ESAPI's default deployment uses. Thus even if this vulnerability were present in log4j 1.x, ESAPI's use of ConsoleAppender makes it a non-issue. - There is a known and unpatched vulnerability in the SLF4J Extensions that some vulnerability scanners may pick up and associate with ESAPI's use of slf4j-api-1.7.25.jar. (Note that OWASP Dependency Check does NOT flag this vulnerability [CVE-2018-8088], but others may.) According to NVD, this vulnerability is associated with "org.slf4j.ext.EventData in the slf4j-ext module in QOS.CH SLF4J before 1.8.0-beta2". Fortunately, I have confirmed that this Java deserialization does not impact ESAPI. First off, the default configuration of ESAPI.properties does not use SLF4J, but even if an application should choose to use it, ESAPI does not include the slf4j-ext jar and it has been confirmed that the vulnerable class (org.slf4j.ext.EventData) is not included in the slf4j-api jar that ESAPI does. Unfortunately, this CVE is not patched in the latest SLF4J packages, so even if we were to update it to latest version (currently 1.80-beta2, as of 12/31/2018), any scanners that associate ESAPI with CVE-2018-8088 would still have this false positive. But the important thing to ESAPI users is to know that if this CVE is identified for ESAPI, that it is a false positive. + - There is a recently discovered issue (see https://app.snyk.io/vuln/SNYK-JAVA-COMMONSBEANUTILS-30077) that is related to CVE-2014-0114 that is a Java deserialization issue in Apache Commons BeanUtils 1.9.3 that can lead to remote command execution attacks. This had been fixed in 1.9.2, but apparently they missed a place where the fix was needed. A GitHub commit (https://github.com/apache/commons-beanutils/pull/7/commits/2780a77600e6428b730e3a5197b7c5baf1c4cca0) has been made to mster branch of the BeanUtils repo, but thus far, no official patch has been released. ESAPI only uses BeanUtils in its AccessController (specifically, DynaBeanACRParameter class), where it has a dependency on org.apache.commons.beanutils.LazyDynaMap. Based on the BeanUtils commit, the fix was in org.apache.commons.beanutils2.PropertyUtilsBean. Based on a cursory examination, the ESAPI team does not believe that this vulnerability reported by Snyk is exploitable given that manner that it is used within ESAPI, or if it is, it is not externally exploitable based on the default access control rules that are provided with ESAPI. However, the ESAPI team will be watching for an official patch to Apache Commons BeanUtils and we will release a patched version of ESAPI as patch point release once a patch is officially available in Maven Central. - Otherwise, ESAPI 2.2.0.0 addresses all know CVEs except for CVE-2013-5960 (which I have fixed in a private BitBucket repo, but getting it to be backward compatible is proving to be more difficult than anticipated.) Besides, if you want to use encryption in Java, I'd highly recommend using Google Tink, which is much more fully featured than ESAPI. (Tink allows key rotation, storing keys in various cloud HSMs, etc.) @@ -25,14 +26,14 @@ It was mainly because of these first two bullet items above that we bumped the r Basic ESAPI facts ESAPI 2.1.0.1 release: - 177 source files - 1547 Junit tests + 177 Java source files + 1547 Junit tests in 88 Java source files ESAPI 2.2.0.0 release: - 194 source files - 4140 JUnit tests!!!!! + 194 Java source files + 4150 JUnit tests in 118 Java source files -That's 2593 NEW tests!!! +That's 2603 NEW tests since the 2.1.0.1 release!!! GitHub Issues fixed in this release [i.e., since 2.1.0.1 release on 2016-Feb-05] @@ -45,7 +46,7 @@ Issue # GitHub Issue Title 37 RandomAccessReferenceMap.update() can randomly corrupt the map 71 java.lang.ExceptionInInitializerError in 2.0 version 129 Add Logging support for SLF4J -157 minimum-config deployment fails +157 minimum-config deployment fails 188 SecurityWrapperRequest seems to mishandle/swallow allowNull argument 209 Build an encoding function specific to HTTP/Response Splitting (tactical remediation) 213 Provide a taglib descriptor (.tld file) @@ -85,6 +86,7 @@ Issue # GitHub Issue Title 301 encodeForHTMLAttribute escapes the forward slash 302 HTMLEntityCodec#decode incorrectly decodes upper-case accented letters as their lower-case counterparts 303 HTMLEntityCodec destroys 32-bit CJK (Chinese, Japanese and Korean) characters +304 encodeForCSS breaks color values 305 ClassCastException when using ESAPI logger 307 Issue with decodeFromURL method in the DefaultEncoder 308 AuthenticatedUser isCredentialsNonExpired() have todo comment, but default return false; @@ -114,7 +116,7 @@ Issue # GitHub Issue Title 386 Avoid using System.err in EsapiPropertyManager 387 'mvn site' fails for FindBugs report, causing 'site' goal to fail 389 Provide an option for the encodeForLDAP method to not encode wildcard characters -394 Refactor Validator.getCanonicalizedUri into Encoder. +394 Refactor Validator.getCanonicalizedUri into Encoder. 395 Issues when I am passing htttp://localhost:8080/user=admin&prodversion=no 396 Trust Boundary Violation - while triggering veracode 397 Update Resource path search to maintain legacy behavior in DefaultSecurityConfiguration.java @@ -126,7 +128,7 @@ Issue # GitHub Issue Title 417 Add additional protection against CVE-2016-1000031 422 Inconsistent dependency structure and vulnerable xml (xerces, xalan, xml-apis ...) dependencies 424 issue with Filename encoding for executeSystemCommand -425 Project build error: Non-resolvable parent POM for org.owasp.esapi:esapi:2.1.0.2-SNAPSHOT: Could not transfer artifact +425 Project build error: Non-resolvable parent POM for org.owasp.esapi:esapi:2.1.0.2-SNAPSHOT: Could not transfer artifact 427 HTTP cookie validation rules too restrictive? 429 Miscellaneous updates to pom.xml 432 ESAPI.properties not found. @@ -138,7 +140,7 @@ Issue # GitHub Issue Title 442 Remove deprecated fields in Encoder interface 444 Delete deprecated method Base64.decodeToObject() and related methods 445 A bunch of dependencies are out of date , I will list them below with the associated vulnerability -447 can't generate MasterKey / MasterSalt +447 can't generate MasterKey / MasterSalt 448 Clean up pom.xml 454 about code eclipse formatter template question 455 New release for mitigation of CVEs @@ -147,11 +149,17 @@ Issue # GitHub Issue Title 462 Allow configurable init parameter in ESAPIFilter for unauthorized requests 463 Create release notes for next ESAPI release 465 Update both ESAPI.properties files to show comment for ESAPI logger support for SLF4J -471 Bump ESAPI release # to 2.2.0.0 -476 DefaultValidator.getValidInput implementation ignores 'canonicalize' method parameter -478 Remove obsolete references to Google Code in pom.xml and any other release prep -483 More miscellaneous prep work for ESAPI 2.2.0.0 release -485 Update Maven dependency check plugin to 5.0.0-M2 +471 Bump ESAPI release # to 2.2.0.0 +476 DefaultValidator.getValidInput implementation ignores 'canonicalize' method parameter +478 Remove obsolete references to Google Code in pom.xml and any other release prep +482 ESAPI 2.2.0.0 release date? +483 More miscellaneous prep work for ESAPI 2.2.0.0 release +485 Update Maven dependency check plugin to 5.0.0-M2 +488 Missed a legal input case in DefaultSecurityConfiguration.java +492 Release candidates on maven central +493 wrong regex validation +499 ValidatorTest.isValidDirectoryPath() has tests that fail under Windows if ESAPI tests run from different drive where Windows installed +500 Suppress noise from ESAPI searching for properties and stop ignoring important IOExceptions ----------------------------------------------------------------------------- @@ -160,7 +168,7 @@ Issue # GitHub Issue Title * Various deprecated methods were _actually_ deleted! This could break existing application code. - 442 Remove deprecated fields in Encoder interface + Issue 442 Remove deprecated fields in Encoder interface Specifically, if you are using any of these previously deprecated fields from the Encoder interface, you need to update your application code to refer insteat to the constances from org.owasp.esapi.EncoderConstants: @@ -176,7 +184,7 @@ Issue # GitHub Issue Title public final static char[] CHAR_PASSWORD_SPECIALS = EncoderConstants.CHAR_PASSWORD_SPECIALS; public final static char[] CHAR_PASSWORD_LETTERS = EncoderConstants.CHAR_PASSWORD_LETTERS; - 444 Delete deprecated method Base64.decodeToObject() and related methods + Issue 444 Delete deprecated method Base64.decodeToObject() and related methods Specifically, the following methods were removed from the org.owasp.esapi.codecs.Base64 class. If you will using any of these methods, you likely already had vulnerabilities in your application code. If any of these methods were being used, you will need to rewrite your application code: @@ -184,11 +192,11 @@ Issue # GitHub Issue Title public static String encodeObject( java.io.Serializable serializableObject, int options ) public static Object decodeToObject( String encodedObject ) - 483 More miscellaneous prep work for ESAPI 2.2.0.0 release + Issue 483 More miscellaneous prep work for ESAPI 2.2.0.0 release Specifically, CipherText.getSerialVersionUID() and DefaultSecurityConfiguration.MAX_FILE_NAME_LENGTH have actually been deleted from the ESAPI code base. For the former, use CipherText.cipherTextVersion() instead. For the latter, there is no replacement. (This wasn't being used, but it was set to 1000 in case you're wondering.) - + * Various properties in ESAPI.properties were changed in a way that might affect your application: - 439 Tighten ESAPI defaults to disallow dubious file suffixes + Issue 439 Tighten ESAPI defaults to disallow dubious file suffixes Specifically, the property HttpUtilities.ApprovedUploadExtensions changed from 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 @@ -212,16 +220,28 @@ Issue # GitHub Issue Title Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$ (Left as an exercise for the reader to figure out what exactly this means. ;-) Validator.HTTPURI: Changed to be much more restrictive; i.e., changed from: - Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ + Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ to: Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$ - + +* Other changes: + Issue 500 Suppress noise from ESAPI searching for properties and stop ignoring important IOExceptions + + Fixing this required changes to the CTORs of the following classes: + + org.owasp.esapi.configuration.EsapiPropertyManager + org.owasp.esapi.configuration.AbstractPrioritizedPropertyLoader + org.owasp.esapi.configuration.EsapiPropertyLoaderFactory + org.owasp.esapi.configuration.StandardEsapiPropertyLoader + org.owasp.esapi.configuration.XmlEsapiPropertyLoader + + These CTORs now explicitly throw IOException if the specified ESAPI property file is not found or not readable. Note that this should not affect most people as most use DefaultSecurityCOnfigurator and it still only throws ConfigurationException. (IOExceptions from these other classes are caught and rethrow as ConfigurationException.) Use of these classes directly should be very rare. ----------------------------------------------------------------------------- Other changes in this release, some of which not tracked via GitHub issues -* Updated minimal version of Maven from 3.0 to 3.1 required to build ESAPI. +* Updated minimal version of Maven from 3.0 to 3.1 required to build ESAPI. * Miscellaneous minor javadoc fixes and updates. * Added the Maven plug-in for OWASP Dependency Check so 3rd party dependencies can be kept up-to-date. * Updated .gitignore file with additional files to be ignored. @@ -305,25 +325,58 @@ List of all PRs closed since 2.1.0.1 (2016-Feb-05) - #472 by jeremiahjstacey was merged on Jan 21, 2019 -- Issue #31 MySQLCodec Updates #475 by jeremiahjstacey was merged on Jan 27, 2019 -- Issue #188 resolution proof: Test updates #477 by jeremiajjstacey was merged on Feb 02, 2019 -- $476 DefaultValidator.getValidInput uses canonicalize method argument +#487 by kwwall was merged on Apr 29, 2019 -- Master branch updates for ESAPI-2.2.0.0-RC2 +#490 by hellyguo was closed on May 12, 2019 -- enhance: cache class and method to avoid reading each time +#491 by hellyguo was merged on May 27, 2019 -- enhance: improve the performance of ObjFactory + -List of contributors of *merged* PRs, listed (rather naively) by # or merged PRs: +List of contributors of *merged* PRs, listed (rather naively) by # of merged PRs: # merged PRs GitHub ID ------------------------- 19 xeno6696 10 jeremiahjstacey - 8 kwwall + 9 kwwall 2 artfullyContrived 2 augustd 2 JoelRabinovitch 1 drm2 + 1 hellyguo 1 jackycct 1 mickilous 1 NiklasMehner 1 simon0117 1 sunnypav - -Thanks you all for your time and effort to ESAPI and making it a better project. +Developer Activity Report (Changes between release 2.1.0.1 and 2.2.0.0, i.e., between 2015-02-05 and 2019-06-09 ) +As created by 'mvn site', however this data was slighty edited to remove email ids replace them with GitHub ids when those were known, or with the developer name. +Sorted first by # of commits and then by developer id / name.. + +Developer Total commits Total Number + of Files Changed +===================================================== +kwwall 362 351 +xeno6696 64 82 +jeremiahjstacey 55 68 +davewichers 7 49 +Anthony Musyoki 4 2 +Kad DEMBELE 4 2 +augustd 3 7 +drmyersii 2 2 +JoelRabinovitch 2 4 +Ben Sleek 1 1 +chrisisbeef 1 5 +hellyguo 1 3 +Jackycct 1 2 +mickilous 1 2 +NiklasMehner 1 2 +Pavan Kumar 1 1 +simon0117 1 3 +taringamberini 1 1 +===================================================== +Totals: 512 399 (unique files changed) + + +Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Appendix: Dependency Updates (as reflected in pom.xml) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt new file mode 100644 index 000000000..e32e0ad8c --- /dev/null +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -0,0 +1,316 @@ +Release notes for ESAPI 2.2.1.0 + Release date: 2020-July-12 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.0.0, 2019-June-24 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +This is a minor release. It's main purpose was to update dependencies to eliminate potential vulnerabilities arising from dependencies with known CVEs. See the section "Changes requiring special attention" below for additional details. + +Also special props to Bill Sempf for stepping up and volunteering to prepare the initial cut of these release notes. Had he not done so, this release either would not have release notes or it would have been delayed another 6 months while I procrastinated further with various distractions. (Squirrel!) + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.0.0 release: + 194 Java source files + 4150 JUnit tests in 118 Java source files + +ESAPI 2.2.1.0 release: + 211 Java source files + 4309 JUnit tests in 134 Java source files + +39 GitHub Issues closed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +143 - Enhance encodeForOS to auto-detect the underling OS +173 - DOMConfigurator is being used inappropriately in the ESAPIWebApplicationFirewallFilter +226 - Javadoc Inaccuracy in getRandomInteger() and getRandomReal() +232 - SecurityWrapperResponse.createCookieHeader modification request (closed; marked 'wontfix') +235 - exception is java.lang.NoClassDefFoundError: org.owasp.esapi.codecs.Codec +245 - KeyDerivationFunction::computeDerivedKey - possible security level mismatch +256 - Whitespace in JavaEncryptor +263 - I am getting validation exception while validating a parameter coming from http request +268 - SecurityWrapperResponse setStatus should not always set SC_OK +269 - org.owasp.esapi.reference.DefaultValidator reports ValidationException with IE 9 +271 - Add Constructor to DefaultSecurityConfiguration to accept a properties file (1.4) +276 - Patch for /branches/2.1/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java +310 - Make HTMLValidationRule to look for antisamy-esapi.xml in classpaths enhancement +382 - Build Fails on path with space +465 - Update both ESAPI.properties files to show comment for ESAPI logger support for SLF4J +488 - Missed a legal input case in DefaultSecurityConfiguration.java +494 - Encoder's encodeForCSS doesn't handle RGB Triplets +495 - Maven Install Requires GPG Key +499 - ValidatorTest.isValidDirectoryPath() has tests that fail under Windows if ESAPI tests run from different drive where Windows installed +500 - Suppress noise from ESAPI searching for properties and stop ignoring important IOExceptions +503 - Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` +509 - HTMLValidationRule.getValid(String,String) does not follow documented specifications +511 - Add missing documentation to Validator.addRule() and Validator.getRule() +512 - Update Apache Commons Bean Utils to 1.9.4 +515 - NullPointerException can result from line 163 of SecurityWrapperRequest.java: +521 - JUnit test ValidatorTest.testIsValidSafeHTML() now failing +522 - javadoc corrections for Encoder.canonicalize() +523 - Links in README to users list and dev list are reversed +527 - Configuration flag for disabling Logger User and App Information +530 - Apply default logging content to SLF4J messages +532 - Update JUL and Log4J code structure and workflow to match SLF4J +536 - SecurityWrapperResponse setHeader error message is unclear +538 - Addressing log4j 1.x CVE-2019-17571 +542 - Write up ESAPI release notes for planned 2.2.1.0 release +552 - Rewrite implementation of some ESAPI classes to remove Java 8 dependencies +554 - CryptoHelper.arrayCompare() fails with NPE under Java 7 when one of the arguments is null +555 - JUnit test org.owasp.esapi.reference.AccessControllerTest.testIsAuthorizedForData fails when run against Java 7 on Linux +556 - Major overhaul to ESAPI Encoder Javadoc based on ESAPI Encoder Usability Study +558 - ValidatorTest.testIsValidDirectoryPath() JUnit test fails under MacOS + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +Related to that CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is not affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + +Notable dependency updates (excludes those only used with JUnit tests): + antiSamy 1.5.8 -> 1.5.10 + batik-css 1.11 -> 1.13 + commons-beansutil 1.9.3 -> 1.9.4 + slf4j-api 1.7.26 -> 1.7.30 + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) + +----------------------------------------------------------------------------- + + Known Issues / Problems + +----------------------------------------------------------------------------- +If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + +I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html +and + https://github.com/bcgit/bc-java/issues/477 +I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + + +Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + +We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + + + *** IMPORTANT WORKAROUND for 2.2.1.0 ESAPI Logging *** + +Lastly, if you try to use the new ESAPI 2.2.1.0 logging, you will notice that you need to change ESAPI.Logger and also possibly provide some other logging properties as well. This is because the logger packages were reorganized to improve maintainability, but we failed to mention it. To use ESAPI logging in ESAPI 2.2.1.0 (and later), you MUST set the ESAPI.Logger property to one of: + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *must* supply an "esapi-java-logging.properties" file in your classpath. Unfortunately, we failed to drop add that to the ESAPI configuration jar under the GitHub 'Releases', so this file has been added explicitly to the 2.2.1.0 release 'Assets' for this release (for details, see https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.1.0). Even worse, there was a logic error in the static initializer of JavaLogFactory (now fixed in the 2.2.1.1 patch release) that causes a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory or Slf4JLogFactory, you will also want to ensure that you have the following ESAPI logging properties set to get the logs to appear what you are used to with Log4J 1.x 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 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +Documentation updates for locating Jar files +Unneeded code removed from ExtensiveEncoderURI test +Inline reader added to ExtensiveEncoder +Additional time for windows to always sleep more than given seconds in CryptoTokenTest +Change required by tweak to CipherText.toString() method +Removed call to deprecated CryptoHelper.computeDerivedKey() method +New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class +Miscellaneous documentation and tests +JavaLogger moved to new package +Log4J 1.x no longer ESAPI's default logger + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-07-11) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +davewichers 2 1 0 +HJW8472 11 8 1 +jeremiahjstacey 78 70 6 +kwwall 67 64 8 +Michael-Ziluck 3 2 2 +sempf 1 1 1 +wiitek 6 4 2 +xeno6696 3 5 1 +======================================================== + Total: 21 + + +----------------------------------------------------------------------------- + + +21 Closed PRs merged since 2.2.0.0 release (those rejected not listed) +====================================================================== +PR# GitHub ID Description +---------------------------------------------------------------------- +504 -- kwwall -- New scripts to suppress noise for 'mvn test' +505 -- kwwall -- Close issue #256. White-space clean up. +506 -- kwwall -- Closes Issue 245 +508 -- Michael-Ziluck -- Resolves #226 - Corrected docs for the bounded, numeric, random methods +510 -- Michael-Ziluck -- Resolve #509 - Properly throw exception when HTML fails +513 -- kwwall -- Close issue #512 by updating to 1.9.4 of Commons Beans Util. +514 -- xeno6696 -- Fixed issues #503 by writing a new addReferer method, also temporarily… +516 -- jeremiahjstacey -- Issue 515 +518 -- jeremiahjstacey -- Issue #511 Copying Docs from DefaultValidator +519 -- jeremiahjstacey -- Issue 494 CSSCodec RGB Triplets +520 -- jeremiahjstacey -- OS Name DefaultExecutorTests #143 +533 -- jeremiahjstacey -- #532 JUL and Log4J match SLF4J class structure and Workflow +535 -- kwwall -- Issue 521 +537 -- jeremiahjstacey -- Issue 536 +539 -- wiiitek -- upgrade for convergence +540 -- wiiitek -- Issue 382: Build Fails on path with space +541 -- HJW8472 -- Fixed issue #310 +543 -- sempf -- Release notes for 2.2.1.0 +551 -- kwwall -- Misc cleanup +553 -- kwwall -- Fix for GitHub Issue 552 +557 -- kwwall -- Final prep for 2.2.1.0 release + + +CHANGELOG: Create your own. May I suggest: + + git log --since=2019-06-25 --reverse --pretty=medium + + which will show all the commits since just after the last (2.2.0.0) release. + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.1.0 + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.1.0 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.5.10:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.12:compile + [INFO] | | \- org.apache.httpcomponents:httpcore:jar:4.4.13:compile + [INFO] | \- commons-codec:commons-codec:jar:1.14:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.13:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xerces:xercesImpl:jar:2.12.0:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.0.4:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.65.01:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + + Release notes written by Bill Sempf (bill.sempf@owasp.org), but please direct any communication to the project leaders. + +Special shout-outs to: + Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire and for refactoring ESAPI loggers. + Dave Wichers (davewichers) - for several extremely useful pom.xml improvements. + Bill Sempf (sempf) -- for these release notes. Awesome job, Bill. I owe you a brew. + Chamila Wijayarathna and Nalin A. G. Arachchilage for their authorship and subsequent extensive discussion of their paper "Fighting Against XSS Attacks: A Usability Evaluation of OWASP ESAPI Output Encoding" (https://scholarspace.manoa.hawaii.edu/bitstream/10125/60167/0727.pdf). Their paper and their willingness to engage with me to discuss it was what led to the (hopefully) improved Javadoc for the ESAPI Encoder interface. + And lastly a special thanks to first-time contributors Michael-Ziluck, wiiitek, and HJW8472. + +Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.2.1.1-release-notes.txt b/documentation/esapi4java-core-2.2.1.1-release-notes.txt new file mode 100644 index 000000000..4670706f7 --- /dev/null +++ b/documentation/esapi4java-core-2.2.1.1-release-notes.txt @@ -0,0 +1,237 @@ +Release notes for ESAPI 2.2.1.1 + Release date: 2020-July-26 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.1.0, 2020-July-12 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +This is a patch release to address GitHub issue #560. See that GitHub issue and + +Also special props to Bill Sempf for stepping up and volunteering to prepare the initial cut of these release notes. Had he not done so, this release either would not have release notes or it would have been delayed another 6 months while I procrastinated further with various distractions. (Squirrel!) + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.1.0 release: + 211 Java source files + 4309 JUnit tests in 134 Java source files + +ESAPI 2.2.1.1 release: + 211 Java source files + 4312 JUnit tests in 134 Java source files + +39 GitHub Issues closed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +560 - Could not initialize class org.owasp.esapi.logging.java.JavaLogFactory (ESAPI 2.2.1.0) +561 - Update ESAPI-release-steps.odt to note how to do 'Release' on GitHub +564 - Create release notes for 2.2.1.1 patch release + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +As of ESAPI 2.2.1.0 (the previous release), the new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +However, if you try to juse the new ESAPI 2.2.1.0 logging you will notice that you need to change ESAPI.Logger and also possibly provide some other properties as well to get the logging behavior that you desire. + +To use ESAPI logging in ESAPI 2.2.1.0 (and later), you will need to set the ESAPI.Logger property to + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *MUST* supply an "esapi-java-logging.properties" file in your classpath. This file is included in the 'esapi-2.2.1.1-configuration.jar' file provided under the 'Assets' section of the GitHub Release at + https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.1.1 + +Unfortunately, there was a logic error in the static initializer of JavaLogFactory (now fixed in this release) that caused a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory, you will also want to ensure that you have the following ESAPI logging properties set: + # 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 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. + + +Related to that aforemented Log4J 1.x CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is *NOT* affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) + +----------------------------------------------------------------------------- + + Known Issues / Problems + +----------------------------------------------------------------------------- +If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + +I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html +and + https://github.com/bcgit/bc-java/issues/477 +I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + + +Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + +We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Updates to README.md fileg +* Minor Javadoc fixes to org.owasp.esapi.Encoder +* Fixes / cleanup to 2.2.1.0 release notes (documentation/esapi4java-core-2.2.1.0-release-notes.txt) + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.1.0 and 2.2.1.1, i.e., between 2020-07-12 and 2020-07-26) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +jeremiahjstacey 5 5 1 +kwwall 67 64 8 +======================================================== + Total: 21 + + +----------------------------------------------------------------------------- + + +2 Closed PRs merged since 2.2.1.0 release (those rejected not listed) +====================================================================== +PR# GitHub ID Description +---------------------------------------------------------------------- +559 -- synk-bot -- Upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5 +562 -- jeremiahjstacey -- Issue #560 JUL fixes + +CHANGELOG: Create your own. May I suggest: + + git log --since=2020-07-13 --reverse --pretty=medium + + which will show all the commits since just after the last (2.2.1.0) release. + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.1.1 + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.1.1 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.5.10:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.12:compile + [INFO] | | \- org.apache.httpcomponents:httpcore:jar:4.4.13:compile + [INFO] | \- commons-codec:commons-codec:jar:1.14:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.13:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xerces:xercesImpl:jar:2.12.0:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.0.5:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.65.01:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + +elangoravi for bringing GitHub issue #560 to our attention. This is one where we thought the workaround instructions was harder than just trying to fix it and thus we were encouraged to release a patch. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.2.2.0-release-notes.txt b/documentation/esapi4java-core-2.2.2.0-release-notes.txt new file mode 100644 index 000000000..833d1b94e --- /dev/null +++ b/documentation/esapi4java-core-2.2.2.0-release-notes.txt @@ -0,0 +1,250 @@ +Release notes for ESAPI 2.2.2.0 + Release date: 2020-November-27 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.1.1, 2020-July-26 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +This is a patch release with the primary intent of updating some dependencies with known vulnerabilities. The main vulnerability that was remediated was CVE-2020-13956, which was a vulnerability introduced through the ESAPI transitive dependency org.apache.httpcomponents:httpclient:4.5.12, potentially exposed through org.owasp.antisamy:antisamy:1.5.10. Updating to AntiSamy 1.5.11 remediated that issue. In addition, that update to AntiSamy 1.5.11 also addressed AntiSamy issue #48 (https://github.com/nahsra/antisamy/issues/48), which was a low risk security issue that potentially could be exposed via phishing. + +For those of you using a Software Configuration Analysis (SCA) services such as Snyk, BlackDuck, Veracode SourceClear, OWASP Dependency Check, etc., you might notice that there is vulnerability in xerces:xercesImpl:2.12.0 that ESAPI uses (also a transitive dependency) that is similar to CVE-2020-14621. Unfortunately there is no official patch for this in the regular Maven Central repository. Further details are described in Security Bulletin #3, which is viewable here + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin3.pdf +and associated with this release on GitHub. Manual workarounds possible. See the security bulletin for further details. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.1.1 release: + 211 Java source files + 4312 JUnit tests in 134 Java source files + +ESAPI 2.2.2.0 release: + 212 Java source files + 4313 JUnit tests in 135 Java source files + +10 GitHub Issues closed in this release, including those we've decided not to fix (marked '(wontfix)'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2020-07-26) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +303 - HTMLEntityCodec destroys 32-bit CJK (Chinese, Japanese and Korean) characters +561 - Update ESAPI-release-steps.odt to note how to do 'Release' on GitHub +566 - API doc comments are not shown when using ESAPI in Intellij Idea (wontfix) +567 - Release 2.2.1.1 Not Loading Properties in dependant JARs +568 - encoder-esapi is not aware of changes in esapi 2.2.1.1, making it to crash (wontfix) +569 - Unable to print the proper package and method name +571 - Logger.always() fails to log all the time when ESAPI is using org.owasp.esapi.logging.java.JavaLogFactory +574 - Multiple encoding issue for Google Chrome +577 - ESAPI decodes html entities without trailing ';' +581 - Updates to pom.xml to update AntiSamy and other dependencies + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +[If you have already successfully been using ESAPI 2.2.1.0 or later, you probably can skip this section.] + +Since ESAPI 2.2.1.0, the new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +However, if you try to juse the new ESAPI 2.2.1.0 or later logging you will notice that you need to change ESAPI.Logger and also possibly provide some other properties as well to get the logging behavior that you desire. + +To use ESAPI logging in ESAPI 2.2.1.0 (and later), you will need to set the ESAPI.Logger property to + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *MUST* supply an "esapi-java-logging.properties" file in your classpath. This file is included in the 'esapi-2.2.2.0-configuration.jar' file provided under the 'Assets' section of the GitHub Release at + https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.2.0 + +Unfortunately, there was a logic error in the static initializer of JavaLogFactory (now fixed in this release) that caused a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory, you will also want to ensure that you have the following ESAPI logging properties set: + # 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 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. + + +Related to that aforemented Log4J 1.x CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is *NOT* affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + +I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html +and + https://github.com/bcgit/bc-java/issues/477 +I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + + +Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + +We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + + +Lastly, some SCA services may continue to flag vulnerabilties in ESAPI 2.2.2.0 related to log4j 1.2.17 and xerces 2.12.0. We do not believe the way that ESAPI uses either of these in a manner that leads to any exploitable behavior. See the security bulletins + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +and + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin3.pdf +respectively, for additional details. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.1.1 and 2.2.2.0, i.e., between 2020-07-26 and 2020-11-27) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +jeremiahjstacey 8 6 1 +dependabot 1 1 1 +kwwall 7 8 0 +======================================================== + Total PRs: 2 + +There were also several snyk-bot PRs that were rejected for various reasons, mostly because 1) I was already making the proposed changes and preferred to do them in single commit or 2) there were other reasons for rejecting them (such as the dependency requiring Java 8). The proposed changes that were not outright rejected were included as part of commit a8a79bc5196653500ce664b7b063284e60bddaa0. + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2020-07-26 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.2.1.1) release. + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.2.0 + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.2.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.5.11:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.13:compile + [INFO] | | \- org.apache.httpcomponents:httpcore:jar:4.4.13:compile + [INFO] | \- commons-codec:commons-codec:jar:1.15:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.13:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xerces:xercesImpl:jar:2.12.0:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.1.4:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13.1:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.65.01:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 0.749 s + [INFO] Finished at: 2020-11-25T16:55:26-05:00 + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + Another hat tip to Dave Wichers for promptly releasing AntiSamy 1.5.11. And thanks to Matt Seil, Jeremiah Stacey, and Dave for reviewing these boring release notes and Security Bulletin #3. Despite their assistance, I take full responsibility for any errors. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.2.3.0-release-notes.txt b/documentation/esapi4java-core-2.2.3.0-release-notes.txt new file mode 100644 index 000000000..1b96a6657 --- /dev/null +++ b/documentation/esapi4java-core-2.2.3.0-release-notes.txt @@ -0,0 +1,288 @@ +Release notes for ESAPI 2.2.3.0 + Release date: 2021-03-23 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.2.0, 2020-11-27 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +Important news for this current release +--------------------------------------- +This is a patch release with the primary intent of updating some dependencies, some with known vulnerabilities. Main update are: + -- AntiSamy, from 1.5.11 to 1.6.2. + -- As a result of the AntiSamy upgrade, the transitive dependency xercesImpl was updated from 2.12.0 to 2.12.1 which should address CVE-2020-14338. + -- Apache batik-css, updated from 1.13 to 1.14. + +Details follow. + + * IMPORTANT: Effects of updating to AntiSamy 1.6.2 + - AntiSamy 1.6.2 which we are now using, is not doing XML schema validation of AntiSamy policy files. It is IMPORTANT that you read through + https://github.com/nahsra/antisamy#note-schema-validation-behavior-change-starting-with-antisamy-160 + to know how that affects you as an ESAPI user. In particular, changes important to ESAPI users include: + o AntiSamy now includes the dependency 'slf4j-simple'. This means if you are using an SLF4J library *other* than that, you should add + + org.slf4j + slf4j-simple + + in your dependency for ESAPI in your application's pom.xml. (You Gradle, Ivy, etc. other build tool users will have to figure out how to do this yourself.) + This is especially important if you are using SLF4J logging in ESAPI with some other SLF4J logger such as slf4j-log4j2, etc. If you don't do that, your logging may not come out as expected. See , under its section discussing Logging for more details. + + o Previously, ESAPI shipped with a default AntiSamy policy file called 'antisamy-esapi.xml'. For 10 plus years, unbeknownst to anyone, that file contained an unused '' node that not only did ESAPI not directly used, but was also completely ignored by AntiSamy! However, starting with AntiSamy 1.6.0, AntiSamy does XML schema validation by default, so that causes any non-compliant AntiSamy policy file to be rejected. This may result in some rather obtuse error messages and you may want to set the AntiSamy system property 'owasp.validator.validateschema' to "false" temporarily until you have time to correct your AntiSamy policy file(s). + + Old News + -------- + * For those of you using a Software Configuration Analysis (SCA) services such as Snyk, BlackDuck, Veracode's SourceClear, OWASP Dependency Check, etc., you will get a notice for CVE-2020-9488 (related to log4j 1.2.17) and you might get a notice that there is still a vulnerability in xerces:xercesImpl:2.12.1 that ESAPI uses (as a transitive dependency of AntiSamy) that is identical or similar to CVE-2020-14338. (A certain SCA service, which I won't mention, referred to CVE-2020-14621, but it's certainly not obvious from the description that this has anything to do with Xerces. But, on the hand, this is for Oracle products, and they are more obtuse than most when it comes to their vulnerability descriptions.) Unfortunately there is no official patch for the former CVE-2020-9488, as log4j 1.x is now well past its end-of-life. And for the latter CVE(s), it (they?) supposedly is (are?) fixed in the 2.12.1 release, but based on the CPEs in NVD for CVE-2020-14621, it's not clear if all SCA services would detect that. + Further details of these vulnerabilities and there potential impact on ESAPI are analyzed in ESAPI Security Advisories #2, #3 and #3, which are viewable here + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf -- The Log4j 1.x CVE + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin3.pdf -- The XercesImpl CVE + https://pastebin.com/Pm6AbcF7 (A somewhat more in-depth analysis that late to security bulletin #3.) + *NEW* -- https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin3.pdf -- Another Log4j 1.x CVE + and associated with this release on GitHub. Manual workarounds possible for each. See the security bulletin for further details. + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.2.0 release: + 212 Java source files + 4313 JUnit tests in 135 Java source files + +ESAPI 2.2.3.0 release: + 212 Java source files + 4316 JUnit tests in 136 Java source files + +12 GitHub Issues closed in this release, including those we've decided not to fix (marked '(wontfix)'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2020-11-27) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +408 Update Travis CI configuration +517 Encoded input is not treated as an attack +586 Upgrade pom.xml to use AntiSamy 1.5.12 Vulnerable Dependencies +588 Possible bug in StringValidationRule.getValid with canonicalize +592 Not able to turn off Java.util.Logging for ESAPI +593 Logger.LogLevel in ESAPI.properties not working if set to ERROR/FATAL +594 Not able to turn off ESAPI logging +599 encodeForCSS changes '#' character after upgrading from 2.0.1 to 2.2.1.1 +602 Update failing ValidatorTest in 'Java CI with Maven' GitHub workflow +606 Vulnerability in transitive dependency of esapi +609 Change log4j dependency scope to provided Build-Maven Component-Docs Component-Logger Configuration +614 Potentlial XXE Injection vulnerability in loading XML version of ESAPI properties file + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +Search for 'IMPORTANT: Effects of updating to AntiSamy 1.6.2' and read that section. The details are all covered there. + + +Old News -- Important Logging Changes +------------------------------------- +[If you have already successfully been using ESAPI 2.2.1.0 or later, you probably can skip this section, which discusses important logging changes.] + +Since ESAPI 2.2.1.0, the new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +However, if you try to juse the new ESAPI 2.2.1.0 or later logging you will notice that you need to change ESAPI.Logger and also possibly provide some other properties as well to get the logging behavior that you desire. + +To use ESAPI logging in ESAPI 2.2.1.0 (and later), you will need to set the ESAPI.Logger property to + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *MUST* supply an "esapi-java-logging.properties" file in your classpath. This file is included in the 'esapi-2.2.2.0-configuration.jar' file provided under the 'Assets' section of the GitHub Release at + https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.2.0 + +Unfortunately, there was a logic error in the static initializer of JavaLogFactory (now fixed in this release) that caused a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory, you will also want to ensure that you have the following ESAPI logging properties set: + # 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 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. + + +Related to that aforemented Log4J 1.x CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is *NOT* affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) We need to do this out of necessity because some of our dependencies are no longer doing updates that support Java 7. + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- + +New News +-------- +See UPDATE, below, in next sub-section under Old News. + + +Old News - Failing JUnit tests +------------------------------ + If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + + I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html + and + https://github.com/bcgit/bc-java/issues/477 + I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + + + Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + + UPDATE: We now believe these at least some of these failures may be because the maven-surefire-plugin is, by default, not creating a new JVM process for each test class. We are looking into this. For now, we have only have observed this behavior on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. (And yes, we are aware of 'false' in the pom for the maven-surefire-plugin, but that causes other tests to fail that we haven't had time to fix.) See GitHub issue #604 (https://github.com/ESAPI/esapi-java-legacy/issues/604) for additional details. + + +----------------------------------------------------------------------------- + + Other changes in this release, most of which were not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Added GitHub CI workflow to run 'mvn package' to test if PRs pass tests. +* Added GitHub workflow for "Super Linter". +* Minor updates to README.md file (still trying to get 'Build Status' badge to work) +* Updated deployment process documentation for ESAPI release steps. +* Updated Security Advisory #3 with new links. +* Created Security Advisory #4. +* Updated ESAPI.properties file to reflect new OWASP wiki page. +* Miscellaneous pom.xml changes related to plugins. +* Updates to CONTRIBUTING-TO-ESAPI.txt to add paragraph on GitHub deprecating passwords for git operations. +* Remove invalid node from antisamy-esapi.xml AntiSamy policy file used by ESAPI. +* Add prominent warnings in log4j.xml about ESAPI's use of Log4J 1.x being deprecated. + +----------------------------------------------------------------------------- + +Closed PRs Activity Report (Changes between release 2.2.2.0 and 2.2.3.0, i.e., between 2020-11-27 and 2021-03-23) + + See https://github.com/ESAPI/esapi-java-legacy/pulls?q=type%3Apr+state%3Aclosed+closed%3A%3E2020-11-27+updated%3A%3C2021-03-24 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2020-11-27 --until=2021-03-23 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.2.2.0) and until the current (2.2.3.0) release. + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.3.0-SNAPSHOT + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.3.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.6.2:compile + [INFO] | +- commons-codec:commons-codec:jar:1.15:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.13:compile + [INFO] | +- org.apache.httpcomponents:httpcore:jar:4.4.14:compile + [INFO] | +- org.slf4j:slf4j-simple:jar:1.7.30:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.1:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.14:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.14:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.14:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.14:compile + [INFO] | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.6:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.2.0:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13.1:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.68:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 0.906 s + [INFO] Finished at: 2021-03-21T22:03:50-04:00 +[INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + Another hat tip to Dave Wichers for promptly releasing AntiSamy 1.6.2. A special shout-out to @simon0117 for adding the Maven and Super-Linter GitHub Actions (PR #583) + And lastly, thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.2.3.1-release-notes.txt b/documentation/esapi4java-core-2.2.3.1-release-notes.txt new file mode 100644 index 000000000..f53aa8360 --- /dev/null +++ b/documentation/esapi4java-core-2.2.3.1-release-notes.txt @@ -0,0 +1,165 @@ +Release notes for ESAPI 2.2.3.1 + Release date: 2021-05-07 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.3.0, 2021-03-23 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a very small patch release with the primary intent of updating some dependencies. + +Major changes: + * Restores Apache Commons IO from 1.3.1 (what it was in 2.2.3.0) to 2.6 (what it was in 2.2.2.0). + * Updates AntiSamy from 1.6.2 to 1.6.3 + +Unless you have already updated to ESAPI 2.2.3.0 and read those release notes, you should read those release notes for additional details. You can find it at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.3.0-release-notes.txt + +That discusses things like security bulletins and other important details that I am not going into for this release. + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.3.1 release (no change since last release): + 212 Java source files + 4316 JUnit tests in 136 Java source files + +3 GitHub Issues closed in this release, including those we've decided not to fix (marked '(wontfix)'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2021-03-23) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +614 Potentlial XXE Injection vulnerability in loading XML version of ESAPI properties file +617 Unresolved Reference for com.ibm.uvm.tools in an OSGI Bundle +624 Update pom.xml to use AntiSamy 1.6.3 and Apache Commons IO 2.6 + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +See this section from the previous release notes at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.3.0-release-notes.txt + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +See this section from the previous release notes at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.3.0-release-notes.txt + +NEW since last release (ESAPI 2.2.3.0) - CVE-2021-29425 + https://nvd.nist.gov/vuln/detail/CVE-2021-29425 + + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +None known. +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.3.0 and 2.2.3.1, i.e., between 2021-03-23 and 2021-05-07) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +jeremiahjstacey 8 6 1 +dependabot 1 1 1 +kwwall 7 8 0 +======================================================== + Total PRs: 2 + +There were also several snyk-bot PRs that were rejected for various reasons, mostly because 1) I was already making the proposed changes and preferred to do them in single commit or 2) there were other reasons for rejecting them (such as the dependency requiring Java 8). The proposed changes that were not outright rejected were included as part of commit a8a79bc5196653500ce664b7b063284e60bddaa0. + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2021-03-23 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.2.3.0) release. + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.3.1-SNAPSHOT + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.3.1-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.6.3:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.13:compile + [INFO] | +- org.apache.httpcomponents:httpcore:jar:4.4.14:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.14:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.14:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.14:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.6:compile + [INFO] | +- org.slf4j:slf4j-simple:jar:1.7.30:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.1:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.2.2:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- commons-codec:commons-codec:jar:1.15:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.68:test + [INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.28:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 0.759 s + [INFO] Finished at: 2021-05-07T01:13:27-04:00 + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + Another hat tip to Dave Wichers for promptly releasing AntiSamy 1.6.2 and for the PR to fix GitHub issue #614. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.3.0.0-release-notes.txt b/documentation/esapi4java-core-2.3.0.0-release-notes.txt new file mode 100644 index 000000000..e4cdefb1d --- /dev/null +++ b/documentation/esapi4java-core-2.3.0.0-release-notes.txt @@ -0,0 +1,211 @@ +Release notes for ESAPI 2.3.0.0 + Release date: 2022-04-17 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.3.1, 2021-05-07 + +Important Announcement +---------------------- +Do NOT: Do NOT use GitHub Issues to ask questions about this of future releases. That is what the ESAPI Google groups are for. (See our GitHub README.md for further details.) If you can't do the ESAPI Google groups, then drop and email to either one or both of the project leaders (email addresses provided above). We will NOT respond to questions posted in GitHub Issues. + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a very important ESAPI release, as it remediates several potentially exploitable vulnerabilities. Part of the remediation may include reviewing and updating your antisamy-esapi.xml configuration file, so be sure to read through ALL the details thoroughly or you may not be fully protected even though you have installed the new ESAPI 2.3.0.0 jar. This will also certainly be the last ESAPI release to support Java 7, so you would do well to prepare to move to Java 8 or later if you have not already done so. + +The primary intent of this release is to patch several potentially exploitable vulnerabilities in ESAPI. Many of these are related to AntiSamy and were introduced by vulnerable transitive dependencies. All but one those (a DoS vulnerability in an AntiSamy dependency) is believed to have been fixed with an update to use the new AntiSamy 1.6.7 release. There are also two vulnerabilities within ESAPI itself which have been remediated as part of this release, one of which dates back to at least ESAPI 1.4. + +In addition to these patches (discussed in a bit more detail later under the section 'Changes Requiring Special Attention'), there were other updates to dependencies made in this release done to simply to keep them as up-to-date as possible. We have also added the generation of an SBOM (Software Bill of Materials) generated via the cyclonedx:cyclonedx-maven-plugin. + +Lastly, support for the deprecated value of "fixed" for the ESAPI property "Encryptor.ChooseIVMethod" has been completely removed from this release. It had been deprecated since 2.2.0.0 and it's removal long scheduled for the 2.3.0.0 release. See the GitHub issue 679 for further details. + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.3.1 release (previous release): + 212 Java source files + 4316 JUnit tests in 136 Java source files + +ESAPI 2.3.0.0 release (current / new release): + 212 Java source files + 4325 JUnit tests in 136 Java source files (1 test ignored) + +24 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +[Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2021-05-07] + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +163 Limit max size of entire cookies Component-Validator enhancement good first issue help wanted imported Priority-High +198 Uninitialized esapi logging assumes logging to System.out/System.err - Make configurable/extensible bug imported wontfix +324 ClassCastException during web application redeploy due to the grift logging classes enhancement imported +564 Create release notes for 2.2.1.1 patch release Component-Docs +567 Release 2.2.1.1 Not Loading Properties in dependent JARs +574 Multiple encoding issue for Google Chrome wontfix +608 Move HTMLValidationRule static Classpath handling into DefaultSecurityConfiguration +624 Update pom.xml to use AntiSamy 1.6.3 and Apache Commons IO 2.6 Build-Maven +629 Define .snyk ignore content +630 Incorrect result for isEnabled() in Slf4JLogger +631 Create Default Logging level configuration for ESAPI library wontfix +634 Removing \ from JSON string by ESAPI.encoder().canonicalize(value) +640 Decouple from AntiSamy slf4j-api dependency & Update dependency +648 Log4J CVE-2021-4104 +652 Fix code scanning alert - tracker 3 duplicate +653 java.io.FileNotFoundException Error in Logs When ESAPI.properties and validation.properties are in resources. +657 Need to update Xerces transitive dependency to fix CVE-2022-23437 +658 Vulnerability issue on dependency commons-io +664 ValidationException exposing potentially sensitive user supplied input to log wontfix +669 JavaEncryptor.java HARDCODED_CREDENTIALS +671 Version 2.2.3.1 contains 5 vulnerabilities in ESAPI dependencies +672 HTMLEntityCodec Bug Decoding "Left Angular Bracket" Symbol +673 Validator.HTTPHeaderValue changed automatically +679 Completely remove support for fixed IVs and throw a ConfigurationException if encountered + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +1) This likely will be the LAST ESAPI release supporting Java 7. There are just some vulnerabilities (notably a DoS one in Neko HtmlUnit that was assigned CVE-2022-28366 after the ESAPI 2.3.0.0 release) that because they are transitive dependencies, that we simply cannot remediate without at least moving on to Java 8 as the minimally supported JDK. Please plan accordingly. + +2) If you are not upgrading to ESAPI release 2.3.0.0 from 2.2.3.1 (the previous release), then you NEED to read at least the release notes in 2.2.3.1 and ideally, all the ones in all the previous ESAPI release notes from where you are updating to 2.3.0.0. In particular, if you were using ESAPI 2.2.1.0 or earlier, you need to see those ESAPI release notes in regards to changes in the ESAPI.Logger property. + + !!!!! VULNERABILITY ALERTS !!!!! + +3) There is one VERY SERIOUS (as in easy to exploit) vulnerability in ESAPI's default antisamy-esapi.xml configuration file. This problem seems to date back to at least ESAPI release 1.4. If you do nothing else, you should update your antisamy-esapi.xml to the one provided in the esapi-2.3.0.0-configuration.jar that can be found on GitHub under "https://github.com/ESAPI/esapi-java-legacy/releases/tag/esapi-2.3.0.0". The ESAPI team will be submitting an official CVE for this, but the bottom line is that the default ESAPI antisamy-esapi.xml configuration file does not properly sanitize 'javascript:' URLs in most cases, but instead accepts the input as "safe". A few more details regarding the configuration is provided in the section "Important checks you take as a developer using ESAPI" given below. (Update: This vulnerability was assigned CVE ID CVE-2022-24891. See GitHub Security Adivisory https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-q77q-vx4q-xx6q and ESAPI Security Bulletin 8 at https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin8.pdf for further details.) + +4) Several other vulnerabilities associated with AntiSamy have been patched via the AntiSamy 1.6.7 (or prior) release. See the AntiSamy release notes for 1.6.7, 1.6.6.1, 1.6.6, 1.6.5 and 1.6.4 at https://github.com/nahsra/antisamy/releases for further details on what has been remediated. Note that the default ESAPI.properties and ESAPI AntiSamy configuration did not really leave ESAPI vulnerable to CVE-2021-35043 which was fixed in AntiSamy 1.6.4, but that was a moot point because of #3, above. + +5) A vulnerability found by GitHub Security Lab that is an example of CWE-22 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')], was discovered by GHSL security researcher Jaroslav Lobačevski. You can find details of it under "documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.md" or "documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.pdf" on ESAPI's GitHub repo or from the ESAPI source zip or tarball files associated with this (or later) release. (Update: After this release, this vulnerability was assigned CVE ID CVE-2022-23457. See GitHub Security Adivisory https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-8m5h-hrqm-pxm2 for further details.) + +6) There remains one known unpatched, potentially exploitable vulnerability (a DoS vulnerability in the transitive dependency Neko HtmlUnit) in ESAPI 2.3.0.0. That vulnerability was later assigned CVE-20222-28366, but it is fixed in certain versions of Neko HtmlUnit after release 2.24.0. However, release 2.24.0 is the last Neko HtmlUnit release that supports Java 7 and thus is the latest one that we can use. That vulnerability is patched only fixed in a version of Neko HtmlUnit that was compiled with Java 8. Since ESAPI (as of release 2.3.0.0) only supports Java 7, we are currently unable to patch to remediate this DoS vulnerability. (This is why we are currently committed for this 2.3.0.0 release to be last release at least to support Java 7). The ESAPI team plans to release a 2.4.0.0 release that will require Java 8 or later as the minimal JDK, and with that release, we will update to AntiSamy 1.7.0 (which requires Java 8) and which uses Neko HtmlUnit 2.60.0 (which also requires Java 8 or later) and that addresses the DoS vulnerability. For further information, see the JUnit test testNekoDOSWithAnHTMLComment in "src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java". (Note that currently, this JUnit test is annotated as '@Ignore' since it would not pass under Java 7 and using Neko HtmlUnit 2.24.0.) + +7) *NEW* It later came to our attention that there was a unknown XSS vulnerability in AntiSamy [later identified as CVE-2022-29577] that was patched in AntiSamy 1.6.8, which was not available at the time of the ESAPI 2.3.0.0 release. (Someone on the AntiSamy team probably told me about this, but I just forgot. Sorry ESAPI folks!) + +NOTE: We plan on issuing an updated README.md and updated security bulletins on #3 and #4 soon, but we wanted to focus on getting the patches out rather than getting the documentation out. This probably will not be in a separate release, but we will announce in on the ESAPI Users and ESAPI Dev Google lists once we drop them on our GitHub repo under the "documentation" folder. + + +FALSE POSITIVE ALERT ==> A final word on vulnerabilities -- CVE-2020-5529 is a False Positive + +Dependency Check picks up a false positive CVE in ESAPI 2.3.0.0. Other SCA tools may as well. Specifically, Dependency Check flags CVE-2020-5529 in a different (the original) Neko HtmlUnit then the one that AntiSamy is using. In Dependency Check, this is a False Positive caused by a mismatch of the CPE (i.e., Common Platform Enumeration) identifier. If you follow the "Hyperlink" section referenced on https://nvd.nist.gov/vuln/detail/CVE-2020-5529 page, you will see that it ultimately references https://github.com/HtmlUnit/htmlunit/releases/tag/2.37.0, which is the old, unmaintained version of Neko that AntiSamy had been using up until recently. Dependency Check is incorrectly matching "net.sourceforge.htmlunit:htmlunit" rather than matching "net.sourceforge.htmlunit:neko-htmlunit", which it what if should be matching. This CPE matching confusion is a common problem with Dependency Check, but it's by design. Understandably, Jeremy Long and other Dependency Check contributors have deliberately tweaked Dependency Check to fall more on the side of False Positives so as to avoid False Negatives, because False Positives are a lot easier to vet and rule out, and one can--if so desired--create a suppressions.xml entry for it to ignore them. (I've decided against suppressing it in Dependency Check--at least for the time being--because there are likely other SCA tools that will also flag this as a False Positive.) For now, it's easier to just acknowledge it in the release notes. (Especially since we'll be releasing a 2.4.0.0 version soon after the 2.3.0.0 version that will support Java 8 as the minimal SDK so this problem will disappear.) Note however that Snyk does not flag ESAPI as being vulnerable to CVE-2020-5529. + +----------------------------------------------------------------------------e + +Important security checks you SHOULD take as a developer using ESAPI + +Simply upgrading to the esapi-2.3.0.0.jar may not be enough. This 2.3.0.0 release patches a bypass around some AntiSamy related sanitization that has been present since at least the ESAPI 1.4 release. It is specifically fixed in the esapi-2.3.0.0-configuration.jar, which you may download from https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.3.0.0/esapi-2.3.0.0-configuration.jar. From that, you will want to extract the configuration/esapi/antisamy-esapi.xml file and use it to replace your previous stock antisamy-esapi.xml file. However, if you have customized your antisamy-esapi.xml file, then to address the vulnerability, you MUST find the vulnerable configuration line where the "onsiteURL" attribute is defined and change the regular expression. + + The original (vulnerable) line will look like: + + + The corrected line should look like: + + +We have also updated the other regular expressions in the '' node for our antisamy-esapi.xml file to reflect the latest regex values from AntiSamy's antisamy.xml configuration file in their official AntiSamy 1.6.7 release. This was done as a precautionary measure only, as the regex pattern seemed to be malformed along the same lines of "onsiteURL" and thus potentially could allow unintended characters to be passed through as "safe". Note however that there are no vulnerabilities known to the ESAPI team regarding these other 2 regular expressions for "htmlTitle" and "offsiteURL". If these prove to be problematic with your applications using ESAPI, you may decide to change the probablematic ones to the original values. + + The original (possibly vulnerable???) regular expression values for htmlTitle and offsiteURL: + + + + The updated regular expression values for them: + + + +In future ESAPI releases, we may consider just replacing ESAPI's antisamy-esapi.xml file with AntiSamy's antisamy.xml, but we will not be doing that lightly. We tested with the latter and it broke some ESAPI JUnit tests so such a change now likely would break some client ESAPI code as well. However, the changes to the "" node did not break any of our ESAPI JUnit tests so we believe they are probably okay. (If not, we apologize in advance, but we prefer to error on the side of caution here.) + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.3.1 and 2.3.0.0, i.e., between 2021-05-07 and 2022-04-17) + +Normally, I (Kevin) write up lots of other details in the release notes, especially to credit those who have contributed PRs to address ESAPI issues. I apologize for not spending time on this right now, but I will try to update this set of release notes for 2.3.0.0 in the near future to add such things. + + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May we suggest: + + git log --stat --since=2021-05-07 --reverse --pretty=medium + + or clone the ESAPI/esapi-java-legacy repo and then run + + mvn site + + and finally, point your browser at + + target/site/changelog.html + + Both approaches should show all the commits since just after the previous (2.2.3.1) release. [Note that the both approaches may include commits after the 2.3.0.0 release, but the first allows to to easily add an end date via '--until=2022-04-17'.] + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.3.0:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.3.0.0 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.6.7:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.1.3:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.1.3:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.1.3:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.14:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.14:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.14:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.6:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- net.sourceforge.htmlunit:neko-htmlunit:jar:2.24:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.36:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.6.0:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.15:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.35:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.2:test + ... + +----------------------------------------------------------------------------- + +Acknowledgments: + * A special shout out to Jaroslav Lobačevski, a security researcher at GitHub Security Labs, who notified the ESAPI team via responsible disclosure and allowed us sufficient time to address GHSL-2022-008. + * A huge hat-tip to Dave Wichers and Sebastian Passaro for promptly addressing vulnerabilities in AntiSamy, many of which were caused by poorly maintained dependencies of AntiSamy. + * A special thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI contributors whom I've undoubtedly forgotten. + * Finally, to all the ESAPI users who make our efforts worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.4.0.0-release-notes.txt b/documentation/esapi4java-core-2.4.0.0-release-notes.txt new file mode 100644 index 000000000..bfc50362d --- /dev/null +++ b/documentation/esapi4java-core-2.4.0.0-release-notes.txt @@ -0,0 +1,147 @@ +Release notes for ESAPI 2.4.0.0 + Release date: 2022-04-24 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.3.0.0, 2022-04-17 + +Important Announcement +---------------------- +Do NOT: Do NOT use GitHub Issues to ask questions about this of future releases. That is what the ESAPI Google groups are for. (See our GitHub README.md for further details.) If you can't do the ESAPI Google groups, then drop and email to either one or both of the project leaders (email addresses provided above). We will NOT respond to questions posted in GitHub Issues. + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a very important ESAPI release as it is the first release to be FULLY INCOMPATIBLE WITH JAVA 1.7! This was expedited in response to some dependencies to resolve prior CVEs (see release notes in 2.3.0.0) that could not be updated as those versions required a JDK > 1.7 which we were forced to. The slightly premature update to Java 1.8 is done to address CVE-2022-28366 that had to be fixed with a version of the transitive depenedency via AntiSamy of NekoHTML that was Java 1.8+ only. (Wrapped into issue #682) It is important to note that the solution to fix CVE-2022-28366 does not exist in ESAPI 2.3.0.0 and there is no intention to fix it for Java 1.7. + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.3.0.0 release (previous release): + 212 Java source files + 4325 JUnit tests in 136 Java source files (1 test ignored) + +ESAPI 2.4.0.0 release (current / new release): + 212 Java source files + 4326 JUnit tests in 136 Java source files + +3 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +[Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2022-04-17] + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +644 Do not include a logging implementation as a dependency slf4j-simple +672 (wontfix) HTMLEntityCodec Bug Decoding "Left Angular Bracket" Symbol +679 Completely remove support for fixed IVs and throw a ConfigurationException if encountered. +682 Update baseline to Java 1.8 + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +1) This is the first ESAPI release that does not support Java 1.7. This library will no longer work if your application is that old. + + !!!!! VULNERABILITY ALERTS !!!!! + +2) This release fixes the known vulnerability ESAPI 2.3.0.0 that had to wait until we supported Java 8 to be patched. The patch was in Neko-HtmlUntil and was fixed in version 2.27, which required Java 8 or later. It was a transitive dependency via AntiSamy and we picked it up by updating to AntiSamy 1.6.8. This was a DoS vulnerability discovered in HtmlUnit-Neko affecting all versions up to 2.26. Full details from MITRE are here: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2022-28366 + +3) This release also patches the (known, but forgotten?) XSS vulnerability ESAPI 2.3.0.0 in AntiSamy 1.6.7 but was fixed in 1.6.8. (The 2.3.0.0 release notes have been updated to mention this.) Full details from MITRE are here: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2022-29577 + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.3.0.0 and 2.4.0.0, i.e., between 2022-04-17 and 2022-04-24) + +Special thanks to Dave Wichers and Sebastian Pessaro from AntiSamy for their work to provide version 1.6.8 which patched 2 CVEs. +Special thanks to Jeremiah J. Stacey for his work to update and prep the library to support java 1.8. (He literally created the PR the day after 2.3.0.0's release.) +Special thanks to Kevin Wall for support in pushing out this release. + + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May we suggest: + + git log --stat --since=2022-04-17 --reverse --pretty=medium + + or clone the ESAPI/esapi-java-legacy repo and then run + + mvn site + + and finally, point your browser at + + target/site/changelog.html + + Both approaches should show all the commits since just after the previous (2.2.3.1) release. [Note that the both approaches may include commits after the 2.3.0.0 release, but the first allows to to easily add an end date via '--until=2022-04-17'.] + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] org.owasp.esapi:esapi:jar:2.4.0.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.4:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.4:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.6.8:compile + [INFO] | +- net.sourceforge.htmlunit:neko-htmlunit:jar:2.61.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.1.3:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.1.3:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.1.3:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.14:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.14:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.14:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.6:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.36:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.11.0:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.6.0:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.15:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.35:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.2:test + ... + +----------------------------------------------------------------------------- + +Acknowledgments: + * A huge hat-tip to Dave Wichers and Sebastian Passaro for promptly releasing AntiSamy 1.6.8 which simplified this releaese + * A special thanks to Jeremiah Stacey to wrote the PR #683, that addressed the updates for Java 8. + * Finally, to all the ESAPI users who make our efforts worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) <== (Him too, this time! :) diff --git a/documentation/esapi4java-core-2.5.0.0-release-notes.txt b/documentation/esapi4java-core-2.5.0.0-release-notes.txt new file mode 100644 index 000000000..b47b084e0 --- /dev/null +++ b/documentation/esapi4java-core-2.5.0.0-release-notes.txt @@ -0,0 +1,254 @@ +Release notes for ESAPI 2.5.0.0 + Release date: 2022-07-20 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.4.0.0, 2022-04-24 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +In addition to this summary, please also be sure to thoroughly read the section "Changes Requiring Special Attention", below. + +Major changes: + Logging: + The major change in ESAPI 2.5.0.0 is the removal of the Log4J 1 dependency (specifically, log4j-1.2.17). It has been removed because in accordance with the ESAPI deprecation policy (see the README.md file), the Log4J supported logger has been deprecated for 2 years. + + For those of you using a Software Configuration Analysis (SCA) services such as Snyk, BlackDuck, Veracode SourceClear, OWASP Dependency Check, etc., you will notice that the 4 Log4J 1.x related CVEs are no longer flagged. This is because of removal of the Log4J 1.2.17 dependency. + + Any remaining flagged vulnerabilities (e.g., CVE-2020-7791 for transitive dependency batik-i18n-1.14) are believed to be false positives. + + You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + AntiSamy 1.7.0 and potentially breaking changes + We have updated to AntiSamy 1.7.0. If you have a custom version of antisamy-esapi.xml,then be sure to read the section "Changes Requiring Special Attention", below. + +Minor changes: + Miscellaneous bug fixes, Javadoc enhancements, and minor dependency updates. + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.4.0.0 release: + 212 Java source files + 4325 JUnit tests in 136 Java source files (1 test skipped) + +ESAPI 2.5.0.0 release: + 206 Java source files + 4274 JUnit tests in 131 Java source files (0 tests skipped) + +19 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2022-04-24) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +717 Update to AntiSamy 1.7.0 once it is officially released +715 ESAPI - Not working with Eclipse bug +713 Should '/' be encoded for LDAP searches? bug +705 Add more details to DefaultValidator class-level javadoc on ESAPI canonicalization properties Component-Docs Component-Validator javadoc +702 ValidatorTest#testIsValidDirectoryPathGHSL_POC fails on Mac +695 Esapi 2.3.0.0 does not supported in opensaml 2.6.6 bug +692 Multiple (2x) encoding detected in from PercentCodec question +690 Plugin/Dependency Version Updates +689 Clean-up ESAPI Javadoc Component-Docs javadoc +686 ESAPI canonicalization in DefaultEncoder ignoring Encoder.DefaultCodecList property bug Component-Encoder +684 Hello world +682 Update baseline to java 1.8 +674 Add the missing Javadoc for the Validator interface Component-Docs Component-Validator good first issue +656 DefaultHTTPUtility uses hard coded Header name/value lengths (Note: Actually fixed in ESAPI 2.3.0.0, but just closed this release. - kww) +644 Do not include a logging implementation as a dependency slf4j-simple +620 Move the default property names and values out of a reference implementation class Component-SecurityConfiguration +587 Drop Xerces dependency from pom.xml Build-Maven Vulnerable Dependencies +534 Delete Deprecated Log4J implementation and Dependencies wait4future +507 LDAP encoding of slash character + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it having first been deprecated.) Thus, your only choice for ESAPI logging are: + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implementation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. + * Create your own custom logger. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +Potentially Breaking Changes in AntiSamy 1.7.0 + +* This version of ESAPI has upgraded to the latest version of AntiSamy (1.7.0 at the time of our release). AntiSamy 1.7.0 has some breaking changes to its SDK and the way that it processes AntiSamy policy files, of which the antisamy-esapi.xml file, included in our esapi-2.5.0.0-configuration.jar found at https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.5.0.0/esapi-2.4.0.0-configuration.jar, is the one we include. + +* None of the AntiSamy SDK changes affected how ESAPI, in its default configuration, uses it, but you may be affected if you have customized your AntiSamy policy file. If your regression tests fail when you upgrade to ESAPI 2.5.0.0 sand they seem to be related to AntiSamy, then please review https://github.com/nahsra/antisamy/blob/main/README.md#important---api-breaking-changes-in-170. Also, as a temporary workaround, you could do something like this (in Maven, but similar exclusion can be done with Gradle) to allow you time to correct your customized AntiSamy policy file: + + + org.owasp.esapi + esapi + 2.5.0.0 + + + + org.owasp.antisamy + antisamy + + + + + org.owasp.antisamy + antisamy + 1.6.8 + + +Indeed the only change that we had to make is to alter a JUnit test that was intended to ensure that invalid AntiSamy policy files could be disabled by setting + Policy.setSchemaValidation(false); +before processing any AntiSamy policy file not conforming to its schema. This specific (previously deprecated) method was removed in AntiSamy 1.7.0 so the schema validation checks can no longer be ignored. (And hence the reason for the workaround noted above.) + +Instead, we simply changed the JUnit test to check that the expected AntiSamy org.owasp.validator.html.PolicyException class is thrown when the invalid policy file is loaded. + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +* 'mvn site' fails to build these two reports: + "Tag reference" report --- maven-taglib-plugin:2.4:tagreference + "Taglibdoc documentation" report --- maven-taglib-plugin:2.4:taglibdoc + + Thus no tag library documentation will be generated. :-( + + We are attempting to find a solution, but on the surface, it seems like the maven-taglib-plugin does not play nicely with versions of Java after Java 6. (So, this probably has been happening for a while and we just noticed it.) + +* We have had to suppress CVE-2017-10355, related to the transitive dependency xercesImpl-2.12.2.jar via antisamy-1.7.0.jar. It is the same jar that has been used for the past 2 years but the CVE just started popping up now, apparently because of changes to Sonatype's OSS Index. More details are available in the OWASP Dependency Check suppression rules contained in the 'suppressions.xml' file. Note that other SCA tools such as Snyk or GitHub Dependabot are not presently reporting it, but it bears watching. + +* Trying to run 'mvn test' with Java 11 or later results in multiple errors in maven-surefire-plugin, so for now, that should be avoided. We think we may have a solution, but at this point, it is too late to test for this release. + +* No others problems are known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.4.0.0 and 2.5.0.0, i.e., between 2022-04-24 and 2022-07-20) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +# +# 34 PRs merged since ESAPI 2.4.0.0 release +# Apparent disparement in the figures below may be explained by serveral things: +# * My failure to do proper counting and basic arithmetic after 4 hours of tweak release notes. +# * Different basis for calculations: +# - Figures here may not agree with generated Change Log Report, which is date-based, as some commits included in this release were prior to ESAPI 2.4.0.0 and thus not included in the Change Log Report. +# - Some commits are done without PRs. Generally, we don't require PRs when we don't require code reviews. That generally is restricted to documenation files, making simple config file changes, and correcting obvious typos. Commits without PRs are resricted to the 3 ESAPI core team members. +# - Sometimes in a PR, multiple commits touch a file multiple times so we count those files once for each commit. +# +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +jeremiahjstacey 265 180 24 +kwwall 39 69 5 +xeno6696 1 267 1 +noloader 5 2 1 +stevebosman-oc 4 3 2 +VinodAnandan 1 1 1 +======================================================== + Total PRs: 34 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2022-04-24 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.4.0.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.3.0:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.5.0.0 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.7:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.4:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.4:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.0:compile + [INFO] | +- net.sourceforge.htmlunit:neko-htmlunit:jar:2.63.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.1.3:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.1.3:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.1.4:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.14:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.14:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.14:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.14:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.6:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.36:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.11.0:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.7.1:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.15:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.35:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.2:test + ... + + +----------------------------------------------------------------------------- + +Acknowledgments: + A special shout-out our new contributors noloader, stevebosman-oc, and VinodAnandan. + Another hat tip to Dave Wichers, Sebastián Passaro, and the rest of the AntiSamy crew for promptly releasing AntiSamy 1.7.0. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.5.1.0-release-notes.txt b/documentation/esapi4java-core-2.5.1.0-release-notes.txt new file mode 100644 index 000000000..b8bfb220e --- /dev/null +++ b/documentation/esapi4java-core-2.5.1.0-release-notes.txt @@ -0,0 +1,203 @@ +Release notes for ESAPI 2.5.1.0 + Release date: 2022-11-27 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.5.0.0, 2022-07-20 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a patch release with the primary intent of updating some dependencies, some which addressed known vulnerabilities in these dependencies, but which we do not believe were exploitable via ESAPI. The major updates are: + * Updates to latest versions of direct dependencies, including: + - An update to AntiSamy: 1.7.0 --> 1.7.2 + - An update to SLFJ4 API: 1.7.36 --> 2.0.4 (Note: 2.0.5 is available and likely would would result in "convergence" issues with the version AntiSamy 1.7.2 pulls in) + * A new codec (org.owasp.esapi.codecs.JSONCodec) is provided that provides JSON output encoding as per section 7 of RFC 8259. It is made available via Encoder.encodeForJSON(). (Note unlike other encoders, there is no corresponding decoder (i.e., decodeForJSON()) made available. Since that would normally be done by your JavaScript code, it wasn't deemed essential. + * Executing 'mvn site' now creates Javadoc for the ESAPI tag library (GitHub issue #733). + +For those of you using a Software Configuration Analysis (SCA) services such as Snyk, BlackDuck, Veracode SourceClear, OWASP Dependency Check, etc., you will notice that the 4 Log4J 1.x related CVEs are no longer flagged. This is because we have finally removed the Log4J 1.2.17 dependency in ESAPI 2.5.0.0. + +Any remaining flagged vulnerabilities (e.g., CVE-2017-10355 for transitive dependency apache:xerces2_java:2.12.2) are believed to be false postives. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.5.0.0 release: + 206 Java source files + 4274 JUnit tests in 131 Java source files (0 tests skipped) + +ESAPI 2.5.1.0 release: + 207 Java source files + 4292 JUnit tests in 131 Java source files (0 tests skipped) + +15 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2022-07-20) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +757 again .. [falsepositive] +755 Upgrade batik-css-1.14 because of vulnerability +754 JSON encoder +749 Error in initializing org.owasp.esapi.logging.java.JavaLogFactory. [Converted to discussion #750.] +747 Encoder class (org.owasp.esapi.reference.DefaultEncoder) CTOR threw exception [Converted to discussion #751] +743 Indirect dependency to vulnerable Xerces, CVE-2017-10355 [falsepositive, wontfix] +740 Update SLF4J log bridge to allow NULL EventTypes +735 Improve ConfigurationException message thrown from EsapiPropertyLoaderFactory.createPropertyLoader() +734 Change skin for mvn site report to use fluido Build-Maven +733 Executing 'mvn site' does not produce tag documentation +727 ESAPI - Not working with Eclipse [falsepositive, wontfix] +710 JavaLogFactory configuration should not override custom java LogManager configuration +610 Add Deprecation Logging content for Log4JLogFactory Usage +527 Configuration flag for disabling Logger User and App Information [falsepositive] +433 Class Cast Exception when trying to run JUnit Tests [question, wontfix] + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.5.0.0 and 2.5.1.0, i.e., between 2022-07-20 and 2022-11-27) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +jeremiahjstacey 10 10 3 +noloader 4 316 4 +Jeff-Walker 2 3 1 +HenriquePinto333 2 1 1 +davewichers 2 3 1 +xeno6696 31 6 1 +kwwall 17 9 0 +======================================================== + Total Merged PRs: 11 + + (Note: Total # commits as reflected in CHANGELOG [see below] are less since commits from PRs are generally "squashed" when the are merged.) +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2022-07-20 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.5.0.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.3.0:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.5.1.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.8:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.4:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.4:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.2:compile + [INFO] | +- net.sourceforge.htmlunit:neko-htmlunit:jar:2.66.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.2:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.2:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.2:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.16:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.16:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.16:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.16:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.16:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.7:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.4:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.11.0:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.7.3:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.15:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.36:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + Special thanks to + noloader, Jeff-Walker, HenriquePinto333, & davewichers + for submitting PRs to ESAPI. + + Another hat tip to the AntiSamy crew for promptly releasing AntiSamy 1.7.2. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.5.2.0-release-notes.txt b/documentation/esapi4java-core-2.5.2.0-release-notes.txt new file mode 100644 index 000000000..b695e9b05 --- /dev/null +++ b/documentation/esapi4java-core-2.5.2.0-release-notes.txt @@ -0,0 +1,188 @@ +Release notes for ESAPI 2.5.2.0 + Release date: 2023-04-12 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.5.1.0, 2022-11-27 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a patch release with the primary intent of updating some dependencies, one with a known DoS vulnerability and a more recent one with a potential RCE. From a vulnerability perspective, it addresses CVE-2023-24998 by upgrading to version 1.5 of Apache Commons File Uploads and adding the necessary call to FileBaseUpload.setFileCountMax(). It also updates to version 1.7.3 of AntiSamy to address CVE-2023-26119, a vulnerability in one of their dependencies. + +If you are not updating from the previous ESAPI release (2.5.1.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to this release (2.5.2.0), you should MINIMALLY +read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.5.2.0, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.5.1.0 release: + 207 Java source files + 4292 JUnit tests in 131 Java source files (0 tests skipped) + +ESAPI 2.5.2.0 release: (unchanged since previous release) + 207 Java source files + 4293 JUnit tests in 131 Java source files (0 tests skipped, 1 commented out) + +7 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2022-11-27) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +773 Esapi giving issue working with graal native image bug +770 latest version of ESAPI 2.5.1.0 not working with spring boot 3.0, it gives classNotFound for javax.servlet. duplicate enhancement +769 ESAPI 2.5.1.0 not working with spring boot 3.0, spring 6 bug +767 Add support for Jakarta Servlet API Specification enhancement [converted to Discussion #768] +764 unable to locate resource: esapi-java-logging.properties +761 JavaLogFactory is not loaded from ESAPI.properties file bug +760 Could not initialize class org. Owasp. Esapi. Reference. DefaultValidator bug + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. +* We are aware that ESAPI does not support Spring Boot 3.x or later or Spring Framework 6.x or later. + - This is because these projects use a version of Jakarta Servlet API that is incompatible with the the Java EE Servlet API. (The package names are different!) + - See Discussion #768 for more details. Please do NOT report this as an issue. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.5.1.0 and 2.5.2.0, i.e., between 2022-11-27-ish and 2023-04-12) +Generated manually based on merged PRs. All errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total # of Unique # Merged +(GitHub ID) commits Files Changed PRs +======================================================== +davewichers 2 4 2 +josephWitthuhnTR 2 2 1 +dependabot 1 1 1 +kwwall 40 31 2 +======================================================== + Total merged PRs: 6 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2022-11-27 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.5.1.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.5.0:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.5.2.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.8:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.4:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.2:compile + [INFO] | +- net.sourceforge.htmlunit:neko-htmlunit:jar:2.66.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.2:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.2:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.2:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.16:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.16:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.16:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.16:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.16:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.7:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.6:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.11.0:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.7.3:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.15:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.36:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.2:test + +----------------------------------------------------------------------------- + +Acknowledgments: + Thanks to my ESAPI co-contributors Matt Seil, Jeremiah Stacey, as well as all the ESAPI users who make our efforts worthwhile. Without you, there would be little point in maintaining this project. Lastly, a special shout-out to Joseph Witthuhn for submitting 2 PRs for this release. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.5.3.0-release-notes.txt b/documentation/esapi4java-core-2.5.3.0-release-notes.txt new file mode 100644 index 000000000..953e1e0c5 --- /dev/null +++ b/documentation/esapi4java-core-2.5.3.0-release-notes.txt @@ -0,0 +1,210 @@ +Release notes for ESAPI 2.5.3.0 + Release date: 2023-11-24 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.5.2.0, 2023-04-12 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a patch release with the primary intent of providing a Jakarta compatible version of ESAPI (see ESAPI Discussion https://github.com/ESAPI/esapi-java-legacy/discussions/768) as well as updating some dependencies, some with known vulnerabilities. Details follow. +* We updated ESAPI's AntiSamy dependency from 1.7.3 to 1.7.4. AntiSamy 1.7.4 was released to address an XSS vulnerability in AntiSamy (CVE-2023-43643). Testing ESAPI's use of AntiSamy along with ESAPI's default antsamy-esapi.xml AntiSamy policy file, indicated there was no exploitable path of this CVE via ESAPI. This is because ESAPI's AntiSamy policy file is ultra-strict. (Of course, YMMV if you are not using the default AntiSamy policy file or are customized it to disable the 'preserveComments' directive.) +* We have deprecated both of ESAPI's Validator.isValidSafeHTML interfaces, as we discovered that they cannot be guaranteed safe. Note that we intend to REMOVE both of these interfaces one year after the ESAPI 2.5.3.0 release. For more details, see GitHub Security Advisory https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-r68h-jhhj-9jvm. There is also an accompanying "ESAPI Security Bulletin 12" (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin12.pdf). The Security Bulletin explains why we did not submit this as a CVE as well as explains some potential workarounds that may work for you. +* Changed ESAPI so that the default RSA modulus length (sometimes referred to as the key size) from 1024-bits to 2048-bits. Note that if you are using an old version of ESAPI.properties file prior to 2.5.3.0 and are using any of the Encryptor interfaces that directly or indirectly use digital signatures (i.e., sign, verifySignature, seal, unseal, verifySeal), you may wish to consider updating properties: + Encryptor.DigitalSignatureAlgorithm=SHA256withDSA # The old SHA1withDSA doesn't support 2048-bit RSA modulus length + Encryptor.DigitalSignatureKeyLength=2048 + Note that if you have persisted previous digital signatures that you must continue to verify, you will have to regenerate them. +* Thanks to a PR by @jcputney (PR #799), I have attempted to upload additional artifacts to Maven Central that will be a transformed jar suitable for use with the new 'jakarata.servlet' changes for Jakarata EE 9 and later. (Previously, 'javax.servlet' was the name space). Because we are still supporting JDK 8 at this point, we still need to support the 'javax.servlet' namespace as well. In addition to the standard jar artifacts, there should be a new esapi--jakarta.jar (which uses 'jakarta.servlet' instead of 'javax.servlet' namespace) as well as corresponding *-javadoc.jar and *-sources.jar files. I am not sure it will work as we have no tests for it, but looing at the binaries, it seems like it should. + For additional details, see: + https://github.com/ESAPI/esapi-java-legacy/pull/799 + https://github.com/ESAPI/esapi-java-legacy/discussions/768 + +Notes if you are not updating from the immediate previous release. release 2.5.2.0: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.5.2.0 release: + 207 Java source files + 4293 JUnit tests in 131 Java source files (0 tests skipped, 1 commented out) + +ESAPI 2.5.3.0 release: + 207 Java source files + 4293 JUnit tests in 131 Java source files (0 failures, 0 errors, 0 tests skipped) + +8 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2023-04-12) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +560 Could not initialize class org.owasp.esapi.logging.java.JavaLogFactory (ESAPI 2.2.1.0) +760 Could not initialize class org. Owasp. Esapi. Reference. DefaultValidator +775 Add documenttion to CONTRIBUTING-TO-ESAPI.txt to mention signed commits are now required. +792 хз +796 Logs printed using println() are always printed and no option to disable them. +798 Insecure default signature key length +805 Does esapi-java-legacy support jDK17 +808 Fix typo in comment in validation.properties files +812 Fix Encoder.encodeForLDAP and Encoder.encodeForDN so they are strictly conformant with Section 3 of RFC 4515 + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +Deprecated methods to be removed 1 year after the 2.5.3.0 release +* As of the ESAPI 2.5.3.0 release, both Validator.isValidSafeHTML have been deprecated and will be removed one year after the 2.5.3.0 release date. + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +The effect of upgrade to AntiSamy 1.7.4 in ESAPI 2.5.3.0 can result in ESAPI's Validator.getValidSafeHTML returning a different cleansed (i.e., sanitized) string than previous versions of ESAPI which used earlier versions of AntiSamy did. There presently is no concern for alarm as all these observed different sanitized strings returned by AntiSamy 1.7.4 still all appear to be "safe"; they are just different than before. However, as a result, this could break any regression tests that you previously had that involved ESAPI's Validator.getValidSafeHTML. See https://github.com/nahsra/antisamy/issues/389 and https://github.com/nahsra/antisamy/pull/388 for additional details. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.5.2.0 and 2.5.3.0, i.e., between 2023-04-12 and 2023-11-24) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +kwwall 40 37 2 +noloader 6 12 3 +preetgami 1 1 1 +robstoll 2 2 1 +jcputney 1 1 1 +======================================================== + Total PRs: 8 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2023-04-12 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.5.2.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.6.1:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.5.3.0 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.4:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.4:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:3.6.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.2.1:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.2:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.2.3:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.17:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.17:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.17:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.9:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.6:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.14.0:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.8.1:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.15:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 1.701 s + [INFO] Finished at: 2023-11-24T13:01:00-05:00 + [INFO] ------------------------------------------------------------------------ +----------------------------------------------------------------------------- + +Acknowledgments: + Thanks to @noloader, @preetgami, and @jcputney for submitting PRs to help move ESAPI forward. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.5.3.1-release-notes.txt b/documentation/esapi4java-core-2.5.3.1-release-notes.txt new file mode 100644 index 000000000..810cb55ca --- /dev/null +++ b/documentation/esapi4java-core-2.5.3.1-release-notes.txt @@ -0,0 +1,184 @@ +Release notes for ESAPI 2.5.3.1 + Release date: 2023-12-01 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.5.3.0, 2023-11-24 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +* Rewrite the Javadoc for Validator.isValidSafeHTML to make the dangers more evident.\ +* Changes to the DefaultValidator.isValidSafeHTM method so that a warning is always logs one time about the method being deprecated and a reference to the relevant GitHub Security Advisory. + +Notes if you are not updating from the immediate previous release. release 2.5.3.0: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.5.3.0 release: + 207 Java source files + 4293 JUnit tests in 131 Java source files (0 failures, 0 errors, 0 tests skipped) + +ESAPI 2.5.3.1 release: (unchanged) + 207 Java source files + 4293 JUnit tests in 131 Java source files (0 failures, 0 errors, 0 tests skipped) + +1 GitHub Issue closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2023-11-24) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +794 canonicalize sees entity which isn't there (wontfix) + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.5.3.0 and 2.5.3.1, i.e., between 2023-11-24 and 2023-12-01) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +kwwall 14 16 2 +======================================================== + Total PRs: 2 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2023-11-24 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.5.3.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.6.1:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.5.3.1 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.4:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.4:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:3.6.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.2.1:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.2:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.2.3:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.17:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.17:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.17:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.9:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.6:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.14.0:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.8.2:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.16.0:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + ... + + ----------------------------------------------------------------------------- + +Acknowledgments: + Another hat tip to Dave Wichers for suggesting logging a warning via the deprecated isValidSafeHTML method. + And, as always, thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.5.4.0-release-notes.txt b/documentation/esapi4java-core-2.5.4.0-release-notes.txt new file mode 100644 index 000000000..665c1d280 --- /dev/null +++ b/documentation/esapi4java-core-2.5.4.0-release-notes.txt @@ -0,0 +1,205 @@ +Release notes for ESAPI 2.5.4.0 + Release date: 2024-05-29 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.5.3.1, 2023-12-01 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a patch release with a few bug fixes and dependency updates. While the AntiSamy 1.7.5 update was intended to address an XSS vulnerability (CVE-2024-23635), the ESAPI team verified that this vulnerability did not affect the default configuration of ESAPI and its strict AntiSamy policy file, antisamy-esapi.xml. (Our AntiSamy policy file has the 'preserveComments' directive disabled.) + +There is one important issue that was patched (GitHub issue # 839) that has a side-effect. In particular, if you have ESAPI configured to use JUL (i.e., Java Util Logging) as your default logger (i.e., you have ESAPI.Logger set to 'org.owasp.esapi.logging.java.JavaLogFactory', then you MUST delete your 'esapi-java-logging.properties' to set some of the JUL properties. This file was conflicting with the other uses of JUL not used through ESAPI. If you start ESAPI 2.5.4.0 and this file is found as a resource, then a ConfigurationException will be thrown and the exception message will direct you to our GitHub wiki page, https://github.com/ESAPI/esapi-java-legacy/wiki/Configuring-the-JavaLogFactory + +Notes if you are not updating from the immediate previous release. release 2.5.3.1: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.5.3.1 release: + 207 Java source files + 4293 JUnit tests in 131 Java source files (0 failures, 0 errors, 0 tests skipped) + +ESAPI 2.5.4.0 release: + 207 Java source files + 4297 JUnit tests in 131 Java source files (0 failures, 0 errors, 0 tests skipped) + +8 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2023-12-01) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +824 DefaultEncoder / getCanonicalizedURI returns mix encoding for HTML special characters +826 Fix Encoder.getCanonicalizedURI(URI) for the test case of a double-ampersand in the HTML Query +827 HTMLEntityCodec Mysteriously decodes &or +831 java.io.FileNotFoundException Error in Logs When ESAPI.properties and validation.properties are in resources. and the application is up ,features are not working. +832 easpi .properties and validation properties are present but still it is throwing error and the application is failing do you have any solution for this +835 Validator.isValidSafeHTML() is vulnerable as per CVE-2023-4780 +837 Validation does not work with esapi jakarta jar +839 ConcurrentModificationException + + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +Important Note affecting ESAPI 2.5.4.0 and later: +* If you are using JUL for ESAPI logging, you will need to delete esapi-java-logging.properties file so it is not found as a resource stream. Failure to do so will result in a ConfigurationException being thrown. For details, see our GitHub wiki page, https://github.com/ESAPI/esapi-java-legacy/wiki/Configuring-the-JavaLogFactory + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (with your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.5.3.1 and 2.5.4.0, i.e., between 2023-12-01 and 2024-05-29) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +xeno6696 5 4 1 +jeremiahjstacey 2 4 1 +dependabot 1 1 1 +mpreziuso 1 2 1 +kwwall 11 6 0 (direct merge) +======================================================== + Total PRs: 4 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2023-12-01 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.5.3.1) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.6.1:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.5.4.0 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.5.0-M1:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.5:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.3.1:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.2.4:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.2.4:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.17:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.17:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.17:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.9:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:3.11.1:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.13:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- commons-io:commons-io:jar:2.15.1:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.8.5:compile + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile + [INFO] +- commons-codec:commons-codec:jar:1.17.0:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.70:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 0.903 s + [INFO] Finished at: 2024-05-29T18:31:30-04:00 + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + A special hat tip to Arshan Dabirsiaghis, original creator of AntiSamy and the creator of the antisamy-esapi.xml file, the ultrastrict AntiSamy policy used by ESAPI. That avoided a vulnerability that affected AntiSamy itself, so kudos to that foresite. + A shutout to Michele Preziuso for the PR that updated AntiSamy, added a test, and avoided the convergence issue that resulted in me having to reject the Snyk and Dependabot PRs. I appreciate you saving me having to do the work. + And special kudos to Jerry Devis for one of the best bug reports (GitHub issue #839) that I've seen since I began working on ESAPI in 2009. I hope your bug report can be an example to all. Excellent work. + + Lastly, our usual special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! +Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.5.5.0-release-notes.txt b/documentation/esapi4java-core-2.5.5.0-release-notes.txt new file mode 100644 index 000000000..69a4e6e77 --- /dev/null +++ b/documentation/esapi4java-core-2.5.5.0-release-notes.txt @@ -0,0 +1,199 @@ +Release notes for ESAPI 2.5.5.0 + Release date: 2024-10-07 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.5.4.0, 2024-05-30 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a minor release. All changes should be backward compatible with the previous ESAPI version. +The updates cover the following areas: +* Updates to dependencies and Maven plugins. +* New logging feature added which should be useful in cloud environments where you are paying for log storage. + See GitHub issue https://github.com/ESAPI/esapi-java-legacy/issues/844 for details. +* Documentation clean-up. + +Notes if you are not updating from the immediate previous release. release 2.5.4.0: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.5.4.0 release: + 207 Java source files + 4297 JUnit tests in 131 Java source files (0 failures, 0 errors, 0 tests skipped) + +ESAPI 2.5.5.0 release: + 207 Java source files + 4315 JUnit tests in 133 Java source files (0 failures, 0 errors, 0 tests skipped) + +8 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive') +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2024-05-30) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +844 Update the logging properties to opt-out of the prefix events Component-Logger enhancement +846 ESAPI.encoder().canonicalize() converts "&or" or similar strings without having trailing semicolon as logical operator +847 Update ESAPI pom to use latest version of AntiSamy (1.7.6) +851 Fix typos + + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.5.4.0 and 2.5.5.0, i.e., between 2024-05-30 and 2024-10-07) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. +Note: This only lists merged PRs, not those that were closed as rejected. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +============================================================ +DebajitKumarPhukan 7 1 1 +DarioViva42 57 44 1 +mickeyz07 10 12 1 +kwwall 11 8 2 +============================================================ + Total PRs: 5 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2024-05-30 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.5.4.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.5.5.0-SNAPSHOT + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.7.1:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.5.5.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.5.0-M2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.6:compile + [INFO] | +- commons-io:commons-io:jar:2.16.1:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.3.1:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.2.4:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.2.5:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.17:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.17:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.17:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.17:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.9:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:4.3.0:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.13:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.8.6:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- commons-codec:commons-codec:jar:1.17.0:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk18on:jar:1.78.1:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + A special shout-out to our new ESAPI contributors, mickeyz07, DarioViva42, and DebajitKumarPhukan. + Another hat tip to Dave Wichers and the AntiSamy crew for promptly releasing AntiSamy 1.7.0. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.6.0.0-release-notes.txt b/documentation/esapi4java-core-2.6.0.0-release-notes.txt new file mode 100644 index 000000000..1a9d41759 --- /dev/null +++ b/documentation/esapi4java-core-2.6.0.0-release-notes.txt @@ -0,0 +1,192 @@ +Release notes for ESAPI 2.6.0.0 + Release date: 2024-11-25 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.5.5.0, 2024-10-08 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This ESAPI release removes the Validator.isValidSafeHTML methods and references to it from ESAPI code. We will NOT be replacing it. This is to fulfill GitHub Security Advisory https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-r68h-jhhj-9jvm and GitHub issue #859. + +ESAPI was also updated to use the latest version of AntiSamy, 1.7.7. + +Notes if you are not updating from the immediate previous release. release 2.5.5.0: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.5.5.0 release: + 207 Java source files + 4297 JUnit tests in 131 Java test files + +ESAPI 2.6.0.0 release: + 207 Java source files + 4312 JUnit tests in 133 Java source files + +2 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2024-10-08) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +204 DefalutValidator.isValidSafeHTML() doesn't work (wontfix) +859 Remove deprecated Validator.isValidSafeHTML methods +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +Breaking Change - deprecated methods removed +* As of 2.6.0.0, the methods Validator.isValidSafeHTML are deleted. We won't be bring them back. See https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-r68h-jhhj-9jvm and the associated Security Bulletin for details. + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + +Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Minor updates to README.md file with respect to version information. + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.5.5.0 and 2.6.0.0, i.e., between 2024-10-08 and 2024-11-25) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. +Figures do not include rejected PRs. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +kwwall 15 13 1 +======================================================== + Total PRs: 1 + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2024-10-08 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.5.5.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.8.0:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.6.0.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.5.0-M2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.7:compile + [INFO] | +- commons-io:commons-io:jar:2.18.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.4.1:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.3.1:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.3.1:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.18:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.18:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.18:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.18:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.18:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.10:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:4.6.0:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.16:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.8.6:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- commons-codec:commons-codec:jar:1.17.1:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk18on:jar:1.78.1:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 0.884 s + [INFO] Finished at: 2024-11-25T15:35:40-05:00 + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + Another hat tip to Dave Wichers and the AntiSamy crew for promptly releasing AntiSamy 1.7.7. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.6.1.0-release-notes.txt b/documentation/esapi4java-core-2.6.1.0-release-notes.txt new file mode 100644 index 000000000..e81f3bda0 --- /dev/null +++ b/documentation/esapi4java-core-2.6.1.0-release-notes.txt @@ -0,0 +1,189 @@ +Release notes for ESAPI 2.6.1.0 + Release date: 2025-05-19 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.6.0.0, 2024-11-25 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a patch release with the primary intent of updating the AntiSamy dependency from v1.7.7 to v1.7.8. Among other fixes, AntiSamy 1.7.8 updated HttpClient 5.x to address CVE-2025-27820, which potentially could affect ESAPI users if they had customized their aAntiSamy Policy File (by default, antisamy-esapi.xml) to allow certain CSS constructs. (The default policy file does not allow CSS markup at all, and I don't believe that it would be exploitable via ESAPI.) + + +Notes if you are not updating from the immediate previous release. release 2.6.0.0: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.6.0.0 release: + 207 Java source files + 4312 JUnit tests in 133 Java source files + +ESAPI 2.6.1.0 release: + 207 Java source files + 4312 JUnit tests in 133 Java source files + +9 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2024-11-25) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +204 DefalutValidator.isValidSafeHTML() doesn't work - bug, Component-Validator, imported, Milestone-Release2.2, Priority-Medium, wontfix +838 Getting org.owasp.esapi.errors.ConfigurationException: java.lang.reflect.InvocationTargetException Encoder class (org.owasp.esapi.reference.DefaultEncoder) CTOR threw exception. - bug, wontfix +858 Fail to run Linux command with double quotes using executeSystemCommand - question, ConvertedToDiscussion +859 Remove deprecated Validator.isValidSafeHTML methods - bug (Note: fixed in previous release, 2.6.0.0) +863 2.6.0.0 still using javax HttpServletRequest - enhancement, falsepositive +867 How to turn off ESAPI logs or change its log level - question, ConvertedToDiscussion +868 Do not depend on commons-collections4 milestone (use 4.4 instead) - bug, Vulnerable Dependencies, wontfix +874 jakarta.servlet-api 5.0(Jakarta EE 9) change the package name from javax.xxx to jakarta.xxxx - enhancement, duplicate, NothingToFixHere +876 Upgrade version of antisamy to 1.7.8 to update transitive dependency affected by CVE-2025-27820 - enhancement, duplicate, NothingToFixHere + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Changes since last release 2.6.0.0 and 2.6.1.0, i.e., changes between 2025-11-25 and 2025-05-19. + + Note: I am no longer going to provide the 'Developer Activity Report' that I used to this manually create in tabluar form. This is in part because I use to use 'mvn site' to assist with its creation, but neither the 'Developer Activiity' nor 'File Activity' sections of the 'mvn site' output is currently working. + + That said, I don't care as this was always a major PITA and I think it had dubious value to start with. + + Therefore, I am replacing it to a stock GitHub tag comparison of the current and previous release, which I can automate. + + Please see, + + https://github.com/ESAPI/esapi-java-legacy/compare/esapi-2.6.0.0...esapi-2.6.1.0 + + for details. It contains all the information that the previous 'Developer Activity Reports' did and then some. + + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2024-11-25 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.6.0.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.8.1:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.6.1.0 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.5.0-M2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.8:compile + [INFO] | +- commons-io:commons-io:jar:2.19.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.4.4:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.3.4:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.3.4:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.19:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.19:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.19:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.19:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.19:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.11:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:4.11.0:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.16:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.9.3:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- commons-codec:commons-codec:jar:1.17.1:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk18on:jar:1.78.1:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + A special thanks to the AntiSamy team in getting a new AntiSamy release out in short order. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.6.2.0-release-notes.txt b/documentation/esapi4java-core-2.6.2.0-release-notes.txt new file mode 100644 index 000000000..a909feea2 --- /dev/null +++ b/documentation/esapi4java-core-2.6.2.0-release-notes.txt @@ -0,0 +1,180 @@ +Release notes for ESAPI 2.6.2.0 + Release date: 2025-06-02 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.6.1.0, 2025-05-19 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a minor patch release with the intent of updating the Apache Commons BeanUtils dependency from v1.9.4 to v1.11.0 to CVE-2025-48734. This CVE wouuld only potentially affect application code that uses the ESAPI's AccessController component. It is extremently unlikely that anyone is using that because the default implmentation for that (the class "org.owasp.esapi.reference.DefaultAccessController") is really a toy implementation that doesn't scale to enterprise levels with out some customization. (The class "org.owasp.esapi.filters.ESAPIFilter" also uses "DefaultAccessController", but it is unlikely that anyone is using that either, unless they are using a customized AccessController implementation.) We plan to deprecate this ESAPI "DefaultAccessControler" shortly in a future release. + +Notes if you are not updating from the immediate previous release. release 2.6.1.0: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.6.1.0 release: + 207 Java source files + 4312 JUnit tests in 133 Java source files + +ESAPI 2.6.2.0 release: + 207 Java source files + 4312 JUnit tests in 133 Java source files + +1 GitHub Issue closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2025-05-19) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +883 Update Apache Commons BeanUtils from 1.9.4 to 1.11.0 to address CVE-2025-48734 + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub and that 'mvn site' fails to properly build some pieces as the ESAPI tag library Javadoc. I suspect this is related to problems with one or more of the Maven plugins. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Changes since last release 2.6.1.0 and 2.6.2.0, i.e., changes between 2025-05-19 and 2025-06-02). + + Note: I am no longer going to provide the 'Developer Activity Report' that I used to this manually create in tabluar form. This is in part because I use to use 'mvn site' to assist with its creation, but neither the 'Developer Activiity' nor 'File Activity' sections of the 'mvn site' output is currently working. + + That said, I don't care as this was always a major PITA and I think it had dubious value to start with. + + Therefore, I am replacing it to a stock GitHub tag comparison of the current and previous release, which I can automate. + + Please see, + + https://github.com/ESAPI/esapi-java-legacy/compare/esapi-...esapi-2.6.2.0 + + for details. It contains all the information that the previous 'Developer Activity Reports' did and then some. + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2025-05-19 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.6.1.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.8.1:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.6.1.0 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.11.0:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.5:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.5.0-M2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.8:compile + [INFO] | +- commons-io:commons-io:jar:2.19.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.4.4:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.3.4:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.3.4:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.19:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.19:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.19:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.19:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.19:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.11:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:4.11.0:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.16:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.9.3:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- commons-codec:commons-codec:jar:1.17.1:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk18on:jar:1.78.1:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + [INFO] ------------------------------------------------------------------------ +----------------------------------------------------------------------------- + +Acknowledgments: + Thanks to GitHub Advanced Security's Dependabot SCA tool for flagging and fixing this one. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/documentation/esapi4java-core-2.7.0.0-release-notes.txt b/documentation/esapi4java-core-2.7.0.0-release-notes.txt new file mode 100644 index 000000000..f88656067 --- /dev/null +++ b/documentation/esapi4java-core-2.7.0.0-release-notes.txt @@ -0,0 +1,194 @@ +Release notes for ESAPI 2.7.0.0 + Release date: 2025-06-27 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.6.2.0, 2025-06-02 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +This is a major patch release with the primary intent of addressing CVE-2025-5878. See https://nvd.nist.gov/vuln/detail/CVE-2025-5078 and especially Security Bulletin #13 (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin13.pdf) for details. It also updates Apache Commons FileUploads to 1.6.0 to address CVE-2025-48976. That CVE likely does not affect the HTTP.getFileUloads interfaces (which is the only methods that use that library), but we have not had time to analyze it fully given the CVE cited against ESAPI. Apache Commons BeanUtils was also updated to 1.11.0 to address CVE-2025-48734 which potentially could anyone using ESAPI's AccessController and has placed their access control policy in a place where an attacker may be overwrite it. That is highly unlikely, but better safe than sorry. + +This 2.7.0.0 release also has significant Javadoc clarifications. Security Bulletin #13 explains why. + +If you fail to read Security Bulletin #13 and you are affected by CVE-2025-5878, your application using ESAPI 2.7.0.0 will not work, so it is VERY IMPORTANT that you read that. + +Notes if you are NOT updating from the immediate previous release. release 2.6.2.0: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.6.2.0 release: + 207 Java source files + 4312 JUnit tests in 133 Java source files + +ESAPI 2.7.0.0 release: + 208 Java source files + 4312 JUnit tests in 134 Java source files + +1 GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D2025-06-02) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +883 Update Apache Commons BeanUtils from 1.9.4 to 1.11.0 to address CVE-2025-48734 + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- + +IMPORTANT: Read Security Bulletin #13 (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin13.pdf) + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it having first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implementation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Changes since last release 2.6.2.0 and 2.7.0.0, i.e., changes between 2025-06-02 and 2025-06-27). + + Note: I am no longer going to provide the 'Developer Activity Report' that I used to this manually create in tabular form. This is in part because I use to use 'mvn site' to assist with its creation, but neither the 'Developer Activity' nor 'File Activity' sections of the 'mvn site' output is currently working. + + That said, I don't care as this was always a major PITA and I think it had dubious value to start with. + + Therefore, I am replacing it to a stock GitHub tag comparison of the current and previous release, which I can automate. + + Please see, + + https://github.com/ESAPI/esapi-java-legacy/compare/esapi-...esapi-2.7.0.0 + + for details. It contains all the information that the previous 'Developer Activity Reports' did and then some. + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=2025-06-02 --reverse --pretty=medium + + which will show all the commits since just after the previous (2.6.2.0) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree + ... + [INFO] --- maven-dependency-plugin:3.8.1:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.7.0.0-SNAPSHOT + [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- xom:xom:jar:1.3.9:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.11.0:compile + [INFO] | +- commons-logging:commons-logging:jar:1.3.5:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.6.0:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.5.0-M2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.7.8:compile + [INFO] | +- commons-io:commons-io:jar:2.19.0:compile + [INFO] | +- org.apache.httpcomponents.client5:httpclient5:jar:5.4.4:compile + [INFO] | | \- org.apache.httpcomponents.core5:httpcore5-h2:jar:5.3.4:compile + [INFO] | +- org.apache.httpcomponents.core5:httpcore5:jar:5.3.4:compile + [INFO] | +- org.apache.xmlgraphics:batik-css:jar:1.19:compile + [INFO] | | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.19:compile + [INFO] | | +- org.apache.xmlgraphics:batik-util:jar:1.19:compile + [INFO] | | | +- org.apache.xmlgraphics:batik-constants:jar:1.19:compile + [INFO] | | | \- org.apache.xmlgraphics:batik-i18n:jar:1.19:compile + [INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.11:compile + [INFO] | +- org.htmlunit:neko-htmlunit:jar:4.11.0:compile + [INFO] | +- xerces:xercesImpl:jar:2.12.2:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- org.slf4j:slf4j-api:jar:2.0.16:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.9.3:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- commons-codec:commons-codec:jar:1.17.1:test + [INFO] +- junit:junit:jar:4.13.2:test + [INFO] +- org.bouncycastle:bcprov-jdk18on:jar:1.78.1:test + [INFO] +- org.hamcrest:hamcrest-core:jar:2.2:test + [INFO] | \- org.hamcrest:hamcrest:jar:2.2:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.9:test + [INFO] +- org.mockito:mockito-core:jar:3.12.4:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.11.13:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.11.13:test + [INFO] | \- org.objenesis:objenesis:jar:3.2:test + [INFO] +- org.powermock:powermock-core:jar:2.0.9:test + [INFO] | \- org.javassist:javassist:jar:3.27.0-GA:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.9:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.9:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.9:test + [INFO] \- org.openjdk.jmh:jmh-core:jar:1.37:test + [INFO] +- net.sf.jopt-simple:jopt-simple:jar:5.0.4:test + [INFO] \- org.apache.commons:commons-math3:jar:3.6.1:test + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + A whole bunch of folks to thank this time: + - Longlong Gong (uglory-gll) - The security researcher who discovered the vulnerability that became CVE-2025-5878. + Most people curse those who find CVEs in their software, but because of Longlong's work, we feel ESAPI is a better library and has a more secure future. (See the "Lessons Learned" section of Security Bulletin #13 for an explanation.) + - The VulDB CNA team. + - In no particular order, Jeff Williams, Matt Seil, Jeremiah Stacey, Erika von Kampen, Bill Sempf, and Ken Pyle, all who provided me with excellent feedback on the documentation and code changes and help me keep my sanity for the past 3 weeks. + - My wife for tolerating my long evenings for the past 3 weeks. I know I've been cranky and it's been stressful for us both, but thanks for being so understanding and supportive. + - And finally, thanks to all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/pom.xml b/pom.xml index 2131e8b7c..f0bc96e7b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,9 @@ - + 4.0.0 org.owasp.esapi esapi - 2.2.0.0-RC3-SNAPSHOT + 2.7.1.0-SNAPSHOT jar @@ -17,27 +17,21 @@ - - org.sonatype.oss - oss-parent - 9 - - BSD - http://www.opensource.org/licenses/bsd-license.php + https://www.opensource.org/licenses/bsd-license.php Code License - New BSD License Creative Commons 3.0 BY-SA - http://creativecommons.org/licenses/by-sa/3.0/ + https://creativecommons.org/licenses/by-sa/3.0/ Content License - Create Commons 3.0 BY-SA ESAPI - https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API + https://owasp.org/www-project-enterprise-security-api/ 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 @@ -48,8 +42,8 @@ - The Open Web Application Security Project (OWASP) - http://www.owasp.org/index.php + The Open Worldwide Application Security Project (OWASP) + https://owasp.org/ @@ -90,30 +84,37 @@ Jeff Williams - Aspect Security + Contrast Security - Project Inventor + Project Founder Kevin W. Wall - Wells Fargo + Verisign - Project Co-owner + Project Co-leader Matt Seil OWASP - Project Co-owner + Project Co-leader + + Jeremiah J. Stacey + + JUnit SME + Jack of all trades, master of many + + Chris Schmidt - Synopsys + Fluid Truck - Former project co-owner + Former project co-leader @@ -121,25 +122,33 @@ Dave Wichers - Aspect Security Jim Manico - - Jeremiah J. Stacey - UTF-8 + 1.37 + 2.0.0-M3 + 2.0.0-M11 + 2.0.9 + 4.9.3 + 4.9.3.1 + 3.5.3 + 1.8 + + + + 2025-06-02 00:00:00 javax.servlet javax.servlet-api - 3.0.1 + 3.1.0 provided @@ -147,32 +156,37 @@ javax.servlet.jsp-api 2.3.3 provided + + + + javax.servlet + javax.servlet-api + + - com.io7m.xom + xom xom - 1.2.10 + 1.3.9 - - - xalan - xalan - + xerces xercesImpl - - xml-apis - xml-apis - commons-beanutils commons-beanutils - - 1.9.3 + + 1.11.0 + + xml-apis + xml-apis + @@ -199,136 +221,129 @@ commons-fileupload commons-fileupload - - 1.3.3 + 1.6.0 - + commons-io commons-io - - log4j - log4j - 1.2.17 - org.apache.commons commons-collections4 - - 4.2 + 4.5.0-M2 org.apache-extras.beanshell - bsh - 2.0b6 + bsh + 2.0b6 org.owasp.antisamy antisamy - 1.5.8 + 1.7.8 + - xml-apis - xml-apis + org.slf4j + slf4j-api + - xerces - xercesImpl + commons-logging + commons-logging org.slf4j slf4j-api - 1.7.26 + 2.0.16 + + + xml-apis + xml-apis + + 1.4.01 - + - commons-io - commons-io - 2.6 + com.github.spotbugs + spotbugs-annotations + ${version.spotbugs} + true + + - org.apache.xmlgraphics - batik-css - 1.11 - - - commons-io - commons-io - - - commons-logging - commons-logging - - + commons-codec + commons-codec + 1.17.1 + test - xalan - xalan - 2.7.2 + junit + junit + 4.13.2 + test - xml-apis - xml-apis + org.hamcrest + hamcrest-core - xerces - xercesImpl - 2.12.0 - - - xml-apis - xml-apis - 1.4.01 - - - - - junit - junit - 4.12 + org.bouncycastle + bcprov-jdk18on + 1.78.1 test - org.bouncycastle - bcprov-jdk15on - 1.61 + org.hamcrest + hamcrest-core + 2.2 test org.powermock powermock-api-mockito2 - 2.0.0 + ${version.powermock} test - - - org.javassist - javassist - - org.mockito mockito-core - - org.powermock - powermock-reflect - - + + + + + org.mockito + mockito-core + + 3.12.4 + test + + + org.powermock + powermock-core + ${version.powermock} + test + + net.bytebuddy byte-buddy @@ -337,34 +352,27 @@ byte-buddy-agent - - - - - - org.javassist - javassist - 3.25.0-GA - test - - - org.mockito - mockito-core - 2.27.0 - test org.powermock powermock-module-junit4 - 2.0.0 + ${version.powermock} test + + + junit + junit + + + org.hamcrest + hamcrest-core + + org.powermock powermock-reflect - 2.0.0 + ${version.powermock} test @@ -384,13 +392,7 @@ org.openjdk.jmh jmh-core - 1.21 - test - - - org.openjdk.jmh - jmh-generator-annprocess - 1.21 + ${version.jmh} test @@ -401,78 +403,93 @@ org.apache.maven.plugins maven-assembly-plugin - 3.1.1 + 3.7.1 org.apache.maven.plugins maven-dependency-plugin - 3.1.1 + 3.8.1 org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.1.1 + + + org.codehaus.mojo + versions-maven-plugin + 2.18.0 + + file:${project.basedir}/versionRuleset.xml + - + + + + org.cyclonedx + cyclonedx-maven-plugin + 2.9.1 + + + package + makeBom + + + com.github.spotbugs spotbugs-maven-plugin - 3.1.11 + ${version.spotbugs.maven} - + com.github.spotbugs spotbugs - 3.1.12 + ${version.spotbugs} - - - - ${PhaseIfJava8plus} - - - - true - true - true - Low - Max - false - - net.sourceforge.maven-taglib - maven-taglib-plugin - 2.4 + com.h3xstream.findsecbugs + findsecbugs-plugin + ${version.findsecbugs} + + + io.github.weblegacy + taglib-maven-plugin + 2.6 - org.apache.maven.plugins maven-changelog-plugin - 2.3 + + 3.0.0-M1 org.apache.maven.plugins maven-clean-plugin - 3.1.0 + 3.5.0 org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.14.0 - 1.7 - 1.7 - 1.7 - 1.7 + ${project.java.target} + ${project.java.target} + ${project.java.target} + ${project.java.target} true true false @@ -501,7 +518,7 @@ org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.1.4 @@ -516,45 +533,73 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M2 + 3.5.0 org.codehaus.mojo extra-enforcer-rules - 1.2 + 1.10.0 + + + org.codehaus.mojo + animal-sniffer-enforcer-rule + 1.24 + - enforce-bytecode-version - - enforce - + enforce-maven + enforce + + + + [3.6.3,) + Building ESAPI 2.x now requires Maven 3.6.3 or later. + + + + + + check-java-versions + compile + enforce + + + ${project.java.target} + + ESAPI 2.x now uses the JDK1.8 for its baseline. Please make sure that your + JAVA_HOME environment variable is pointed to a JDK1.8 or later distribution. + + - 1.7 + ${project.java.target} + true + + Dependencies shouldn't require Java 9+ true - enforce-jdk-version - - enforce - - - - - 1.7 - - ESAPI 2.x now uses the JDK1.7 for it's baseline. Please make sure that your - JAVA_HOME environment variable is pointed to a JDK1.7 distribution. - - - - + check-java8API-signatures + compile + enforce + + + + + org.codehaus.mojo.signature + + java18 + 1.0 + + + + @@ -562,7 +607,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.7 sign-artifacts @@ -575,13 +620,13 @@ org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.1.4 org.apache.maven.plugins maven-jar-plugin - 3.1.0 + 3.4.2 @@ -593,12 +638,12 @@ - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.0 + org.apache.maven.plugins + maven-javadoc-plugin + 3.11.2 - 7 - none + 8 + none @@ -613,37 +658,46 @@ org.apache.maven.plugins maven-jxr-plugin - 3.0.0 + 3.6.0 org.apache.maven.plugins maven-pmd-plugin - 3.11.0 + 3.27.0 org.apache.maven.plugins maven-project-info-reports-plugin - 3.0.0 + 3.9.0 org.apache.maven.plugins maven-resources-plugin - 3.1.0 + 3.3.1 + org.apache.maven.plugins maven-site-plugin - 3.7.1 + 4.0.0-M16 + + + org.apache.maven.skins + maven-fluido-skin + ${version.fluido} + + org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.3.1 attach-sources @@ -655,14 +709,14 @@ org.apache.maven.plugins - maven-surefire-report-plugin - 2.22.1 + maven-surefire-plugin + ${version.surefire} org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 + maven-surefire-report-plugin + ${version.surefire} @@ -678,17 +732,10 @@ - org.codehaus.mojo + io.github.jiangxincode jdepend-maven-plugin - 2.0 + 2.1 - - - org.codehaus.mojo - versions-maven-plugin - 2.7 - - org.eluder.coveralls coveralls-maven-plugin @@ -698,18 +745,28 @@ org.owasp dependency-check-maven - 5.0.0-M2 + + + 10.0.4 - 5.9 - ./suppressions.xml + ${env.NVD_API_KEY} + 1.0 + ./suppressions.xml - - ${PhaseIfJava8plus} - check + purge + @@ -720,9 +777,9 @@ - - net.sourceforge.maven-taglib - maven-taglib-plugin + + io.github.weblegacy + taglib-maven-plugin org.apache.maven.plugins @@ -732,18 +789,21 @@ https://github.com/ESAPI/esapi-java-legacy/issues/%ISSUE% date - - 2015-02-05 00:00:00 + + + ${date.prev_release} - + org.apache.maven.plugins maven-javadoc-plugin none - 7 + 8 + true + true @@ -756,7 +816,7 @@ org.apache.maven.plugins maven-pmd-plugin - 1.7 + ${project.java.target} utf-8 @@ -767,6 +827,7 @@ + index dependency-convergence @@ -778,19 +839,19 @@ org.apache.maven.plugins - maven-surefire-report-plugin + maven-surefire-plugin org.apache.maven.plugins - maven-surefire-plugin + maven-surefire-report-plugin - org.codehaus.mojo + io.github.jiangxincode jdepend-maven-plugin - + org.codehaus.mojo versions-maven-plugin @@ -809,16 +870,7 @@ - - - [1.8,) - - - site - - - - + dist @@ -838,7 +890,6 @@ maven-jar-plugin - @@ -895,7 +946,6 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 https://github.com/ESAPI/esapi-java-legacy/tags @@ -905,5 +955,74 @@ + + jakarta + + true + + + + + org.eclipse.transformer + transformer-maven-plugin + 0.5.0 + true + + + true + + + + + default-jar + + jar + + + jakarta + + org.owasp.esapi + esapi + ${project.version} + + + + + javadoc-jar + + jar + + + false + jakarta-javadoc + + org.owasp.esapi + esapi + ${project.version} + javadoc + + + + + source-jar + + jar + + + false + jakarta-sources + + org.owasp.esapi + esapi + ${project.version} + sources + + + + + + + + diff --git a/scripts/README.txt b/scripts/README.txt new file mode 100644 index 000000000..0e28bb29c --- /dev/null +++ b/scripts/README.txt @@ -0,0 +1,16 @@ +This directory is for utilities used for building / packaging / releasing ESAPI. + +The scripts and configuration files in this directory are mostly used to create ESAPI release notes. +(The 2 'mvnQuietTest' scripts are the major exceptions to that.) + +======================== + +README.txt -- This readme file. +mvnQuietTest.bat -- Run 'mvn test' from DOS cmd prompt with logSpecial output suppressed. +mvnQuietTest.sh -- Run 'mvn test' from bash with logSpecial output suppressed. +createVarsFile.sh -- Bash script to create a vars.2.x.y.z file that is 'sourced' by the 'newReleaseNotes.sh' script. +esapi4java-core-TEMPLATE-release-notes.txt - Basic template used to create the new release notes file. +newReleaseNotes.sh -- Bash script to create the release notes boillerplate from the provided release argument and the TEMPLATE file. +vars.2.?.?.? -- File that is 'sourced' (as in "source ./filename") and used with newReleaseNotes.sh + and is associated with the release number associated with the file name. +vars.template -- Template to construct the release specific vars files diff --git a/scripts/createVarsFile.sh b/scripts/createVarsFile.sh new file mode 100755 index 000000000..ad19a5747 --- /dev/null +++ b/scripts/createVarsFile.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# Purpose: Answer some questions and provide a new 'vars.' from 'vars.template' to use for creating release notes. + +prog="${0##*/}" + +function iprompt # prompt_message +{ + typeset ANS + read -p "$@ (y|n): " ANS + case "$ANS" in + [Yy]|[Yy][Ee][Ss]) return 0 ;; + *) return 1 ;; + esac +} + +read -p "Enter release # for NEW ESAPI version you are doing release notes for: " VERSION +if [[ -f "vars.$VERSION" ]] +then + iprompt "File 'vars.$VERSION' already exists. Continuing will overwrite it. Continue?" || exit 1 +fi + + +read -p "Enter release # for the PREVIOUS ESAPI version: " PREV_VERSION +read -p "Enter (planned) release date of NEW / current version you are preparing in YYYY-MM-DD format: " YYYY_MM_DD_RELEASE_DATE +read -p "Enter release date of PREVIOUS ESAPI version in YYYY-MM-DD format: " PREV_RELEASE_DATE + +echo You entered: +echo ================================================= +echo VERSION=$VERSION +echo PREV_VERSION=$PREV_VERSION +echo YYYY_MM_DD_RELEASE_DATE=$YYYY_MM_DD_RELEASE_DATE +echo PREV_RELEASE_DATE=$PREV_RELEASE_DATE +echo ================================================= +echo + +if iprompt "Are ALL your previous answers correct?" +then + # Create the new vars.${VERSION} file based on vars.template + sed -e "s/^VERSION/VERSION=$VERSION/" \ + -e "s/^PREV_VERSION/PREV_VERSION=$PREV_VERSION/" \ + -e "s/^YYYY_MM_DD_RELEASE_DATE/YYYY_MM_DD_RELEASE_DATE=$YYYY_MM_DD_RELEASE_DATE/" \ + -e "s/^PREV_RELEASE_DATE/PREV_RELEASE_DATE=$PREV_RELEASE_DATE/" \ + vars.template > "vars.$VERSION" +else + echo "$prog: Aborting. Rerun the script to correct your answers." >&2 + exit 1 +fi diff --git a/scripts/esapi4java-core-TEMPLATE-release-notes.txt b/scripts/esapi4java-core-TEMPLATE-release-notes.txt new file mode 100644 index 000000000..d03f64be6 --- /dev/null +++ b/scripts/esapi4java-core-TEMPLATE-release-notes.txt @@ -0,0 +1,151 @@ +@@@@ IMPORTANT: Be sure to 1) save in DOS text format, and 2) Delete this line and others starting with @@@@ +@@@@ Edit this file in vim with :set tw=0 +@@@@ Meant to be used with scripts/newReleaseNotes.sh and the 'vars.*' scripts there. +@@@@ There are specific references to ESAPI 2.5.0.0 and other old releases in this file. Do NOT change the version #s. They are there for a reason. +Release notes for ESAPI ${VERSION} + Release date: ${YYYY_MM_DD_RELEASE_DATE} + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI ${PREV_VERSION}, ${PREV_RELEASE_DATE} + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ +@@@@ View previous release notes to see examples of what to put here. This is typical. YMMV. +@@@@ Obviously, you should summarize any major changes / new features here. +This is a patch release with the primary intent of updating some dependencies, some with known vulnerabilities. Details follow. +@@@@ Provide a sentence or to + +Notes if you are not updating from the immediate previous release. release ${PREV_VERSION}: + * You need to read through the series of release notes FIRST, going in order. + * For example, if you were updating from an older ESAPI release (say, 2.3.0.0), you should go back and FIRST read all the subsequent release notes in turn. For instance, if you are currently on release 2.3.0.0 and upgrading to (say) release 2.x.y.z, you should MINIMALLY read the sections "Changes Requiring Special Attention" in each of the subsequent release notes. So, going from release 2.3.0.0 to 2.x.y.z, you should in turn, read: + + esapi4java-core-2.4.0.0-release-notes.txt + esapi4java-core-2.5.0.0-release-notes.txt + esapi4java-core-2.5.1.0-release-notes.txt + esapi4java-core-2.5.2.0-release-notes.txt + ...etc., up through the current set of release notes... + esapi4java-core-2.x.y.z-release-notes.txt + +in that order. YOU HAVE BEEN WARNED!!! (These release notes are too large to put all this in a given document; very few read them thoroughly as it is.) + +If your SCA tool is reporting any CVE from a direct or transitive dependency in ESAPI, before reporting it as an GitHub issue, please make sure that you review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md. Please email us or contact us in our GitHub Discussions page if you have questions about this. See also the SECURITY.md file to report any security issues with ESAPI. + +You are encouraged to review the vulnerability analysis written up in https://github.com/ESAPI/esapi-java-legacy/blob/develop/Vulnerability-Summary.md and email us or contact us in our GitHub Discussions page if you have questions. + + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI ${PREV_VERSION} release: +@@@@ Look up the figures from the previous release notes. + #### Java source files + #### JUnit tests in #### Java test files + +ESAPI ${VERSION} release: +@@@@ Count them and run 'mvn test' to get the # of tests. +@@@@ Count Java source files by executing: +@@@@ find src/main -type f -name '*.java' | wc -l + #### Java source files + #### JUnit tests in #### Java source files + +XXX GitHub Issues closed in this release, including those we've decided not to fix (marked 'wontfix' and 'falsepositive'). +(Reference: https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+state%3Aclosed+updated%3A%3E%3D${PREV_RELEASE_DATE}) + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +@@@@ Capture issue #s and 1 line desription from above GitHub url +@@@@ Insert here and massage until it looks pretty. Recommend alignment with spaces instead of tabs. + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +@@@@ NOTE any special notes here. Probably leave this one, but I would suggest noting additions BEFORE this. + +Important JDK Support Announcement +* ESAPI 2.3.0.0 was the last Java release to support Java 7. ESAPI 2.4.0 requires using Java 8 or later. See the ESAPI 2.4.0.0 release notes (https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.4.0.0-release-notes.txt) for details as to the reason. + - This means if your project requires Java 7, you must use ESAPI 2.3.0.0 or earlier. + +Important ESAPI Logging Changes + +* Since ESAPI 2.5.0.0, support for logging directly via Log4J 1 has been removed. (This was two years after it haveing first been deprecated.) Thus, you only choice of ESAPI logging are + - java.util.logging (JUL), which as been the default since ESAPI 2.2.1.0. + * Set ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory in your ESAPI.properties file. + - SLF4J (which your choice of supported SLF4J logging implemmentation) + * Set ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory in your ESAPI.properties file. +* Logger configuration notes - If you are migrating from prior to ESAPI 2.2.1.1, you will need to update your ESAPI.properties file as logging-related configuration as per the ESAPI 2.2.1.1 release notes, which may be found at: + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.1.1-release-notes.txt#L39-L78 + +If you use ESAPI 2.5.0.0 or later, you will get an ClassNotFoundException as the root cause if you still have your ESAPI.Logger property set to use Log4J because the org.owasp.esapi.logger.log4j.Log4JFactory class has been completely removed from the ESAPI jar. If you are dead set on continuing to use Log4J 1, you ought to be able to do so via SLF4J. The set up for Log4J 1 (which has not be tested), should be similar to configure ESAPI to use SLF4J with Log4J 2 as described here: + https://github.com/ESAPI/esapi-java-legacy/wiki/Using-ESAPI-with-SLF4J#slf4j-using-log4j-2x + +----------------------------------------------------------------------------- + + Remaining Known Issues / Problems + +----------------------------------------------------------------------------- +None known, other than the remaining open issues on GitHub. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Changes since last release ${PREV_VERSION} and ${VERSION}, i.e., changes between ${PREV_RELEASE_DATE} and ${YYYY_MM_DD_RELEASE_DATE}). + + Note: I am no longer going to provide the 'Developer Activity Report' that I used to this manually create in tabluar form. This is in part because I use to use 'mvn site' to assist with its creation, but neither the 'Developer Activiity' nor 'File Activity' sections of the 'mvn site' output is currently working. + + That said, I don't care as this was always a major PITA and I think it had dubious value to start with. + + Therefore, I am replacing it to a stock GitHub tag comparison of the current and previous release, which I can automate. + + Please see, + + https://github.com/ESAPI/esapi-java-legacy/compare/esapi-${PREVIOUS_VERSION}...esapi-${VERSION} + + for details. It contains all the information that the previous 'Developer Activity Reports' did and then some. + +----------------------------------------------------------------------------- + +CHANGELOG: Create your own. May I suggest: + + git log --stat --since=${PREV_RELEASE_DATE} --reverse --pretty=medium + + which will show all the commits since just after the previous (${PREV_VERSION}) release. + + Alternately, you can download the most recent ESAPI source and run + + mvn site + + which will create a CHANGELOG file named 'target/site/changelog.html' + + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn -B dependency:tree +@@@@ Include output from 'mvn -B dependency:tree' here +@@@@ RECOMMENDATION: Run the above only after ensuring you are using the latest +@@@@ plugins and dependencies so you only have to do this once. +@@@@ Check via: +@@@@ mvn -U versions:display-plugin-updates +@@@@ mvn -U versions:display-dependency-updates +@@@@ mvn -U versions:display-property-updates + +----------------------------------------------------------------------------- + +@@@@ Review these notes, especially the reference to the AntiSamy version information. +Acknowledgments: + Another hat tip to Dave Wichers and the AntiSamy crew for promptly releasing AntiSamy 1.7.0. And thanks to Matt Seil, Jeremiah Stacey, and all the ESAPI users who make this worthwhile. This is for you. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/scripts/mvnQuietTest.bat b/scripts/mvnQuietTest.bat new file mode 100644 index 000000000..ccf76dcb1 --- /dev/null +++ b/scripts/mvnQuietTest.bat @@ -0,0 +1,8 @@ +@ECHO off +rem Purpose: Run 'mvn test' with system property +rem 'org.owasp.esapi.logSpecial.discard' +rem set to true, so that all of the logSpecial output is suppressed. +rem This reduces the total output of 'mvn test' by about 2000 or so +rem lines. + +mvn -Dorg.owasp.esapi.logSpecial.discard=true test %* diff --git a/scripts/mvnQuietTest.sh b/scripts/mvnQuietTest.sh new file mode 100644 index 000000000..b66ca2913 --- /dev/null +++ b/scripts/mvnQuietTest.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Purpose: Run 'mvn test' with system property +# 'org.owasp.esapi.logSpecial.discard' +# set to true, so that all of the logSpecial output is suppressed. +# This reduces the total output of 'mvn test' by about 2000 or so +# lines. + +exec mvn -Dorg.owasp.esapi.logSpecial.discard=true test $@ diff --git a/scripts/newReleaseNotes.sh b/scripts/newReleaseNotes.sh new file mode 100755 index 000000000..c9f3d351a --- /dev/null +++ b/scripts/newReleaseNotes.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Purpose: Provide an assistance towards writing new ESAPI release notes. Still a lot of manual editing though. +# +# Usage: ./newReleaseNotes.sh new_esapi_vers_# +# Should be run from the 'scripts' directory. + +prog=${0##*/} +template="esapi4java-core-TEMPLATE-release-notes.txt" + +newVers=${1?Missing new ESAPI version number} + +if [[ -r vars.${newVers} ]] +then source vars.${newVers} +else echo "$prog: Can't find vars.${newVers} to source. Did you forget to create it based on vars.template?" >&2 + echo " Execute './createVarsFile.sh' from the 'scripts' directory to create vars.${newVers}." >&2 + exit 1 +fi + +hereDocBanner="__________@@@@@___@@@@@__________" +tmpfile="/tmp/relNotes.$$" +trap "rm $tmpfile" EXIT + +if [[ -r $template ]] +then + echo "#!/bin/bash" > $tmpfile + echo "source vars.${newVers}" >> $tmpfile + echo "set -o allexport" >> $tmpfile + echo "cat >esapi4java-core-${VERSION}-release-notes.txt <<${hereDocBanner}" >> $tmpfile + cat $template >> $tmpfile + echo "${hereDocBanner}" >> $tmpfile + echo "ls -l esapi4java-core-${VERSION}-release-notes.txt" >> $tmpfile + bash $tmpfile +else echo "$prog: Can't find or read release notes template file $template" >&2 + exit 1 +fi + +echo +echo "Now move the file 'esapi4java-core-${VERSION}-release-notes.txt' to the 'documenation/' directory" +echo "and finish editing it there. Be sure to remove all the instructional lines starting with @@@" +echo "before committing it to GitHub." diff --git a/scripts/vars.2.2.3.0 b/scripts/vars.2.2.3.0 new file mode 100644 index 000000000..7c43cccef --- /dev/null +++ b/scripts/vars.2.2.3.0 @@ -0,0 +1,13 @@ +# Do NOT edit this file directly. It will be created by the new newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.2.3.0 + +# Previous ESAPI version +PREV_VERSION=2.2.2.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2021-03-23 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2021-11-27 diff --git a/scripts/vars.2.2.3.1 b/scripts/vars.2.2.3.1 new file mode 100644 index 000000000..284e4f7fc --- /dev/null +++ b/scripts/vars.2.2.3.1 @@ -0,0 +1,13 @@ +# Do NOT edit this file directly. It will be created by the new newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.2.3.1 + +# Previous ESAPI version +PREV_VERSION=2.2.3.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2021-05-07 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2021-03-23 diff --git a/scripts/vars.2.3.0.0 b/scripts/vars.2.3.0.0 new file mode 100644 index 000000000..4695f1f6f --- /dev/null +++ b/scripts/vars.2.3.0.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.3.0.0 + +# Previous ESAPI version +PREV_VERSION=2.2.3.1 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2022-04-16 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2021-05-07 diff --git a/scripts/vars.2.4.0.0 b/scripts/vars.2.4.0.0 new file mode 100644 index 000000000..9e2f84ded --- /dev/null +++ b/scripts/vars.2.4.0.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.4.0.0 + +# Previous ESAPI version +PREV_VERSION=2.3.0.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2022-04-24 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2022-04-16 diff --git a/scripts/vars.2.5.0.0 b/scripts/vars.2.5.0.0 new file mode 100644 index 000000000..4d33532ea --- /dev/null +++ b/scripts/vars.2.5.0.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.5.0.0 + +# Previous ESAPI version +PREV_VERSION=2.4.0.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2022-07-20 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2022-04-24 diff --git a/scripts/vars.2.5.1.0 b/scripts/vars.2.5.1.0 new file mode 100644 index 000000000..ab72bcf74 --- /dev/null +++ b/scripts/vars.2.5.1.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.5.1.0 + +# Previous ESAPI version +PREV_VERSION=2.5.0.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2022-11-27 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2022-07-20 diff --git a/scripts/vars.2.5.2.0 b/scripts/vars.2.5.2.0 new file mode 100644 index 000000000..750ab57b8 --- /dev/null +++ b/scripts/vars.2.5.2.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.5.2.0 + +# Previous ESAPI version +PREV_VERSION=2.5.1.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2023-04-12 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2022-11-27 diff --git a/scripts/vars.2.5.3.0 b/scripts/vars.2.5.3.0 new file mode 100644 index 000000000..8fd04fa0a --- /dev/null +++ b/scripts/vars.2.5.3.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.5.3.0 + +# Previous ESAPI version +PREV_VERSION=2.5.2.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2023-11-24 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2023-04-12 diff --git a/scripts/vars.2.5.3.1 b/scripts/vars.2.5.3.1 new file mode 100644 index 000000000..0de9b1416 --- /dev/null +++ b/scripts/vars.2.5.3.1 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.5.3.1 + +# Previous ESAPI version +PREV_VERSION=2.5.3.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2023-12-01 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2023-11-24 diff --git a/scripts/vars.2.5.4.0 b/scripts/vars.2.5.4.0 new file mode 100644 index 000000000..a5423489c --- /dev/null +++ b/scripts/vars.2.5.4.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.5.4.0 + +# Previous ESAPI version +PREV_VERSION=2.5.3.1 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2024-05-29 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2023-12-01 diff --git a/scripts/vars.2.5.5.0 b/scripts/vars.2.5.5.0 new file mode 100644 index 000000000..3f483d045 --- /dev/null +++ b/scripts/vars.2.5.5.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.5.5.0 + +# Previous ESAPI version +PREV_VERSION=2.5.4.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2024-10-08 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2024-05-30 diff --git a/scripts/vars.2.6.0.0 b/scripts/vars.2.6.0.0 new file mode 100644 index 000000000..e44bce867 --- /dev/null +++ b/scripts/vars.2.6.0.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.6.0.0 + +# Previous ESAPI version +PREV_VERSION=2.5.5.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2024-11-25 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2024-10-08 diff --git a/scripts/vars.2.6.1.0 b/scripts/vars.2.6.1.0 new file mode 100644 index 000000000..b067cb61f --- /dev/null +++ b/scripts/vars.2.6.1.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.6.1.0 + +# Previous ESAPI version +PREV_VERSION=2.6.0.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2025-05-19 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2024-11-25 diff --git a/scripts/vars.2.6.2.0 b/scripts/vars.2.6.2.0 new file mode 100644 index 000000000..244aeaf3b --- /dev/null +++ b/scripts/vars.2.6.2.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.6.2.0 + +# Previous ESAPI version +PREV_VERSION=2.6.1.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2025-06-02 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2025-05-19 diff --git a/scripts/vars.2.7.0.0 b/scripts/vars.2.7.0.0 new file mode 100644 index 000000000..829c1663c --- /dev/null +++ b/scripts/vars.2.7.0.0 @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION=2.7.0.0 + +# Previous ESAPI version +PREV_VERSION=2.6.2.0 + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE=2025-06-27 + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE=2025-06-02 diff --git a/scripts/vars.template b/scripts/vars.template new file mode 100644 index 000000000..7e127c7a3 --- /dev/null +++ b/scripts/vars.template @@ -0,0 +1,14 @@ +# Do NOT edit this file directly. It will be created by the new createVarsFile.sh script, +# which should be run prior to the newReleaseNotes.sh script. + +# ESAPI (new / current) version +VERSION + +# Previous ESAPI version +PREV_VERSION + +# Release date of current version in yyyy-mm-dd format +YYYY_MM_DD_RELEASE_DATE + +# Previous ESAPI release date in same format +PREV_RELEASE_DATE diff --git a/src/examples/README b/src/examples/README index 9421f8819..126b6d85f 100644 --- a/src/examples/README +++ b/src/examples/README @@ -7,7 +7,7 @@ scripts - Some scripts to run that interact with the Java examples (but see NOTE: All scripts are meant to be run under *nix (or using something like - Cygwin under Windows) from under the 'scripts' directory using either + Cygwin or WSL under Windows) from under the 'scripts' directory using either bash or KornShell E.g., to compile the Java examples, execute: @@ -17,8 +17,8 @@ NOTE: All scripts are meant to be run under *nix (or using something like $ # "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 + $ # . ./setenv-git.sh + $ # if ESAPI downloaded from a Git or Svn repository. For the $ # example of this README file, we will assume it was $ # downloaded via the zip file. $ . ./setenv-zip.sh @@ -29,9 +29,9 @@ NOTE: All scripts are meant to be run under *nix (or using something like 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 + Eclipse or Maven as pulled directly from the ESAPI GitHub + repository or from pulled down from the zip file on GitHub at: + https://github.com/ESAPI/esapi-java-legacy/archive/refs/heads/develop.zip If you get it from any other place, then all bets are off. -- Kevin Wall diff --git a/src/examples/java/DisplayEncryptedProperties.java b/src/examples/java/DisplayEncryptedProperties.java index d71ac7d83..ba0ce15ef 100644 --- a/src/examples/java/DisplayEncryptedProperties.java +++ b/src/examples/java/DisplayEncryptedProperties.java @@ -8,7 +8,7 @@ // that were encrypted using ESAPI's EncryptedProperties class. // // Usage: java -classpath DisplayEncryptedProperties encryptedPropFileName -// where is proper classpath, which minimally include esapi.jar & log4j.jar +// where is proper classpath, which minimally includes the esapi.jar. public class DisplayEncryptedProperties { public DisplayEncryptedProperties() { diff --git a/src/examples/java/ESAPILogging.java b/src/examples/java/ESAPILogging.java index 338e77e89..e2b1fc85c 100644 --- a/src/examples/java/ESAPILogging.java +++ b/src/examples/java/ESAPILogging.java @@ -4,14 +4,14 @@ // Purpose: Short code snippet to show how ESAPI logging works. // // Usage: java -classpath ESAPILogging -// where is proper classpath, which minimally include esapi.jar & log4j.jar +// where is proper classpath, which minimally includes the esapi.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) { diff --git a/src/examples/java/PersistedEncryptedData.java b/src/examples/java/PersistedEncryptedData.java index a034d0a50..1cabc04bb 100644 --- a/src/examples/java/PersistedEncryptedData.java +++ b/src/examples/java/PersistedEncryptedData.java @@ -4,7 +4,6 @@ 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 @@ -72,7 +71,7 @@ public static int persistEncryptedData(PlainText plaintext, 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. * diff --git a/src/examples/scripts/compile.sh b/src/examples/scripts/compile.sh index 95203d72b..eb5aa33e0 100755 --- a/src/examples/scripts/compile.sh +++ b/src/examples/scripts/compile.sh @@ -2,11 +2,11 @@ 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" + echo >&2 "esapi_classpath not set. Did you dot the appropriate env file?" + echo >&2 "If you are using ESAPI from downloaded zip file, use:" + echo >&2 " . ./setenv-zip.sh" + echo >&2 "If you are using ESAPI pulled from SVN repository, use:" + echo >&2 " . ./setenv-git.sh" exit 1 fi cd ../java diff --git a/src/examples/scripts/encryptProperties.sh b/src/examples/scripts/encryptProperties.sh index bc606830b..7b09e3985 100755 --- a/src/examples/scripts/encryptProperties.sh +++ b/src/examples/scripts/encryptProperties.sh @@ -9,11 +9,11 @@ USAGE="Usage: encryptedProperties.sh {-display|-create} [encrypted_properties_fi 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" + echo >&2 "esapi_classpath not set. Did you dot the appropriate env file?" + echo >&2 "If you are using ESAPI from downloaded zip file, use:" + echo >&2 " . ./setenv-zip.sh" + echo >&2 "If you are using ESAPI pulled from SVN repository, use:" + echo >&2 " . ./setenv-git.sh" exit 1 fi @@ -49,8 +49,7 @@ cd ../java if [[ "$action" == "-display" ]] then set -x - java -Dlog4j.configuration="file:$log4j_properties" \ - -Dorg.owasp.esapi.resources="$esapi_resources_test" \ + java -Dorg.owasp.esapi.resources="$esapi_resources_test" \ -classpath "$esapi_classpath" \ DisplayEncryptedProperties "$filename" else @@ -61,12 +60,14 @@ else 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 "Using your TEST version of ESAPI.properties file: $esapi_resources_test/ESAPI.properties" echo =========================================================== echo echo "Hit to continue..."; read GO set -x - java -Dlog4j.configuration="file:$log4j_properties" \ - -Dorg.owasp.esapi.resources="$esapi_resources_test" \ + java -Dorg.owasp.esapi.resources="$esapi_resources_test" \ + -Djava.util.logging.config.file="$esapi_resources/esapi-java-logging.properties" \ -classpath "$esapi_classpath" \ org.owasp.esapi.reference.crypto.DefaultEncryptedProperties "$filename" && echo "Output of encrypted properties in file: $filename" diff --git a/src/examples/scripts/persistEncryptedData.sh b/src/examples/scripts/persistEncryptedData.sh index e7dbbe42b..a63fce2c9 100755 --- a/src/examples/scripts/persistEncryptedData.sh +++ b/src/examples/scripts/persistEncryptedData.sh @@ -10,20 +10,21 @@ 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" + echo >&2 "esapi_classpath not set. Did you dot the appropriate env file?" + echo >&2 "If you are using ESAPI from downloaded zip file, use:" + echo >&2 " . ./setenv-zip.sh" + echo >&2 "If you are using ESAPI pulled from SVN repository, use:" + echo >&2 " . ./setenv-git.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 "$@" +echo "Using your TEST version of ESAPI.properties file: $esapi_resources_test/ESAPI.properties" +set -x +java -Dorg.owasp.esapi.resources="$esapi_resources_test" \ + -Djava.util.logging.config.file="$esapi_resources/esapi-java-logging.properties" \ + -ea -classpath "$esapi_classpath" \ + PersistedEncryptedData "$@" diff --git a/src/examples/scripts/runClass.sh b/src/examples/scripts/runClass.sh index 1c79082c9..c8fbf4b8e 100755 --- a/src/examples/scripts/runClass.sh +++ b/src/examples/scripts/runClass.sh @@ -3,11 +3,11 @@ 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" + echo >&2 "esapi_classpath not set. Did you dot the appropriate env file?" + echo >&2 "If you are using ESAPI from downloaded zip file, use:" + echo >&2 " . ./setenv-zip.sh" + echo >&2 "If you are using ESAPI pulled from SVN repository, use:" + echo >&2 " . ./setenv-git.sh" exit 1 fi @@ -18,11 +18,10 @@ 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 "Using your TEST version of ESAPI.properties file: ${esapi_resources_test:?}/ESAPI.properties" echo set -x -java -Dlog4j.configuration="file:$log4j_properties" \ - -Dorg.owasp.esapi.resources="$esapi_resources_test" \ +java -Dorg.owasp.esapi.resources="$esapi_resources_test" \ + -Djava.util.logging.config.file="$esapi_resources/esapi-java-logging.properties" \ -classpath "$esapi_classpath" \ ${className} "$@" diff --git a/src/examples/scripts/setMasterKey.sh b/src/examples/scripts/setMasterKey.sh index 7075532bc..74b1fa3ae 100755 --- a/src/examples/scripts/setMasterKey.sh +++ b/src/examples/scripts/setMasterKey.sh @@ -1,22 +1,29 @@ #!/bin/sh -if [[ -z "$esapi_classpath" ]] +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" + echo >&2 "esapi_classpath not set. Did you dot the appropriate env file?" + echo >&2 "If you are using ESAPI from downloaded zip file, use:" + echo >&2 " . ./setenv-zip.sh" + echo >&2 "If you are using ESAPI pulled from Git or Svn repository, use:" + echo >&2 " . ./setenv-git.sh" exit 1 fi cd ../java -echo "Your ESAPI.properties file: $esapi_properties" +echo "This will generate the properties Encryptor.MasterKey and Encryptor.MasterSalt" +echo "which you will have to paste into your production ESAPI.properties file." echo -# set -x +echo "Do NOT copy those properties from your TEST ESAPI.properties as they are" +echo "the same for everyone and therefore are not secret." +echo +echo "Your PRODUCTION version of ESAPI.properties file: $esapi_resources/ESAPI.properties" +echo "Hit to continue..."; read GO +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" \ +java -Dorg.owasp.esapi.resources="$esapi_resources" \ + -Djava.util.logging.config.file="$esapi_resources/esapi-java-logging.properties" \ -classpath "$esapi_classpath" \ org.owasp.esapi.reference.crypto.JavaEncryptor "$@" diff --git a/src/examples/scripts/setenv-git.sh b/src/examples/scripts/setenv-git.sh new file mode 100755 index 000000000..99b9a86f8 --- /dev/null +++ b/src/examples/scripts/setenv-git.sh @@ -0,0 +1,40 @@ +#/bin/bash +# Purpose: Use to set up environment to compile and run examples if ESAPI +# downloaded from an Svn or Git repository. +# Usage: From csh, tcsh: +# $ source ./setenv-git.sh +# From most other *nix shells: +# $ . ./setenv-git.sh +# +# where '$' represents the shell command line prompt. +########################################################################### + +esapi_resources="$(\cd ../../../configuration/esapi >&- 2>&- && pwd)" +esapi_resources_test="$(\cd ../../../src/test/resources/esapi >&- 2>&- && pwd)" + + +# IMPORTANT NOTE: These dependency versions may need updated. Should match +# what is in ESAPI's pom.xml. +esapi_classpath=".:\ +../../../target/classes:\ +$(ls ../../../target/esapi-*.jar 2>&- || echo .):\ +$(./findjar.sh commons-fileupload-1.5.jar):\ +$(./findjar.sh servlet-api-2.4.jar)" + +if [[ ! -r "$esapi_resources"/ESAPI.properties ]] +then echo >&2 "setenv-git.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 "setenv-git.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 + +echo ############################################################ +echo "esapi_resources=$esapi_resources" +echo "esapi_resources_test=$esapi_resources_test" +echo "esapi_classpath=$esapi_classpath" +echo ############################################################ + +export esapi_classpath esapi_resources esapi_resources_test diff --git a/src/examples/scripts/setenv-svn.sh b/src/examples/scripts/setenv-svn.sh index db4846e7c..a60a498cf 100755 --- a/src/examples/scripts/setenv-svn.sh +++ b/src/examples/scripts/setenv-svn.sh @@ -9,44 +9,32 @@ # 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. -# +# IMPORTANT NOTE: These dependency versions may need updated. Should match +# what is in ESAPI's pom.xml. esapi_classpath=".:\ ../../../target/classes:\ $(ls ../../../target/esapi-*.jar 2>&- || echo .):\ -$(./findjar.sh log4j-1.2.17.jar):\ -$(./findjar.sh commons-fileupload-1.3.1.jar):\ -$(./findjar.sh servlet-api-2.5.jar)" +$(./findjar.sh commons-fileupload-1.4.jar):\ +$(./findjar.sh servlet-api-3.1.0.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" +then echo >&2 "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" +then echo >&2 "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 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 +export esapi_classpath esapi_resources esapi_resources_test diff --git a/src/examples/scripts/setenv-zip.sh b/src/examples/scripts/setenv-zip.sh index 310864ac0..8bf714c97 100755 --- a/src/examples/scripts/setenv-zip.sh +++ b/src/examples/scripts/setenv-zip.sh @@ -9,42 +9,33 @@ # where '$' represents the shell command line prompt. ########################################################################### +esapi_resources="$(\cd ../../../configuration/esapi >&- 2>&- && pwd)" +esapi_resources_test="$(\cd ../../../src/test/resources/esapi >&- 2>&- && pwd)" + # 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.17.jar, commons-fileupload-1.3.1.jar, and servlet-api-2.5.jar. +# versions change. 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" +then echo >&2 "setenv-zip.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" +then echo >&2 "setenv-zip.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 +export esapi_classpath esapi_resources esapi_resources_test diff --git a/src/main/assembly/dist.xml b/src/main/assembly/dist.xml index 3b2ee8e57..676039c82 100644 --- a/src/main/assembly/dist.xml +++ b/src/main/assembly/dist.xml @@ -38,8 +38,6 @@ configuration esapi/**/* - log4j.dtd - log4j.xml properties/**/* diff --git a/src/main/java/org/owasp/esapi/AccessControlRule.java b/src/main/java/org/owasp/esapi/AccessControlRule.java index b89af4e48..51458ff3b 100644 --- a/src/main/java/org/owasp/esapi/AccessControlRule.java +++ b/src/main/java/org/owasp/esapi/AccessControlRule.java @@ -2,7 +2,7 @@ public interface AccessControlRule { - void setPolicyParameters(P policyParameter); - P getPolicyParameters(); - boolean isAuthorized(R runtimeParameter) throws Exception; + void setPolicyParameters(P policyParameter); + P getPolicyParameters(); + 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 index 15dfb5d7f..ad23d14c7 100644 --- a/src/main/java/org/owasp/esapi/AccessController.java +++ b/src/main/java/org/owasp/esapi/AccessController.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2007-2019 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @created 2007 */ @@ -26,18 +26,18 @@ * 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. + * information, the implementation should return an access control decision. *

- * 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 + * 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. *

- * 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: - * + * 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: + * *

  * try {
  *     ESAPI.accessController().assertAuthorized("businessFunction", runtimeData);
@@ -46,13 +46,13 @@
  * ... attack in progress
  * }
  * 
- * + * * 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. - * + * *
  * <% if ( ESAPI.accessController().isAuthorized( "businessFunction", runtimeData ) ) { %>
  * <a href="/doAdminFunction">ADMIN</a>
@@ -60,140 +60,140 @@
  * <a href="/doNormalFunction">NORMAL</a>
  * <% } %>
  * 
- * + * * @author Mike H. Fauzy (mike.fauzy@aspectsecurity.com) ESAPI v1.6- * @author Jeff Williams (jeff.williams@aspectsecurity.com) ESAPI v0-1.5 */ public interface AccessController { - /** - * isAuthorized executes the AccessControlRule - * that is identified by key and listed in the - * resources/ESAPI-AccessControlPolicy.xml file. It returns - * true if the AccessControlRule decides that the operation - * should be allowed. Otherwise, it returns false. Any exception thrown by - * the AccessControlRule must result in false. If - * key does not map to an AccessControlRule, 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 key maps to - * <AccessControlPolicy><AccessControlRules> - * <AccessControlRule name="key" - * @param runtimeParameter runtimeParameter can contain anything that - * the AccessControlRule needs from the runtime system. - * @return Returns true if and only if the AccessControlRule specified - * by key exists and returned true. - * Otherwise returns false - */ - boolean isAuthorized(Object key, Object runtimeParameter); - - /** - * assertAuthorized executes the AccessControlRule - * that is identified by key and listed in the - * resources/ESAPI-AccessControlPolicy.xml file. It does - * nothing if the AccessControlRule decides that the operation - * should be allowed. Otherwise, it throws an - * org.owasp.esapi.errors.AccessControlException. Any exception - * thrown by the AccessControlRule will also result in an - * AccesControlException. If key does not map to - * an AccessControlRule, then an AccessControlException - * 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 assertAuthorized should - * be integrated into the application framework so that it is called - * automatically. - * - * @param key key maps to - * <AccessControlPolicy><AccessControlRules> - * <AccessControlRule name="key" - * @param runtimeParameter runtimeParameter can contain anything that - * the AccessControlRule needs from the runtime system. - */ - void assertAuthorized(Object key, Object runtimeParameter) - throws AccessControlException; + /** + * isAuthorized executes the AccessControlRule + * that is identified by key and listed in the + * resources/ESAPI-AccessControlPolicy.xml file. It returns + * true if the AccessControlRule decides that the operation + * should be allowed. Otherwise, it returns false. Any exception thrown by + * the AccessControlRule must result in false. If + * key does not map to an AccessControlRule, 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 key maps to + * <AccessControlPolicy><AccessControlRules> + * <AccessControlRule name="key" + * @param runtimeParameter runtimeParameter can contain anything that + * the AccessControlRule needs from the runtime system. + * @return Returns true if and only if the AccessControlRule specified + * by key exists and returned true. + * Otherwise returns false + */ + boolean isAuthorized(Object key, Object runtimeParameter); + + /** + * assertAuthorized executes the AccessControlRule + * that is identified by key and listed in the + * resources/ESAPI-AccessControlPolicy.xml file. It does + * nothing if the AccessControlRule decides that the operation + * should be allowed. Otherwise, it throws an + * org.owasp.esapi.errors.AccessControlException. Any exception + * thrown by the AccessControlRule will also result in an + * AccesControlException. If key does not map to + * an AccessControlRule, then an AccessControlException + * 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 assertAuthorized should + * be integrated into the application framework so that it is called + * automatically. + * + * @param key key maps to + * <AccessControlPolicy><AccessControlRules> + * <AccessControlRule name="key" + * @param runtimeParameter runtimeParameter can contain anything that + * the AccessControlRule needs from the runtime system. + */ + void assertAuthorized(Object key, Object runtimeParameter) + throws AccessControlException; + + + + + /*** Below this line has been deprecated as of ESAPI 1.6 ***/ + + - - - /*** 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: *
ESAPI.accessController().isAuthorizedForURL(request.getRequestURI().toString());
- * - * 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 + * + * 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 + * + * @param url + * the URL as returned by request.getRequestURI().toString() + * + * @return + * true, if is authorized for URL */ @Deprecated 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 + * 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 + * + * @param functionName + * the name of the function + * + * @return + * true, if is authorized for function */ @Deprecated 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 + * 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 + * 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 + * The actual object or object identifier being accessed or a reference to the object being accessed. + * + * @return + * true, if is authorized for the data */ @Deprecated 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 + * 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 + * + * @param filepath + * the path of the file to be checked, including filename + * + * @return + * true, if is authorized for the file */ @Deprecated boolean isAuthorizedForFile(String filepath); @@ -201,15 +201,15 @@ void assertAuthorized(Object key, Object runtimeParameter) /** * 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 + * + * 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 + * + * @param serviceName + * the service name + * + * @return + * true, if is authorized for the service */ @Deprecated boolean isAuthorizedForService(String serviceName); @@ -218,8 +218,8 @@ void assertAuthorized(Object key, Object runtimeParameter) * 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: - *
ESAPI.accessController().assertAuthorizedForURL(request.getRequestURI().toString());
- * + *
ESAPI.accessController().assertAuthorizedForURL(request.getRequestURI().toString());
+ * * 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. *

@@ -229,20 +229,20 @@ void assertAuthorized(Object key, Object runtimeParameter) *

  • Use available information to make an access control decision
  • *
      *
    1. Ideally, this policy would be data driven
    2. - *
    3. You can use the current User, roles, data type, data name, time of day, etc.
    4. - *
    5. Access control decisions must deny by default
    6. + *
    7. You can use the current User, roles, data type, data name, time of day, etc.
    8. + *
    9. Access control decisions must deny by default
    10. *
    *
  • If access is not permitted, throw an AccessControlException with details
  • - * - * @param url - * the URL as returned by request.getRequestURI().toString() - * - * @throws AccessControlException - * if access is not permitted + * + * @param url + * the URL as returned by request.getRequestURI().toString() + * + * @throws AccessControlException + * if access is not permitted */ @Deprecated 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 @@ -257,24 +257,24 @@ void assertAuthorized(Object key, Object runtimeParameter) *
  • Use available information to make an access control decision
  • *
      *
    1. Ideally, this policy would be data driven
    2. - *
    3. You can use the current User, roles, data type, data name, time of day, etc.
    4. - *
    5. Access control decisions must deny by default
    6. + *
    7. You can use the current User, roles, data type, data name, time of day, etc.
    8. + *
    9. Access control decisions must deny by default
    10. *
    *
  • If access is not permitted, throw an AccessControlException with details
  • - * - * - * @param functionName - * the function name - * - * @throws AccessControlException - * if access is not permitted + * + * + * @param functionName + * the function name + * + * @throws AccessControlException + * if access is not permitted */ @Deprecated 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. + * 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. *

    * Specification: The implementation should do the following: @@ -283,27 +283,27 @@ void assertAuthorized(Object key, Object runtimeParameter) *

  • Use available information to make an access control decision
  • *
      *
    1. Ideally, this policy would be data driven
    2. - *
    3. You can use the current User, roles, data type, data name, time of day, etc.
    4. - *
    5. Access control decisions must deny by default
    6. + *
    7. You can use the current User, roles, data type, data name, time of day, etc.
    8. + *
    9. Access control decisions must deny by default
    10. *
    *
  • If access is not permitted, throw an AccessControlException with details
  • - * - * + * + * * @param action - * The action to verify for an access control decision, such as a role, or an action being performed on the object + * 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 + * The actual object or object identifier being accessed or a reference to the object being accessed. + * + * @throws AccessControlException + * if access is not permitted */ @Deprecated 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 + * 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. *

    * This method throws an AccessControlException if access is not authorized, or if the referenced File does not exist. @@ -315,19 +315,19 @@ void assertAuthorized(Object key, Object runtimeParameter) *

  • Use available information to make an access control decision
  • *
      *
    1. Ideally, this policy would be data driven
    2. - *
    3. You can use the current User, roles, data type, data name, time of day, etc.
    4. - *
    5. Access control decisions must deny by default
    6. + *
    7. You can use the current User, roles, data type, data name, time of day, etc.
    8. + *
    9. Access control decisions must deny by default
    10. *
    *
  • If access is not permitted, throw an AccessControlException with details
  • - * - * + * + * * @param filepath - * Path to the file to be checked + * Path to the file to be checked * @throws AccessControlException if access is denied */ @Deprecated 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. @@ -341,19 +341,19 @@ void assertAuthorized(Object key, Object runtimeParameter) *
  • Use available information to make an access control decision
  • *
      *
    1. Ideally, this policy would be data driven
    2. - *
    3. You can use the current User, roles, data type, data name, time of day, etc.
    4. - *
    5. Access control decisions must deny by default
    6. + *
    7. You can use the current User, roles, data type, data name, time of day, etc.
    8. + *
    9. Access control decisions must deny by default
    10. *
    *
  • If access is not permitted, throw an AccessControlException with details
  • - * - * - * @param serviceName - * the service name - * + * + * + * @param serviceName + * the service name + * * @throws AccessControlException - * if access is not permitted - */ + * if access is not permitted + */ @Deprecated 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 index 4fd74e01e..5f7e4e73c 100644 --- a/src/main/java/org/owasp/esapi/AccessReferenceMap.java +++ b/src/main/java/org/owasp/esapi/AccessReferenceMap.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -40,7 +40,7 @@ * 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. - * + * *
      * Set fileSet = new HashSet();
      * fileSet.addAll(...); // add direct references (e.g. File objects)
    @@ -55,42 +55,42 @@
      * String indref = request.getParameter( "file" );
      * File file = (File)map.getDirectReference( indref );
      * 
    - * + * *

    - * + * * @author Jeff Williams (jeff.williams@aspectsecurity.com) * @author Chris Schmidt (chrisisbeef@gmail.com) */ public interface AccessReferenceMap 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 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 - */ - K getIndirectReference(T directReference); + /** + * 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 + */ + 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. + /** + * 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 @@ -122,55 +122,55 @@ public interface AccessReferenceMap extends Serializable { * // ... * } *

    - * - * @param indirectReference - * the indirect reference - * - * @return - * the direct reference - * - * @throws AccessControlException - * if no direct reference exists for the specified indirect reference + * + * @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 getDirectReference(K indirectReference) throws AccessControlException; + */ + 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 + */ + K addDirectReference(T direct); - /** - * 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 - */ - 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 + /** + * 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. - */ - K removeDirectReference(T direct) throws AccessControlException; + */ + 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); + /** + * 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 index 57c090a46..4e83903d5 100644 --- a/src/main/java/org/owasp/esapi/Authenticator.java +++ b/src/main/java/org/owasp/esapi/Authenticator.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -42,7 +42,7 @@ * 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. - * + * *
      * public void doPost(ServletRequest request, ServletResponse response) {
      * try {
    @@ -52,222 +52,222 @@
      * // handle failed authentication (it's already been logged)
      * }
      * 
    - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 */ public interface Authenticator { - /** - * Clears the current User. This allows the thread to be reused safely. - * + /** + * 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(); + * 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; + /** + * 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. By default, this method verifies password hashes created via the + /** + * Verify that the supplied password matches the password for this user. Password should + * be stored as a hash. By default, this method verifies password hashes created via the * {@code hashPassword(password, accountName)} method in this class, however see WARNING * the {@code hashPassword} method. *

    - * 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 + * 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 * * @see #hashPassword(String password, String accountName) - */ - boolean verifyPassword(User user, String password); - - /** - * Logs out the current user. - * - * This is usually done by calling User.logout on the current 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. + /** + * 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. *

    * WARNING: The implementation of this method as defined in the * default reference implementation class, {@code FileBasedAuthenticator}, - * uses a password hash algorthim that is known to be weak. You are advised + * uses a password hash algorithm that is known to be weak. You are advised * to replace the default reference implementation class with your own custom * implementation that uses a stronger password hashing algorithm. * See class comments in * {@code FileBasedAuthenticator} for further details. - * - * @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; + * + * @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 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); - /** - * 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; - /** - * 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); + /** + * 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); - /** - * Gets a collection containing all the existing user names. - * - * @return - * a set of all user names - */ - Set getUserNames(); + /** + * 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); - /** - * Returns the currently logged in User. - * - * @return - * the matching User object, or the Anonymous User if no match - * exists - */ - User getCurrentUser(); + /** + * 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); + /** + * 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. + /** + * 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. *

    * WARNING: The implementation of this method as defined in the * default reference implementation class, {@code FileBasedAuthenticator}, @@ -275,71 +275,71 @@ public interface Authenticator { * meant to be an example implementation and generally should be avoided * and replaced with your own implementation. See class comments in * {@code FileBasedAuthenticator} for further details. - * - * @param password - * the password to hash - * @param accountName - * the account name to use as the salt - * - * @return - * the hashed password + * + * @param password + * the password to hash + * @param accountName + * the account name to use as the salt + * + * @return + * the hashed password * @throws EncryptionException * * @see org.owasp.esapi.reference.FileBasedAuthenticator FileBasedAuthenticator, * the default reference implementation of this interface. - */ - String hashPassword(String password, String accountName) 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; + /** + * 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 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; + /** + * 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); + /** + * 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 index 33eca3fcc..55e4d896d 100644 --- a/src/main/java/org/owasp/esapi/ESAPI.java +++ b/src/main/java/org/owasp/esapi/ESAPI.java @@ -1,194 +1,201 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @author Rogan Dawes Aspect Security * @created 2008 */ package org.owasp.esapi; +import java.util.Arrays; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.owasp.esapi.util.ObjFactory; +import org.owasp.esapi.errors.ConfigurationException; /** * 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. - *

    -		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();
    -			}
    -		}
    -	 * 
    - * 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() { + 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. + *
    +        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();
    +            }
    +        }
    +     * 
    + * 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 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() { + /** + * The ESAPI {@code Encoder} is primarily used to provide output encoding to + * prevent Cross-Site Scripting (XSS). + * @return the current ESAPI {@code 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() { + /** + * ESAPI {@code Encryptor} provides a set of methods for performing common encryption, random number, and + * hashing operations. + * @return the current ESAPI {@code 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 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 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 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() { + } + + /** + * 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. - */ - 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() { + } + + /** + * @param clazz The class to associate the logger with. + * @return The current Logger associated with the specified class. + */ + 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 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 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 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 @@ -219,4 +226,74 @@ public static String initialize( String impl ) { public static void override( SecurityConfiguration config ) { overrideConfig = config; } + + // KWW - OPEN ISSUE: I don't like placing this here, but it's convenient and I + // don't really know a better place for it and would rather not create + // a whole new utility class just to use it. + /** + * Determine if a given fully qualified (ESAPI) method name has been explicitly + * enabled in the ESAPI.properties's file via the property name + * ESAPI.dangerouslyAllowUnsafeMethods.methodNames. Note that there + * is no real reason for an ESAPI client to use this, It is intended for + * interal use, + *

    + * The reason this method exists is because certain (other) ESAPI method names + * are considered "unsafe" and therefore should be used with extra caution. + * These "unsafe" methods may include methods that are: + *

      + *
    • Deprecated and thus no longer suggested for long term use.
    • + *
    • Methods where the programming contract is not in itself sufficient to ensure safety alone + * and developers are expected to take addional actions on their own to secure their application.
    • + *
    • Methods that are using some unpatched transitive dependency that we haven't firmly + * established grounds for it not being exploitable in the manner that ESAPI uses it.
    • + *
    • Methods whose reference implementations are not scalable to the enterprise level.
    • + *
    + * Public methods that are not in that list for the above ESAPI property + * are generally are considered enabled and okay to use unless their Javadoc + * indicates otherwise. + *

    + * Note that this method is intended primarilly for internal ESAPI use and if we were + * using Java Modules (in JDK 9 and later), this method would not be exported. + *

    + * For further details, please see the ESAPI GitHub wiki article, + * "Reducing the ESAPI Library's Attack Surface". + * @param fullyQualifiedMethodName A fully qualified ESAPI class name (so, should start + * "org.owasp.esapi.") followed by the method name (but without + * parenthesis or any parameter signature information. + * @return {@code true} if the parameter {@code fullyQualifiedMethodName} is in the comma-separated + * list of values in the ESAPI property ESAPI.dangerouslyAllowUnsafeMethods.methodNames, + * otherwise {@code false} is returned. + */ + public static boolean isMethodExplicityEnabled(String fullyQualifiedMethodName) { + if ( fullyQualifiedMethodName == null || fullyQualifiedMethodName.trim().isEmpty() ) { + throw new IllegalArgumentException("Program error: fullyQualifiedMethodName parameter cannot be null or empty"); + } + String desiredMethodName = fullyQualifiedMethodName.trim(); + // This regex is too liberal to be anything more than just a trivial + // sanity test to protect against typos. + if ( !desiredMethodName.matches("^org\\.owasp\\.esapi\\.(\\p{Alnum}|\\.)*$") ) { + throw new IllegalArgumentException("Program error: fullyQualifiedMethodName must start with " + + "'org.owasp.esapi.' and be a valid method name."); + } + + String enabledMethods = null; + try { + // Need to do this w/in a try/catch because if the property is not + // found, getStringProp will throw a ConfigurationException rather + // than returning a null. + enabledMethods = securityConfiguration().getStringProp("ESAPI.dangerouslyAllowUnsafeMethods.methodNames"); + } catch( ConfigurationException cex ) { + return false; // Property not found at all. + } + + + // Split it up by ',' and then filter it by finding the first on that + // matches the desired method name passed in as the method parameter. + // If no matches, return the empty string. + String result = Arrays.stream( enabledMethods.trim().split(",") ) + .filter(methodName -> methodName.trim().equals( desiredMethodName ) ) + .findFirst() + .orElse(""); + return !result.isEmpty(); + } } diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index ca82238be..409b27b24 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2007-2019 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @created 2007 */ @@ -23,75 +23,208 @@ /** - * 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 - * before validating to prevent encoded attacks. + * The {@code Encoder} interface contains a number of methods for decoding input and encoding output + * so that it will be safe for a variety of interpreters. Its primary use is to + * provide output encoding to prevent XSS. *

    - * 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. - *

    - * 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: - *

    • CSS Escaping
    • + * To prevent double-encoding, callers should make sure input does not already contain encoded characters + * by calling one of the {@code canonicalize()} methods. Validator implementations should call + * {@code canonicalize()} on user input before validating to prevent encoded attacks. + *

      + * All of the methods must use an "allow list" or "positive" security model rather + * than a "deny list" or "negative" 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. + *

      + * The {@code Encoder} performs two key functions, encoding (also referred to as "escaping" in this Javadoc) + * and decoding. These functions rely on a set of codecs that can be found in the + * {@code org.owasp.esapi.codecs} package. These include: + *

        + *
      • CSS Escaping
      • *
      • HTMLEntity Encoding
      • *
      • JavaScript Escaping
      • - *
      • MySQL Escaping
      • - *
      • Oracle Escaping
      • + *
      • MySQL Database Escaping
      • + *
      • Oracle Database Escaping
      • + *
      • JSON Escaping
      • *
      • Percent Encoding (aka URL Encoding)
      • - *
      • Unix Escaping
      • + *
      • Unix Shell Escaping
      • *
      • VBScript Escaping
      • - *
      • Windows Encoding
      - *

      - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + *

    • Windows Cmd Escaping
    • + *
    • LDAP Escaping
    • + *
    • XML and XML Attribute Encoding
    • + *
    • XPath Escaping
    • + *
    • Base64 Encoding
    • + *
    + *

    + * The primary use of ESAPI {@code Encoder} is to prevent XSS vulnerabilities by + * providing output encoding using the various "encodeForXYZ()" methods, + * where XYZ is one of CSS, HTML, HTMLAttribute, JavaScript, or URL. When + * using the ESAPI output encoders, it is important that you use the one for the + * appropriate context where the output will be rendered. For example, it + * the output appears in an JavaScript context, you should use {@code encodeForJavaScript} + * (note this includes all of the DOM JavaScript event handler attributes such as + * 'onfocus', 'onclick', 'onload', etc.). If the output would be rendered in an HTML + * attribute context (with the exception of the aforementioned 'onevent' type event + * handler attributes), you would use {@code encodeForHTMLAttribute}. If you are + * encoding anywhere a URL is expected (e.g., a 'href' attribute for for <a> or + * a 'src' attribute on a <img> tag, etc.), then you should use use {@code encodeForURL}. + * If encoding CSS, then use {@code encodeForCSS}. Etc. This is because there are + * different escaping requirements for these different contexts. Developers who are + * new to ESAPI or to defending against XSS vulnerabilities are highly encouraged to + * first read the + * + * OWASP Cross-Site Scripting Prevention Cheat Sheet. + *

    + * Note that in addition to these encoder methods, ESAPI also provides a JSP Tag + * Library ({@code META-INF/esapi.tld}) in the ESAPI jar. This allows one to use + * the more convenient JSP tags in JSPs. These JSP tags are simply wrappers for the + * various these "encodeForXXYZ()" method docmented in this {@code Encoder} + * interface. + *

    + * Some important final words: + *

      + *
    • Where to output encode for HTML rendering: + * Knowing where to place the output encoding in your code + * is just as important as knowing which context (HTML, HTML attribute, CSS, + * JavaScript, or URL) to use for the output encoding and surprisingly the two + * are often related. In general, output encoding should be done just prior to the + * output being rendered (that is, as close to the 'sink' as possible) because that + * is what determines what the appropriate context is for the output encoding. + * In fact, doing output encoding on untrusted data that is stored and to + * be used later--whether stored in an HTTP session or in a database--is almost + * always considered an anti-pattern. An example of this is one gathers and + * stores some untrusted data item such as an email address from a user. A + * developer thinks "let's output encode this and store the encoded data in + * the database, thus making the untrusted data safe to use all the time, thus + * saving all of us developers all the encoding troubles later on". On the surface, + * that sounds like a reasonable approach. The problem is how to know what + * output encoding to use, not only for now, but for all possible future + * uses? It might be that the current application code base is only using it in + * an HTML context that is displayed in an HTML report or shown in an HTML + * context in the user's profile. But what if it is later used in a {@code mailto:} URL? + * Then instead of HTML encoding, it would need to have URL encoding. Similarly, + * what if there is a later switch made to use AJAX and the untrusted email + * address gets used in a JavaScript context? The complication is that even if + * you know with certainty today all the ways that an untrusted data item is + * used in your application, it is generally impossible to predict all the + * contexts that it may be used in the future, not only in your application, but + * in other applications that could access that data in the database. + *
    • + *
    • Avoiding multiple nested contexts: + * A really tricky situation to get correct is when there are multiple nested + * encoding contexts. But far, the most common place this seems to come up is + * untrusted URLs used in JavaScript. How should you handle that? Well, + * the best way is to rewrite your code to avoid it! An example of + * this that is well worth reading may be found at + * ESAPI-DEV mailing list archives: + * URL encoding within JavaScript. Be sure to read the entire thread. + * The question itself is too nuanced to be answered in Javadoc, but now, + * hopefully you are at least aware of the potential pitfalls. There is little + * available research or examples on how to do output encoding when multiple + * mixed encodings are required, although one that you may find useful is + * + * Automated Detecting and Repair of Cross-SiteScripting Vulnerabilities through Unit Testing + * It at least discusses a few of the common errors involved in multiple mixed + * encoding contexts. + *
    • A word about unit testing: + * Unit testing this is hard. You may be satisfied with stopped after you have + * tested against the ubiquitous XSS test case of + *
      + *      </script>alert(1)</script>
      + * 
      + * or similar simplistic XSS attack payloads and if that is properly encoded + * (or, you don't see an alert box popped in your browser), you consider it + * "problem fixed", and consider the unit testing sufficient. Unfortunately, that + * minimalist testing may not always detect places where you used the wrong output + * encoder. You need to do better. Fortunately, the aforementioned link, + * + * Automated Detecting and Repair of Cross-SiteScripting Vulnerabilities through Unit Testing + * provides some insight on this. You may also wish to look at the + * ESAPI Encoder JUnittest cases for ideas. + * If you are really ambitious, an excellent resource for XSS attack patterns is + * BeEF - The Browser Exploitation Framework Project. + *
    • A final note on {@code Encoder} implementation details: + * Most of the {@code Encoder} methods make extensive use of ESAPI's {@link org.owasp.esapi.codecs.Codec} + * classes under-the-hood. These {@code Codec} classes are intended for use for encoding and decoding + * input based on some particular context or specification. While the OWASP team + * over the years have made every effort to be cautious--often going to extremes + * to make "safe harbor" decisions on harmful inputs other similar encoders assume are already safe + * (we did this to in order to protect the client's users from buggy browsers that don't adhere + * to the W3C HTML specications)&em;the various {@code Codec} implemtations can offer + * NO GUARANTEE of safety of the content being encoded or decoded. Therefore, + * it is highly advised to practice a security-in-depth approach for everything you do. + * By following that advice, you will minimize the impact and/or likelihood of any + * vulnerabilities from bugs in the ESAPI code or accidental misuse of the ESAPI + * library on your part. In particular, whenever there are cases where cients use + * any of these {@link org.owasp.esapi.codecs.Codec} classes directly, it is highly + * recommended to perform canonicalization followed by strict input valiation both + * prior to encoding and after decoding to protect your application from input-based + * attacks. + *
    • + *
    + *

    + * @see OWASP Cross-Site Scripting Prevention Cheat Sheet + * @see org.owasp.esapi.Validator + * @see OWASP Proactive Controls: C4: Encode and Escape Data + * @see Properly encoding and escaping for the web + * @author Jeff Williams (jeff.williams .at. owasp.org) * @since June 1, 2007 */ public interface Encoder { - - /** - * This method is equivalent to calling
    Encoder.canonicalize(input, restrictMultiple, restrictMixed);
    - * - * The default values for restrictMultiple and restrictMixed come from ESAPI.properties - *
    -	 * Encoder.AllowMultipleEncoding=false
    -	 * Encoder.AllowMixedEncoding=false
    -	 * 
    - * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize - * @see W3C specifications - * - * @param input the text to canonicalize - * @return a String containing the canonicalized text - */ - String canonicalize(String input); - - /** - * This method is the equivalent to calling
    Encoder.canonicalize(input, strict, strict);
    - * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize - * @see W3C specifications - * - * @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. - *

    + /** + * This method is equivalent to calling {@code Encoder.canonicalize(input, restrictMultiple, restrictMixed);}. + * + * The default values for {@code restrictMultiple} and {@code restrictMixed} come from {@code ESAPI.properties}. + *

    +     * Encoder.AllowMultipleEncoding=false
    +     * Encoder.AllowMixedEncoding=false
    +     * 
    + * and the default codecs that are used for canonicalization are the list + * of codecs that comes from: + *
    +     * Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    +     * 
    + * (If the {@code Encoder.DefaultCodecList} property is null or not set, + * these same codecs are listed in the same order. Note that you may supply + * your own codec by using a fully cqualified class name of a class that + * implements {@code org.owasp.esapi.codecs.Codec}. + * + * @see #canonicalize(String, boolean, boolean) + * @see W3C specifications + * + * @param input the text to canonicalize + * @return a String containing the canonicalized text + */ + String canonicalize(String input); + + /** + * This method is the equivalent to calling {@code Encoder.canonicalize(input, strict, strict);}. + * + * @see #canonicalize(String, boolean, boolean) + * @see W3C specifications + * + * @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. + *

    * Everyone says 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 @@ -100,7 +233,7 @@ public interface Encoder { *

    • Perverse but legal variants of escaping schemes
    • *
    • Multiple escaping (%2526 or &lt;)
    • *
    • Mixed escaping (%26lt;)
    • - *
    • Nested escaping (%%316 or &%6ct;)
    • + *
    • Nested escaping (%%316 or &%6ct;)
    • *
    • All combinations of multiple, mixed, and nested encoding/escaping (%253c or ┦gt;)
    *

    * Using canonicalize is simple. The default is just... @@ -119,7 +252,25 @@ public interface Encoder { * Encoder encoder = new DefaultEncoder( list ); * String clean = encoder.canonicalize( request.getParameter( "input" )); *

    - * In ESAPI, the Validator uses the canonicalize method before it does validation. So all you need to + * or alternately, you can just customize {@code Encoder.DefaultCodecList} property + * in the {@code ESAPI.properties} file with your preferred codecs; for + * example: + *
    +     * Encoder.DefaultCodecList=WindowsCodec,MySQLCodec,PercentCodec
    +     * 
    + * and then use: + *
    +     *     Encoder encoder = ESAPI.encoder();
    +     *     String clean = encoder.canonicalize( request.getParameter( "input" ));
    +     * 
    + * as you normally would. However, the downside to using the + * {@code ESAPI.properties} file approach does not allow you to vary your + * list of codecs that are used each time. The downside to using the + * {@code DefaultEncoder} constructor is that your code is now timed to + * specific reference implementations rather than just interfaces and those + * reference implementations are what is most likely to change in ESAPI 3.x. + *

    + * In ESAPI, the {@code Validator} uses the {@code 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. *

          *     String input = request.getParameter( "name" );
    @@ -136,326 +287,475 @@ public interface Encoder {
          * 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:
    -	 * 
    -	 * Encoder.AllowMultipleEncoding=false
    -	 * Encoder.AllowMixedEncoding=false
    -	 * 
    - * 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 + * in {@code ESAPI.properties} using the properties: + *
    +     * Encoder.AllowMultipleEncoding=false
    +     * Encoder.AllowMixedEncoding=false
    +     * 
    + * 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. *
          *     // disabling strict mode to allow mixed encoding
          *     String url = ESAPI.encoder().canonicalize( request.getParameter("url"), false, false);
          * 
    - * - * @see W3C specifications - * - * @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); + * WARNING #1!!! Please note that this method is incompatible with URLs and if there exist any HTML Entities + * that correspond with parameter values in a URL such as "&para;" in a URL like + * "https://foo.com/?bar=foo&parameter=wrong" you will get a mixed encoding validation exception. + *

    + * If you wish to canonicalize a URL/URI use the method {@code Encoder.getCanonicalizedURI(URI dirtyUri);} + *

    + * WARNING #2!!! Even if you use {@code WindowsCodec} or {@code UnixCodec} + * as appropriate, file path names in the {@code input} parameter will NOT + * be canonicalized. It the failure of such file path name canonicalization + * presents a potential security issue, consider using one of the + * {@code Validator.getValidDirectoryPath()} methods instead of or in addition to this method. + * + * @see W3C specifications + * @see #canonicalize(String) + * @see #getCanonicalizedURI(URI dirtyUri) + * @see org.owasp.esapi.Validator#getValidDirectoryPath(java.lang.String, java.lang.String, java.io.File, boolean) + * + * @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 CSS Syntax [w3.org] - * - * @param input - * the text to encode for CSS - * - * @return input encoded for CSS - */ - String encodeForCSS(String input); + /** + * Encode data for use in Cascading Style Sheets (CSS) content. + * + * @see CSS Syntax [w3.org] + * + * @param untrustedData + * the untrusted data to output encode for CSS + * + * @return the untrusted data safely output encoded for use in a CSS + */ + String encodeForCSS(String untrustedData); - /** - * Encode data for use in HTML using HTML entity encoding - *

    - * Note that the following characters: - * 00-08, 0B-0C, 0E-1F, and 7F-9F - *

    cannot be used in HTML. - * - * @see HTML Encodings [wikipedia.org] - * @see SGML Specification [w3.org] + /** + * Encode data for use in HTML using HTML entity encoding + *

    + * Note that the following characters: + * 00-08, 0B-0C, 0E-1F, and 7F-9F + *

    cannot be used in HTML. + * + * @see HTML Encodings [wikipedia.org] + * @see SGML Specification [w3.org] * @see XML Specification [w3.org] - * - * @param input - * the text to encode for HTML - * - * @return input encoded for HTML - */ - String encodeForHTML(String input); + * + * @param untrustedData + * the untrusted data to output encode for HTML + * + * @return the untrusted data safely output encoded for use in a HTML + */ + String encodeForHTML(String untrustedData); - /** + /** * Decodes HTML entities. * @param input the String to decode * @return the newly decoded String */ - 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); + String decodeForHTML(String input); + + /** + * Encode data for use in HTML attributes. + * + * @param untrustedData + * the untrusted data to output encode for an HTML attribute + * + * @return the untrusted data safely output encoded for use in a use as an HTML attribute + */ + String encodeForHTMLAttribute(String untrustedData); /** - * Encode data for insertion inside a data value or function argument in JavaScript. Including user data + * 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 + * + * 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: *

          *  <script>
          *      window.setInterval('<%= EVEN IF YOU ENCODE UNTRUSTED DATA YOU ARE XSSED HERE %>');
          *  </script>
          * 
    - * @param input - * the text to encode for JavaScript - * - * @return input encoded for use in JavaScript + * @param untrustedData + * the untrusted data to output encode for JavaScript + * + * @return the untrusted data safely output encoded for use in a use in JavaScript */ - String encodeForJavaScript(String input); + String encodeForJavaScript(String untrustedData); - /** - * 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 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 untrustedData + * the untrusted data to output encode for VBScript + * + * @return the untrusted data safely output encoded for use in a use in VBScript + */ + String encodeForVBScript(String untrustedData); - /** - * 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 JDBC Specification - * - * @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 input for use in a SQL query, according to the selected codec + * (appropriate codecs include the {@link org.owasp.esapi.codecs.MySQLCodec} + * and {@link org.owasp.esapi.codecs.OracleCodec}), but see + * "SECURITY WARNING" below before using. + *

    + * The this method attempts to ensure make sure any single-quotes are double-quoted + * (i.e., as '', not double-quotes, as in "). 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. + *

    + * SECURITY WARNING: This method is NOT recommended. The use of the {@code PreparedStatement} + * interface is the preferred approach. However, if for some reason + * this is impossible, then this method is provided as a significantly weaker + * alternative. In particular, it should be noted that if all you do to + * address potential SQL Injection attacks is to use this method to escape + * parameters, you will fail miserably. According to the + * + * OWASP SQL Injection Prevention Cheat Sheet, these are the primary + * defenses against SQL Injection (as of June 2025): + *

      + *
    • Option 1: Use of Prepared Statements (with Parameterized Queries)
    • + *
    • Option 2: Use of Properly Constructed Stored Procedures
    • + *
    • Option 3: Allow-list Input Validation
    • + *
    • Option 4: STRONGLY DISCOURAGED: Escaping All User Supplied Input
    • + *
    + *

    + * According to "Option 4" (which is what this method implements), that OWASP Cheat Sheet + * states: + *

    + * In this approach, the developer will escape all user input + * before putting it in a query. It is very database specific + * in its implementation. This methodology is frail compared + * to other defenses, and we CANNOT guarantee that this option + * will prevent all SQL injections in all situations. + *
    + * (Emphasis ours.) + *

    + * Note you could give yourself a slightly better chance at success if prior to + * escaping by this method, you first canonicalize the input and run it through + * some strong allow-list validation. We will not provide anymore details than + * that, lest we encourage its misuse; however, it should be noted that resorting + * to use this method--especially by itself--should rarely, if ever, used. It + * is intended as a last ditch, emergency, Hail Mary effort. (To be honest, you'd + * likely have more success setting up a WAF such as + * OWASP ModSecurity and + * OWASP CRS + * if you need a temporary emergency SQLi defense shield, but using {@code PreparedStatement} + * is still your best option if you have the time and resources. + *

    + * Note to AppSec / Security Auditor teams: If see this method being used in + * application code, the risk of an exploitable SQLi vulnerability is still high. We + * stress the importance of the first two Options discussed in the + * + * OWASP SQL Injection Prevention Cheat Sheet. If you allow this, we recommend only + * doing so for a limited time duration and in the meantime creating some sort of security + * exception ticket to track it. + *

    + * IMPORTANT NOTE: If you really do insist enabling leg cannon mode and use + * this method, then you MUST follow these instructions. Failure to do so will + * result in a {@link org.owasp.esapi.errors.NotConfiguredByDefaultException} being + * thrown when you try to call it. Thus to make it work, you need to add the implementation + * method corresponding to this interace (defined in the property "ESAPI.Encoder" + * (wihch defaults to "org.owasp.esapi.reference.DefaultEncoder") in your "ESAPI.properties" file, + * to the ESAPI property "ESAPI.dangerouslyAllowUnsafeMethods.methodNames". See + * the Security Bulletin #13 document referenced below for additional details. + *

    + * @see JDBC Specification + * @see java.sql.PreparedStatement + * @see ESAPI Security Bulletin #13 + * + * @param codec + * a {@link org.owasp.esapi.codecs.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 + * @see + * ESAPI Security Bulletin #13 + * @deprecated This method is considered dangerous and not easily made safe and thus under strong + * consideration to be removed within 1 years time after the 2.7.0.0 release. Please + * see the referenced ESAPI Security Bulletin #13 for further details. + */ + @Deprecated + 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). + * 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: * - * 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 + * + * @param codec * a Codec that declares which operating system 'input' is being encoded for (ie. Windows, Unix, etc.) - * @param input + * @param input * the text to encode for the command shell - * + * * @return input encoded for use in command shell */ - String encodeForOS(Codec codec, String input); + String encodeForOS(Codec codec, String input); + + /** + * Encode data for use in LDAP queries. Wildcard (*) characters will be encoded. + * + * This encoder operates according to RFC 4515, Section 3. RFC 4515 says the following character ranges + * are valid: 0x01-0x27, 0x2B-0x5B and 0x5D-0x7F. Characters outside the ranges are hex encoded, and they + * include 0x00 (NUL), 0x28 (LPAREN), 0x29 (RPAREN), 0x2A (ASTERISK), and 0x5C (ESC). The encoder will also + * encode 0x2F (FSLASH), which is required by Microsoft Active Directory. + * + * NB: At ESAPI 2.5.3, {@code encodeForLDAP} began strict conformance with RFC 4515. Characters above 0x7F + * are converted to UTF-8, and then the byte sequences are hex encoded according to the RFC. + * + * @param input + * the text to encode for LDAP + * + * @return input encoded for use in LDAP + * + * @see RFC 4515, Lightweight Directory Access Protocol + * (LDAP): String Representation of Search Filters + * + * @since ESAPI 1.3 + */ + String encodeForLDAP(String input); - /** - * Encode data for use in LDAP queries. Wildcard (*) characters will be encoded. - * - * @param input - * the text to encode for LDAP - * - * @return input encoded for use in LDAP - */ - String encodeForLDAP(String input); + /** + * Encode data for use in LDAP queries. You have the option whether or not to encode wildcard (*) characters. + * + * This encoder operates according to RFC 4515, Section 3. RFC 4515 says the following character ranges + * are valid: 0x01-0x27, 0x2B-0x5B and 0x5D-0x7F. Characters outside the ranges are hex encoded, and they + * include 0x00 (NUL), 0x28 (LPAREN), 0x29 (RPAREN), 0x2A (ASTERISK), and 0x5C (ESC). The encoder will also + * encode 0x2F (FSLASH), which is required by Microsoft Active Directory. + * + * NB: At ESAPI 2.5.3, {@code encodeForLDAP} began strict conformance with RFC 4515. Characters above 0x7F + * are converted to UTF-8, and then the byte sequences are hex encoded according to the RFC. + * + * @param input + * the text to encode for LDAP + * @param encodeWildcards + * whether or not wildcard (*) characters will be encoded. + * + * @return input encoded for use in LDAP + * + * @see RFC 4515, Lightweight Directory Access Protocol + * (LDAP): String Representation of Search Filters + * + * @since ESAPI 1.3 + */ + String encodeForLDAP(String input, boolean encodeWildcards); - /** - * Encode data for use in LDAP queries. You have the option whether or not to encode wildcard (*) characters. - * - * @param input - * the text to encode for LDAP - * @param encodeWildcards - * whether or not wildcard (*) characters will be encoded. - * - * @return input encoded for use in LDAP - */ - String encodeForLDAP(String input, boolean encodeWildcards); - - /** - * 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 LDAP distinguished name. + * + * This encoder operates according to RFC 4514, Section 3. RFC 4514 says the following character ranges + * are valid: 0x01-0x21, 0x23-0x2A, 0x2D-0x3A, 0x3D, 0x3F-0x5B, 0x5D-0x7F. Characters outside the ranges + * are hex encoded, and they include 0x00 (NUL), 0x22 (DQUOTE), 0x2B (PLUS), 0x2C (COMMA), + * 0x3B (SEMI), 0x3C (LANGLE), 0x3E (RANGLE) and 0x5C (ESC). The encoder will also encode 0x2F (FSLASH), + * which is required by Microsoft Active Directory. The leading and trailing characters in a distinguished + * name string will also have 0x20 (SPACE) and 0x23 (SHARP) encoded. + * + * NB: At ESAPI 2.5.3, {@code encodeForDN} began strict conformance with RFC 4514. Characters above 0x7F + * are converted to UTF-8, and then the byte sequences are hex encoded according to the RFC. + * + * @param input + * the text to encode for an LDAP distinguished name + * + * @return input encoded for use in an LDAP distinguished name + * + * @see RFC 4514, Lightweight Directory Access Protocol + * (LDAP): String Representation of Distinguished Names + * + * @since ESAPI 1.3 + */ + 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 this + * article which specifies the following list of characters as the most + * dangerous: ^ & " * ' ; < > ( ) . This + * paper suggests disallowing ' and " in queries. + * + * @see XPath Injection [ibm.com] + * @see Blind XPath Injection [packetstormsecurity.org] + * + * @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 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 this - * article which specifies the following list of characters as the most - * dangerous: ^&"*';<>(). This - * paper suggests disallowing ' and " in queries. - * - * @see XPath Injection [ibm.com] - * @see Blind XPath Injection [packetstormsecurity.org] - * - * @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 Character Encoding in Entities + * from W3C. + *

    + * 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 parser, this method provides + * a safe mechanism to do so. + * + * @see Character Encoding in Entities + * + * @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 element. The implementation should follow the XML Encoding - * Standard from the W3C. - *

    - * 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 XML Encoding Standard - * - * @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 Character Encoding in Entities + * from W3C. + *

    + * 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 Character Encoding in Entities + * + * @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 data for use in an XML attribute. The implementation should follow - * the XML Encoding - * Standard from the W3C. - *

    - * 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 XML Encoding Standard - * - * @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 URL encoding + * on the entire string. + * + * @see URL encoding + * + * @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; - /** - * Encode for use in a URL. This method performs URL encoding - * on the entire string. - * - * @see URL encoding - * - * @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; + /** + * Encode data for use in JSON strings. This method performs String escaping + * on the entire string according to RFC 8259, Section 7. + * + * @see RFC 8259, + * The JavaScript Object Notation (JSON) Data Interchange Format, Section 7 + * + * @param input + * the text to escape for JSON string + * + * @return input + * escaped for use in JSON string + */ + String encodeForJSON(String input); - /** - * 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; + /** + * 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); + /** + * 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; + /** + * 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; - /** - * Get a version of the input URI that will be safe to run regex and other validations against. - * It is not recommended to persist this value as it will transform user input. This method - * will not test to see if the URI is RFC-3986 compliant. - * - * @return The canonicalized URI - */ - String getCanonicalizedURI(URI dirtyUri); + /** + * Get a version of the input URI that will be safe to run regex and other validations against. + * It is not recommended to persist this value as it will transform user input. This method + * will not test to see if the URI is RFC-3986 compliant. + * + * @param dirtyUri + * the tainted URI + * @return The canonicalized URI + */ + String getCanonicalizedURI(URI dirtyUri); + /** + * Decode data encoded for JSON strings. This method removes String escaping + * on the entire string according to RFC 8259, Section 7. + * + * @see RFC 8259, + * The JavaScript Object Notation (JSON) Data Interchange Format, Section 7 + * + * @param input + * the JSON string to decode + * + * @return input + * decoded from JSON string + */ + String decodeFromJSON(String input); } diff --git a/src/main/java/org/owasp/esapi/EncoderConstants.java b/src/main/java/org/owasp/esapi/EncoderConstants.java index 1498d5ea7..ec2cbfac4 100644 --- a/src/main/java/org/owasp/esapi/EncoderConstants.java +++ b/src/main/java/org/owasp/esapi/EncoderConstants.java @@ -11,107 +11,107 @@ * @see User */ public class EncoderConstants { - /** - * !$*-.=?@_ - */ - public final static char[] CHAR_PASSWORD_SPECIALS = { '!', '$', '*', '-', '.', '=', '?', '@', '_' }; - public final static Set 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 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 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 DIGITS; - static { - DIGITS = CollectionsUtil.arrayToSet(CHAR_DIGITS); - } - - /** - * !$*+-.=?@^_|~ - */ - public final static char[] CHAR_SPECIALS = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' }; - public final static Set 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 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 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 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 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 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 PASSWORD_LETTERS; - static { - PASSWORD_LETTERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_LETTERS); - } + /** + * !$*-.=?@_ + */ + public final static char[] CHAR_PASSWORD_SPECIALS = { '!', '$', '*', '-', '.', '=', '?', '@', '_' }; + public final static Set PASSWORD_SPECIALS; + static { + PASSWORD_SPECIALS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS); + } - private EncoderConstants() { - // prevent instantiation - } + /** + * 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 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 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 DIGITS; + static { + DIGITS = CollectionsUtil.arrayToSet(CHAR_DIGITS); + } + + /** + * !$*+-.=?@^_|~ + */ + public final static char[] CHAR_SPECIALS = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' }; + public final static Set 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 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 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 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 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 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 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 index 9e1a857ac..b1db53112 100644 --- a/src/main/java/org/owasp/esapi/EncryptedProperties.java +++ b/src/main/java/org/owasp/esapi/EncryptedProperties.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2007-2019 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @created 2007 */ @@ -38,75 +38,75 @@ */ 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; + /** + * 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. + */ + 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. + */ + 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. + */ + void store(OutputStream out, String comments) throws IOException; + - /** - * 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. - */ - 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. - */ - 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. - */ - 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 index 6c83fef60..c1729a839 100644 --- a/src/main/java/org/owasp/esapi/Encryptor.java +++ b/src/main/java/org/owasp/esapi/Encryptor.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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 © 2007-2019 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @author kevin.w.wall@gmail.com * @created 2007 @@ -49,7 +49,7 @@ * section 4 of * * Design Goals in OWASP ESAPI Cryptography. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -57,177 +57,177 @@ */ 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 - * this article 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 + * this article 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 - * this article 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 Encryptor.CipherTransformation - * and the master encryption key as specified by the property - * {@code Encryptor.MasterKey} as defined in the ESAPI.properties file. - *

    - * - * @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; + /** + * 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 + * this article 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 Encryptor.CipherTransformation + * and the master encryption key as specified by the property + * {@code Encryptor.MasterKey} as defined in the ESAPI.properties file. + *

    + * + * @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 Encryptor.CipherTransformation - * as defined in the ESAPI.properties file and the - * specified secret key. - *

    - * 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 master encryption key as specified by the property - * {@code Encryptor.MasterKey} as defined in the {@code ESAPI.properties} - * file. - *

    - * @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 specified secret key. - *

    - * This decrypt method is similar to {@link #decrypt(CipherText)} except that - * it allows decrypting with a secret key other than the master secret key. - *

    - * @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. - *

    - * Limitations: 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. - *

    - * - * @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; + /** + * Encrypts the provided plaintext bytes using the cipher transformation + * specified by the property Encryptor.CipherTransformation + * as defined in the ESAPI.properties file and the + * specified secret key. + *

    + * 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; - /** - * Verifies a digital signature (created with the sign method) and returns - * the boolean result. + /** + * Decrypts the provided {@link CipherText} using the information from it + * and the master encryption key as specified by the property + * {@code Encryptor.MasterKey} as defined in the {@code ESAPI.properties} + * file. + *

    + * @param ciphertext The {@code CipherText} object to be decrypted. + * @return The {@code PlainText} object resulting from decrypting the specified + * ciphertext. Note that if it is desired to convert the returned + * plaintext byte array to a Java String it 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 specified secret key. + *

    + * This decrypt method is similar to {@link #decrypt(CipherText)} except that + * it allows decrypting with a secret key other than the master secret key. + *

    + * @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 if it is desired to convert the returned + * plaintext byte array to a Java String it 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. + *

    + * Limitations: 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. + *

    + * + * @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. *

    * Limitations: A new public/private key pair used for ESAPI 2.0 digital * signatures with this method and {@link #sign(String)} @@ -238,79 +238,79 @@ CipherText encrypt(SecretKey key, PlainText plaintext) * can not be used with expected results across JVM instances. This limitation * will be addressed in ESAPI 2.1. *

    - * @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); + * @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 - * + /** + * 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; + */ + 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 + */ + long getRelativeTimeStamp( long offset ); - /** - * 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 - */ - 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(); + /** + * 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 index e9835e300..6887b80c1 100644 --- a/src/main/java/org/owasp/esapi/ExecuteResult.java +++ b/src/main/java/org/owasp/esapi/ExecuteResult.java @@ -25,51 +25,51 @@ * @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; - } + private final int exitValue; + private final String output; + private final String errors; - /** - * @return the code from java.lang.Process.exitValue() - */ - public int getExitValue() { - return exitValue; - } + /** + * 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 contents read from java.lang.Process.getInputStream() - */ - public String getOutput() { - return output; - } + /** + * @return the code from java.lang.Process.exitValue() + */ + public int getExitValue() { + return exitValue; + } - /** - * @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+"]"; - } + /** + * @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 index b475da90d..122dcdb94 100644 --- a/src/main/java/org/owasp/esapi/Executor.java +++ b/src/main/java/org/owasp/esapi/Executor.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -23,56 +23,56 @@ /** * The Executor interface is used to run an OS command with reduced security risk. - * + * *

    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.

    - * + * of service attacks.

    + * *

    The class should perform logging and error handling as * well. Finally, implementation should handle errors and generate an * ExecutorException with all the necessary information.

    * *

    The reference implementation does all of the above.

    - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @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; + /** + * 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; + /** + * 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 index c1b83f5c4..582f1a1b7 100644 --- a/src/main/java/org/owasp/esapi/HTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/HTTPUtilities.java @@ -30,33 +30,38 @@ /** * The HTTPUtilities interface is a collection of methods that provide additional security related to HTTP requests, * responses, sessions, cookies, headers, and logging. - * + *

    + * Note: This most of the methods in this interface NOT compatible with the Jakarta Servlet API Spec 5.0 or later, which + * uses the jakarta.servlet package namespace rather than the javax.servlet package namespace. For further details, + * please see the GitHub Discussion issue + * Add support for Jakarta Servlet API Specification #768. + *

    * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 */ public interface HTTPUtilities { - // All implied static final as this is an interface + // All implied static final as this is an interface String REMEMBER_TOKEN_COOKIE_NAME = "rtoken"; int MAX_COOKIE_LEN = 4096; // From RFC 2109 - int MAX_COOKIE_PAIRS = 20; // From RFC 2109 - String CSRF_TOKEN_NAME = "ctoken"; - String ESAPI_STATE = "estate"; + int MAX_COOKIE_PAIRS = 20; // From RFC 2109 + String CSRF_TOKEN_NAME = "ctoken"; + String ESAPI_STATE = "estate"; - int PARAMETER = 0; - int HEADER = 1; - int COOKIE = 2; + int PARAMETER = 0; + int HEADER = 1; + int COOKIE = 2; - /** + /** * Calls addCookie with the *current* request. * * @param cookie The cookie to add - * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + * + * @see 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. @@ -66,7 +71,7 @@ public interface HTTPUtilities */ 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. * @@ -78,7 +83,7 @@ public interface HTTPUtilities /** * Calls addHeader with the *current* request. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) */ void addHeader(String name, String value); @@ -95,49 +100,49 @@ public interface HTTPUtilities */ void addHeader(HttpServletResponse response, String name, String value); - /** + /** * Calls assertSecureRequest with the *current* request. - * @see HTTPUtilities#assertSecureRequest(HttpServletRequest) - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - void assertSecureRequest() throws AccessControlException; + * @see HTTPUtilities#assertSecureRequest(HttpServletRequest) + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ + void assertSecureRequest() throws AccessControlException; - /** + /** * Calls assertSecureChannel with the *current* request. - * @see HTTPUtilities#assertSecureChannel(HttpServletRequest) - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - void assertSecureChannel() throws AccessControlException; + * @see HTTPUtilities#assertSecureChannel(HttpServletRequest) + * @see 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. + /** + * 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. + /** + * 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 HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) */ - HttpSession changeSessionIdentifier() throws AuthenticationException; + 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 @@ -150,34 +155,34 @@ public interface HTTPUtilities HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException; /** - * Clears the current HttpRequest and HttpResponse associated with the current thread. + * Clears the current HttpRequest and HttpResponse associated with the current thread. * - * @see ESAPI#clearCurrent() - */ + * @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. + * 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); + * @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. + * 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 - */ + * @param encrypted the encrypted querystring to decrypt + * @return a Map object containing the decrypted querystring + * @throws EncryptionException + */ Map decryptQueryString(String encrypted) throws EncryptionException; /** * Calls decryptStateFromCookie with the *current* request. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) */ Map decryptStateFromCookie() throws EncryptionException; @@ -186,7 +191,7 @@ public interface HTTPUtilities * * @param request * @return a map containing the decrypted cookie state value - * @throws EncryptionException + * @throws EncryptionException */ Map decryptStateFromCookie(HttpServletRequest request) throws EncryptionException; @@ -197,22 +202,22 @@ public interface HTTPUtilities * @return the encrypted value of the hidden field * @throws EncryptionException */ - String encryptHiddenField(String value) 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. + /** + * 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; + * @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. + /** + * Calls encryptStateInCookie with the *current* response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ void encryptStateInCookie(Map cleartext) throws EncryptionException; /** @@ -229,28 +234,28 @@ public interface HTTPUtilities void encryptStateInCookie(HttpServletResponse response, Map cleartext) throws EncryptionException; /** - * Calls getCookie with the *current* response. - * - * @param name The cookie to get + * Calls getCookie with the *current* response. + * + * @param name The cookie to get * @return the requested cookie value * @throws ValidationException * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - String getCookie(String name) throws ValidationException; + * @see 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 The cookie to get + * @param name The cookie to get * @return the requested cookie value * @throws ValidationException */ - String getCookie(HttpServletRequest request, String name) throws ValidationException; + String getCookie(HttpServletRequest request, String name) throws ValidationException; /** * Returns the current user's CSRF token. If there is no current user then return null. @@ -259,68 +264,144 @@ public interface HTTPUtilities */ 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 + /** + * Calls {@code getFileUploads} with the current request, default upload directory, and default allowed file extensions * * @return List of new File objects from upload * @throws ValidationException if the file fails validation - * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - List getFileUploads() throws ValidationException; + * + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ + List getFileUploads() throws ValidationException; /** - * Call getFileUploads with the specified request, default upload directory, and default allowed file extensions + * Call {@code getFileUploads} with the specified request, default upload directory, and default allowed file extensions + * + * @param request The applicable HTTP request * * @return List of new File objects from upload * @throws ValidationException if the file fails validation - */ - List getFileUploads(HttpServletRequest request) throws ValidationException; + */ + List getFileUploads(HttpServletRequest request) throws ValidationException; /** - * Call getFileUploads with the specified request, specified upload directory, and default allowed file extensions + * Call {@code getFileUploads} with the specified request, specified upload directory, and default allowed file extensions + * + * @param request The applicable HTTP request + * @param finalDir The destination directory to leave the uploaded file(s) in. * * @return List of new File objects from upload * @throws ValidationException if the file fails validation - */ + */ 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 + * Extract uploaded files from a multipart/form-data HTTP request. 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. - *

    - * This method uses {@link HTTPUtilities#getCurrentRequest()} to obtain the {@link HttpServletRequest} object - * - * @param request - * @return List of new File objects from upload + * information. Important Note: The ESAPI reference implementation (i.e., + * {@code org.owasp.esapi.referenceDefaultHTTPUtilities} only does some of these things listed above and some of those + * are limited to which {@code getFileUploads} method is called and how you've set your relevant ESAPI properties + * in your ESAPI.properties file. + *

    + * This method uses {@link HTTPUtilities#getCurrentRequest()} to obtain the + * {@link javax.servlet.http.HttpServletRequest HttpServletRequest} + * object. If the ESAPI property HttpUtilities.FileUploadAllowAnonymousUser is set to {@code false} (the + * default is {@code true}), then {@code getFileUploads} will call {@code ESAPI.authenticator().getCurrentUser()} + * to check if the user is authenticated. If that property is set to {@code false} and a call to that function returns + * an anonymous (i.e., unauthenticated) user, then the file upload is blocked. + *

    + * ESAPI properties relevant to this and the other {@code getFileUploads} methods referenced in this table. The + * last 2 properties are new since release 2.5.2.0: + *

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    ESAPI Property NameESAPI.properties DefaultBuiltin DefaultMeaning
    HttpUtilities.UploadDirC:\ESAPI\testUploadUploadDirFinal destination directory for uploaded files.
    HttpUtilities.UploadTempDirC:\tempValue of system property java.io.tmpdirTemporary staging directory for uploaded files.
    HttpUtilities.ApprovedUploadExtensions.pdf,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.rtf,.txt,.jpg,.pn.pdf,.txt,.jpg,.pngComma separated allowed list of file suffixes that may be uploaded.
    HttpUtilities.MaxUploadFileBytes50000005000000Total maximum upload file size for uploaded files per HTTP request.
    HttpUtilities.MaxUploadFileCount2020Maximum total number of uploaded files per HTTP request.
    HttpUtilities.FileUploadAllowAnonymousUsertruetrueControls whether anonymous (i.e., unauthenticated) users may upload files.
    + *

    + * As alluded to above, it is important to note that these {@code getFileUploads} methods do not do + * everything to keey your application and environment secure. Some of the more obvious omissions are the + * absence of examining the actual file content to determine the actual file type or running some AV scan + * on the uploaded files. You have to add that functionality to you if you want or need that. Some + * resource that you may find useful are: + *

    + * + * @param request The applicable HTTP request + * @param destinationDir The destination directory to leave the uploaded file in. + * @param allowedExtensions Permitted file suffixes. (Yes, this is a weak check. Use Apache Tika if you + * want something more.) + * @return List of new {@code File} objects from upload * @throws ValidationException if the file fails validation + * @throws java.security.AccessControlException If anonymous users are not allowed and the user is + * not authenticated as per the ESAPI {@code Authenticator}. */ List getFileUploads(HttpServletRequest request, File destinationDir, List allowedExtensions) throws ValidationException; - /** - * Calls getHeader with the *current* request. + /** + * Calls getHeader with the *current* request. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - String getHeader(String name) throws ValidationException; + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ + String getHeader(String name) throws ValidationException; /** * A safer replacement for getHeader() in HttpServletRequest that returns the canonicalized @@ -332,14 +413,14 @@ public interface HTTPUtilities * @param name * @return the requested header value */ - String getHeader(HttpServletRequest request, String name) throws ValidationException; + String getHeader(HttpServletRequest request, String name) throws ValidationException; - /** - * Calls getParameter with the *current* request. + /** + * Calls getParameter with the *current* request. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - String getParameter(String name) throws ValidationException; + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ + String getParameter(String name) throws ValidationException; /** * A safer replacement for getParameter() in HttpServletRequest that returns the canonicalized @@ -353,12 +434,12 @@ public interface HTTPUtilities */ String getParameter(HttpServletRequest request, String name) throws ValidationException; - /** - * Calls killAllCookies with the *current* request and response. + /** + * Calls killAllCookies with the *current* request and response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - void killAllCookies(); + * @see 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 @@ -369,12 +450,12 @@ public interface HTTPUtilities */ void killAllCookies(HttpServletRequest request, HttpServletResponse response); - /** - * Calls killCookie with the *current* request and response. + /** + * Calls killCookie with the *current* request and response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - void killCookie(String name); + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ + void killCookie(String name); /** * Kills the specified cookie by setting a new cookie that expires immediately. Note that this @@ -386,12 +467,12 @@ public interface HTTPUtilities */ void killCookie(HttpServletRequest request, HttpServletResponse response, String name); - /** - * Calls logHTTPRequest with the *current* request and logger. + /** + * Calls logHTTPRequest with the *current* request and logger. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ - void logHTTPRequest(); + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ + void logHTTPRequest(); /** * Format the Source IP address, URL, URL parameters, and all form @@ -420,11 +501,11 @@ public interface HTTPUtilities */ void logHTTPRequest(HttpServletRequest request, Logger logger, List parameterNamesToObfuscate); - /** - * Calls sendForward with the *current* request and response. + /** + * Calls sendForward with the *current* request and response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ void sendForward(String location) throws AccessControlException, ServletException, IOException; /** @@ -443,11 +524,11 @@ public interface HTTPUtilities void sendForward(HttpServletRequest request, HttpServletResponse response, String location) throws AccessControlException, ServletException, IOException; - /** - * Calls sendRedirect with the *current* response. + /** + * Calls sendRedirect with the *current* response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ void sendRedirect(String location) throws AccessControlException, IOException; @@ -464,25 +545,25 @@ public interface HTTPUtilities */ void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException; - /** - * Calls setContentType with the *current* request and response. + /** + * Calls setContentType with the *current* request and response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * @see 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. + * 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. *

    - * Implementations of this method should set the content type header to a safe value for your environment. + * 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. + * versions of HTML and HTTP. See RFC 2047 (http://ds.internic.net/rfc/rfc2045.txt) for more + * information about character encoding and MIME. *

    - * The DefaultHTTPUtilities reference implementation sets the content type as specified. + * The DefaultHTTPUtilities reference implementation sets the content type as specified. * * @param response The servlet response to set the content type for. */ @@ -498,11 +579,11 @@ public interface HTTPUtilities void setCurrentHTTP(HttpServletRequest request, HttpServletResponse response); - /** - * Calls setHeader with the *current* response. + /** + * Calls setHeader with the *current* response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ void setHeader(String name, String value); /** @@ -518,11 +599,11 @@ public interface HTTPUtilities void setHeader(HttpServletResponse response, String name, String value); - /** - * Calls setNoCacheHeaders with the *current* response. + /** + * Calls setNoCacheHeaders with the *current* response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ void setNoCacheHeaders(); @@ -556,67 +637,67 @@ public interface HTTPUtilities */ void setNoCacheHeaders(HttpServletResponse response); - /** - * Calls setNoCacheHeaders with the *current* response. - * - * ~DEPRECATED~ Per Kevin Wall, storing passwords with reversible encryption is contrary to *many* - * company's stated security policies. + /** + * Calls setNoCacheHeaders with the *current* response. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * ~DEPRECATED~ Per Kevin Wall, storing passwords with reversible encryption is contrary to *many* + * company's stated security policies. + * + * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) + */ @Deprecated String setRememberToken(String password, int maxAge, String domain, String path); /** - * + * */ String setRememberToken(HttpServletRequest request, HttpServletResponse response, 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 + * 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. *

    * 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 + * 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. *

    - * The ESAPI reference implementation, DefaultHTTPUtilities.setRememberToken() implements all these suggestions. + * The ESAPI reference implementation, DefaultHTTPUtilities.setRememberToken() implements all these suggestions. *

    * The username can be retrieved with: User username = ESAPI.authenticator().getCurrentUser(); * *~DEPRECATED~ Per Kevin Wall, storing passwords with reversible encryption is contrary to *many* - * company's stated security policies. + * company's stated security policies. * * @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 - */ + * @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 + */ @Deprecated String setRememberToken(HttpServletRequest request, HttpServletResponse response, String password, int maxAge, String domain, String path); - /** - * Calls verifyCSRFToken with the *current* request. + /** + * Calls verifyCSRFToken with the *current* request. * - * @see HTTPUtilities#setCurrentHTTP(HttpServletRequest, HttpServletResponse) - */ + * @see 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. + * throws an IntrusionException if it is missing. * * @param request * @throws IntrusionException if CSRF token is missing or incorrect - */ + */ void verifyCSRFToken(HttpServletRequest request) throws IntrusionException; /** diff --git a/src/main/java/org/owasp/esapi/IntrusionDetector.java b/src/main/java/org/owasp/esapi/IntrusionDetector.java index 6a14ec3c3..ccb2bc984 100644 --- a/src/main/java/org/owasp/esapi/IntrusionDetector.java +++ b/src/main/java/org/owasp/esapi/IntrusionDetector.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -25,23 +25,23 @@ *

    * 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) Aspect Security * @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 + * 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 + * + * @param exception + * the exception thrown + * + * @throws IntrusionException + * the intrusion exception */ void addException(Exception exception) throws IntrusionException; @@ -49,14 +49,14 @@ public interface IntrusionDetector { * 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 + * + * @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 index e3736986d..7571ea460 100644 --- a/src/main/java/org/owasp/esapi/LogFactory.java +++ b/src/main/java/org/owasp/esapi/LogFactory.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 DawesAspect Security * @created 2008 */ @@ -18,43 +18,43 @@ /** * 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. - * + * + * 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. - *

    - * 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. - *

    - * 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); - + + /** + * 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. + *

    + * 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. + *

    + * 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 index fd20331ce..b0a5b7ccc 100644 --- a/src/main/java/org/owasp/esapi/Logger.java +++ b/src/main/java/org/owasp/esapi/Logger.java @@ -1,27 +1,27 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2007-2019 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @created 2007 */ package org.owasp.esapi; /** - * The Logger interface defines a set of methods that can be used to log + * The {@code 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. - *

    + *

    * The logging levels defined by this interface (in descending order) are: *

    * There are also several variations of {@code always()} methods that will always * log a message regardless of the log level. - *

    + *

    * 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: + * The {@code Logger} interface predefines 6 types of Log events: *

    - *

    - * Your implementation can extend or change this list if desired. - *

    - * This Logger allows callers to determine which logging levels are enabled, and to submit events + *

    + * Your custom implementation can extend or change this list if desired. + *

    + * This {@code Logger} allows callers to determine which logging levels are enabled, and to submit events * at different severity levels.
    *
    Implementors of this interface should: - * + * *

      - *
    1. provide a mechanism for setting the logging level threshold that is currently enabled. This usually works by logging all + *
    2. 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.
    3. - *
    4. ensure that dangerous HTML characters are encoded before they are logged to defend against malicious injection into logs + *
    5. 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.
    6. - *
    7. encode any CRLF characters included in log data in order to prevent log injection attacks.
    8. - *
    9. 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.
    10. - *
    11. record the following information with each event:
    12. + *
    13. Encode any CRLF characters included in log data in order to prevent log injection attacks.
    14. + *
    15. 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.
    16. + *
    17. Record the following information with each event:
    18. *
        - *
      1. identity of the user that caused the event,
      2. - *
      3. a description of the event (supplied by the caller),
      4. - *
      5. whether the event succeeded or failed (indicated by the caller),
      6. - *
      7. severity level of the event (indicated by the caller),
      8. - *
      9. that this is a security relevant event (indicated by the caller),
      10. - *
      11. hostname or IP where the event occurred (and ideally the user's source IP as well),
      12. - *
      13. a time stamp
      14. + *
      15. Identity of the user that caused the event.
      16. + *
      17. A description of the event (supplied by the caller).
      18. + *
      19. Whether the event succeeded or failed (indicated by the caller).
      20. + *
      21. Severity level of the event (indicated by the caller).
      22. + *
      23. That this is a security relevant event (indicated by the caller).
      24. + *
      25. Hostname or IP where the event occurred (and ideally the user's source IP as well).
      26. + *
      27. A date/time stamp.
      28. *
      *
    - * + * * Custom logger implementations might also: *
      - *
    1. filter out any sensitive data specific to the current application or organization, such as credit cards, + *
    2. Filter out any sensitive data specific to the current application or organization, such as credit cards, * social security numbers, etc.
    3. *
    - * - * 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.
    - *
    - * 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. - * + * + * There are both SLF4J and native Java Logging (i.e., {@code java.util.logging}, aka JUL) implementations + * of the ESAPI logger with JUL being our default logger for our stock ESAPI.properties file that + * is delivered along with ESAPI releases in a separate esapi-configuration jar available from the + * releases mentioned on + * ESAPI's GitHub Releases page. + *

    + * The {@code org.owasp.esapi.logging.java.JavaLogger} class uses the {@code java.util.logging} package as + * the basis for its logging implementation. Both provided implementations implement requirements #1 through #5 above. + *

    + * Customization: It is expected that most organizations may wish to implement their own custom {@code Logger} class in + * order to integrate ESAPI logging with their specific logging infrastructure. The ESAPI reference implementations + * can serve as a useful starting point to intended to provide a simple functional example of an implementation, but + * they are also largely usable out-of-the-box with some additional minimal log configuration. + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 */ public interface Logger { - // All implied static final as this is an interface - - /** + // All implied static final as this is an interface + + /** * A security type of log event that has succeeded. This is one of 6 predefined * ESAPI logging events. New events can be added. */ - EventType SECURITY_SUCCESS = new EventType( "SECURITY SUCCESS", true); + 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. */ - EventType SECURITY_FAILURE = new EventType( "SECURITY FAILURE", false); + 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. - EventType SECURITY_AUDIT = new EventType( "SECURITY AUDIT", null); + /** + * 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. + 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. */ - EventType EVENT_SUCCESS = new EventType( "EVENT SUCCESS", true); - - /** + 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. */ - EventType EVENT_FAILURE = new EventType( "EVENT FAILURE", false); + 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. */ - 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. - */ - 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; - } - + 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. + */ + 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 + @Override public String toString() { - return this.type; - } - } - - /* - * The Logger interface defines 6 logging levels: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE. It also + 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. + * 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. */ - int OFF = Integer.MAX_VALUE; - /** FATAL indicates that only FATAL messages should be logged. This level is initialized to 1000. */ - int FATAL = 1000; + /** OFF indicates that no messages should be logged. This level is initialized to Integer.MAX_VALUE. */ + int OFF = Integer.MAX_VALUE; + + /** FATAL indicates that only FATAL messages should be logged. This level is initialized to 1000. */ + int FATAL = 1000; - /** ERROR indicates that ERROR messages and above should be logged. - * This level is initialized to 800. */ + /** ERROR indicates that ERROR messages and above should be logged. + * This level is initialized to 800. */ int ERROR = 800; - /** WARNING indicates that WARNING messages and above should be logged. + /** WARNING indicates that WARNING messages and above should be logged. * This level is initialized to 600. */ int WARNING = 600; - /** INFO indicates that INFO messages and above should be logged. + /** INFO indicates that INFO messages and above should be logged. * This level is initialized to 400. */ int INFO = 400; - /** DEBUG indicates that DEBUG messages and above should be logged. + /** DEBUG indicates that DEBUG messages and above should be logged. * This level is initialized to 200. */ int DEBUG = 200; - /** TRACE indicates that TRACE messages and above should be logged. + /** TRACE indicates that TRACE messages and above should be logged. * This level is initialized to 100. */ int TRACE = 100; /** ALL indicates that all messages should be logged. This level is initialized to Integer.MIN_VALUE. */ int ALL = Integer.MIN_VALUE; - + /** - * Dynamically set the ESAPI logging severity level. All events of this level and higher will be logged from + * 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. + * + * @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()}. - * + + /** Retrieve the current ESAPI logging level for this logger. + * * @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 + * + * @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 + 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 + * + * @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); + 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(); + /** + * 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 + * + * @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 + 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 + * + * @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); + 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(); + /** + * 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 + * + * @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 + 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 + * + * @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); + 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(); + /** + * 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 + * + * @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 + 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 + * + * @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); + 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(); + /** + * 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 + * + * @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 + 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 + * + * @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); + 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(); + /** + * 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 + * + * @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 + 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 + * + * @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); + 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(); + /** + * 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 + *
    + * Note that logging will not occur if the underlying logging implementation has logging disabled. + * + * @param type + * the type of event + * @param message + * the message to log */ - void always(EventType type, String message); - - /** + 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 + *
    + * Note that logging will not occur if the underlying logging implementation has logging disabled. + * + * @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); + 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 index ba26087f6..5102a324c 100644 --- a/src/main/java/org/owasp/esapi/PreparedString.java +++ b/src/main/java/org/owasp/esapi/PreparedString.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2007-2019 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @created 2009 */ @@ -21,117 +21,117 @@ /** * 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. - *

     
    + * 
      * 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() );
      * 
    - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 */ public class PreparedString { - char parameterCharacter = '?'; - Codec codec = null; - String[] parameters = null; - ArrayList parts = new ArrayList(); - private final static char[] IMMUNE = {}; + 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]; + } - /** - * 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 ); - } + /** + * 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; + } - /** - * 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 ); - } + /** + * 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; + } - /** - * 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(); - } + /** + * 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/PropNames.java b/src/main/java/org/owasp/esapi/PropNames.java new file mode 100644 index 000000000..8aa4179a9 --- /dev/null +++ b/src/main/java/org/owasp/esapi/PropNames.java @@ -0,0 +1,206 @@ +// TODO: Discuss: Should the name of this be PropConstants or PropertConstants +// since there are some property values included here? I don't +// really like that as much as PropNames, but I could live with +// it. +/* + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * https://owasp.org/www-project-enterprise-security-api/. + * + * Copyright (c) 2022 - The OWASP Foundation + * + * The ESAPI 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; + + +/** + * This non-constructable class of public constants defines all the property names used in {@code ESAPI.properties} as + * well as some of the default property values for some of those properties. This class is not intended + * to be extended or instantiated. Technically, an interface would have worked here, but we + * also wanted to be able to prevent 'implements PropNames', which really does not make much + * sense since no specific behavior is promised here. Another alternative would have + * been to place all of these in the {@code org.owasp.esapi.SecurityConfiguration} interface, + * but that interface is already overly bloated. Hence this was decided as a compromise. + *

    + * Note that the constants herein were originally all defined within + * {@code org.owasp.esapi.reference.DefaultSecurityConfiguration}, but those + * values are now marked deprecated and they are candidates for removal 2 years + * from the date of this release. + *

    + * Mostly this is intended to prevent having to hard-code property names all + * over the place in implementation-level classes (e.g., + * {@code org.owasp.esapi.reference.DefaultSecurityConfiguration}). + * It is suggested that this file be used as a 'static import'; + * e.g., + *

    + *      import static org.owasp.esapi.PropNames.*;                      // Import all properties, en masse
    + * or
    + *      import static org.owasp.esapi.PropNames.SomeSpecificPropName;   // Import specific property name
    + * 
    + * This can be extremely useful when used with methods such as + * {@code SecurityConfiguration.getIntProp(String propName)}, + * {@code SecurityConfiguration.getBooleanProp(String propName)}, + * {@code SecurityConfiguration.getStringProp(String propName)}, etc. + * + * @author Kevin W. Wall (kevin.w.wall .at. gmail.com) + * @since 2.4.1.0 + * @see org.owasp.esapi.reference.DefaultSecurityConfiguration + */ + +public final class PropNames { + + 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"; + 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"; // Will be removed in future release. + 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 ACCEPTED_UNSAFE_METHOD_NAMES = "ESAPI.dangerouslyAllowUnsafeMethods.methodNames"; + public static final String ACCEPTED_UNSAFE_METHODS_JUSTIFICATION = "ESAPI.dangerouslyAllowUnsafeMethods.justification"; + + 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 MAX_UPLOAD_FILE_COUNT = "HttpUtilities.MaxUploadFileCount"; + public static final String FILEUPLOAD_ALLOW_ANONYMOUS_USERS = "HttpUtilities.FileUploadAllowAnonymousUser"; + 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_USER_INFO = "Logger.UserInfo"; + public static final String LOG_CLIENT_INFO = "Logger.ClientInfo"; + 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 LOG_PREFIX = "Logger.LogPrefix"; + + public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile"; + public static final String VALIDATION_PROPERTIES_MULTIVALUED = "Validator.ConfigurationFile.MultiValued"; + public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates"; + public static final String VALIDATOR_HTML_VALIDATION_ACTION = "Validator.HtmlValidationAction"; + public static final String VALIDATOR_HTML_VALIDATION_CONFIGURATION_FILE = "Validator.HtmlValidationConfigurationFile"; + + /** + * Special {@code java.lang.System} property that, if set to {@code true}, will + * disable logging from {@code DefaultSecurityConfiguration.logToStdout()} + * methods, which is called from various {@code logSpecial()} methods. + * + * @see org.owasp.esapi.reference.DefaultSecurityConfiguration#logToStdout(String msg, Throwable t) + */ + public static final String DISCARD_LOGSPECIAL = "org.owasp.esapi.logSpecial.discard"; + + /* + * Implementation Keys for the various major ESAPI components. + */ + 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"; + + + ////////////////////////////////////////////////////////////////////////////// + // // + // These are not really property names, but the shouldn't really be in an // + // implementation class that we want to only deal with via the // + // SecurityConfiguration interface. // + // // + ////////////////////////////////////////////////////////////////////////////// + + + /* + * These are default implementation classes. + */ + public static final String DEFAULT_LOG_IMPLEMENTATION = "org.owasp.esapi.logging.java.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.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"; + + /** The name of the ESAPI property file */ + public static final String DEFAULT_RESOURCE_FILE = "ESAPI.properties"; + + // + // Private CTOR to prevent creation of PropName objects. We wouldn't need + // this if this were an interface, nor would we need the explict 'public static final'. + // + private PropNames() { + throw new AssertionError("Thought you'd cheat using reflection or JNI, huh? :)"); + } + + + /** Enum used with the search paths used to locate an + * {@code ESAPI.properties} and/or a {@code validation.properties} + * file. + */ + public enum DefaultSearchPath { + + RESOURCE_DIRECTORY("resourceDirectory/"), + SRC_MAIN_RESOURCES("src/main/resources/"), + ROOT(""), + DOT_ESAPI(".esapi/"), + ESAPI("esapi/"), + RESOURCES("resources/"); + + private final String path; + + private DefaultSearchPath(String s){ + this.path = s; + } + + public String value(){ + return path; + } + } +} diff --git a/src/main/java/org/owasp/esapi/Randomizer.java b/src/main/java/org/owasp/esapi/Randomizer.java index ae144b04f..bef0c4a1b 100644 --- a/src/main/java/org/owasp/esapi/Randomizer.java +++ b/src/main/java/org/owasp/esapi/Randomizer.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -30,118 +30,118 @@ */ 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 generator used should comply with the - * statistical random number generator tests specified in - * FIPS 140-2, Security Requirements for Cryptographic Modules, 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); + /** + * 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 generator used should comply with the + * statistical random number generator tests specified in + * FIPS 140-2, Security Requirements for Cryptographic Modules, 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 generator used should comply with the + * statistical random number generator tests specified in + * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. + * + * @return + * true or false, randomly + */ + boolean getRandomBoolean(); + + /** + * Gets the random integer in the range of [min, max). 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 generator used should comply with the + * statistical random number generator tests specified in + * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. + * + * @param min + * the minimum integer that will be returned, inclusive + * @param max + * the maximum integer that will be returned, exclusive + * + * @return + * the random integer + */ + int getRandomInteger(int min, int max); - /** - * 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 generator used should comply with the - * statistical random number generator tests specified in - * FIPS 140-2, Security Requirements for Cryptographic Modules, 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 generator used should comply with the - * statistical random number generator tests specified in - * FIPS 140-2, Security Requirements for Cryptographic Modules, 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 generator used should comply with the - * statistical random number generator tests specified in - * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. - * - * @return - * the random long - */ + /** + * 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 generator used should comply with the + * statistical random number generator tests specified in + * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. + * + * @return + * the random long + */ 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 + * 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 + * + * @param extension + * extension to add to the random filename + * + * @return + * a random unguessable filename ending with the specified extension */ 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 generator used should comply with the - * statistical random number generator tests specified in - * FIPS 140-2, Security Requirements for Cryptographic Modules, 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); + + + /** + * Gets the random real in the range of [min, max]. 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 generator used should comply with the + * statistical random number generator tests specified in + * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. + * + * @param min + * the minimum real number that will be returned, inclusive + * @param max + * the maximum real number that will be returned, inclusive + * + * @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 + * 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. *

    * For more information including algorithms used to create UUIDs, * see the Internet-Draft UUIDs and GUIDs * or the standards body definition at ISO/IEC 11578:1996. - * @return - * the GUID - * - * @throws - * EncryptionException if hashing or encryption fails + * @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. + * @param n The requested number of random bytes. * @return The {@code n} random bytes are returned. */ byte[] getRandomBytes(int n); - + } diff --git a/src/main/java/org/owasp/esapi/SafeFile.java b/src/main/java/org/owasp/esapi/SafeFile.java index 7df86bb38..e048e9419 100644 --- a/src/main/java/org/owasp/esapi/SafeFile.java +++ b/src/main/java/org/owasp/esapi/SafeFile.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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 - The OWASP Foundation - * + * * The ESAPI is 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 Aspect Security * @created 2008 */ @@ -30,78 +30,78 @@ */ 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 ( null != m1 && m1.find() ) { - throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains illegal character: " + m1.group() ); - } - - Matcher m2 = PERCENTS_PAT.matcher( path ); - if (null != m2 && 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; - } + 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 ( null != m1 && m1.find() ) { + throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains illegal character: " + m1.group() ); + } + + Matcher m2 = PERCENTS_PAT.matcher( path ); + if (null != m2 && 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 index ed326d6ae..e0b529b49 100644 --- a/src/main/java/org/owasp/esapi/SecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/SecurityConfiguration.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @author Jeff Williams Aspect Security * @created 2007 @@ -45,276 +45,276 @@ *

    * The ESAPI reference implementation (DefaultSecurityConfiguration.java) does * not encrypt its properties file. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 */ public interface SecurityConfiguration extends EsapiPropertyLoader { - /** - * Gets the application name, used for logging - * - * @return the name of the current application + /** + * Gets the application name, used for logging + * + * @return the name of the current application * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getApplicationName(); - - /** - * Returns the fully qualified classname of the ESAPI Logging implementation. + */ + @Deprecated + String getApplicationName(); + + /** + * Returns the fully qualified classname of the ESAPI Logging implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getLogImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI Authentication implementation. + */ + @Deprecated + String getLogImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI Authentication implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getAuthenticationImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI Encoder implementation. + */ + @Deprecated + String getAuthenticationImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI Encoder implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getEncoderImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI Access Control implementation. + */ + @Deprecated + String getEncoderImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI Access Control implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getAccessControlImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI Intrusion Detection implementation. + */ + @Deprecated + String getAccessControlImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI Intrusion Detection implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getIntrusionDetectionImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI Randomizer implementation. + */ + @Deprecated + String getIntrusionDetectionImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI Randomizer implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getRandomizerImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI Encryption implementation. + */ + @Deprecated + String getRandomizerImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI Encryption implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getEncryptionImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI Validation implementation. + */ + @Deprecated + String getEncryptionImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI Validation implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getValidationImplementation(); - - /** - * Returns the validation pattern for a particular type - * @param typeName - * @return the validation pattern - */ + */ + @Deprecated + String getValidationImplementation(); + + /** + * Returns the validation pattern for a particular type + * @param typeName + * @return the validation pattern + */ 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) * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated boolean getLenientDatesAccepted(); - - /** - * Returns the fully qualified classname of the ESAPI OS Execution implementation. + + /** + * Returns the fully qualified classname of the ESAPI OS Execution implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getExecutorImplementation(); - - /** - * Returns the fully qualified classname of the ESAPI HTTPUtilities implementation. + */ + @Deprecated + String getExecutorImplementation(); + + /** + * Returns the fully qualified classname of the ESAPI HTTPUtilities implementation. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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 + */ + @Deprecated + 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 * @deprecated Use SecurityConfiguration.getByteArrayProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - byte[] getMasterKey(); + */ + @Deprecated + byte[] getMasterKey(); - /** + /** * Retrieves the upload directory as specified in the ESAPI.properties file. * @return the upload directory */ File getUploadDirectory(); - + /** * Retrieves the temp directory to use when uploading files, as specified in ESAPI.properties. * @return the temp directory */ File getUploadTempDirectory(); - /** - * Gets the key length to use in cryptographic operations declared in the ESAPI properties file. + /** + * Gets the key length to use in cryptographic operations declared in the ESAPI properties file. * Note that this corresponds to the ESAPI property Encryptor.EncryptionKeyLength which is * considered the default key size that ESAPI will use for symmetric * ciphers supporting multiple key sizes. (Note that there is also an Encryptor.MinEncryptionKeyLength, * which is the minimum key size (in bits) that ESAPI will support - * for encryption. (There is no miminimum for decryption.) - * - * @return the key length (in bits) + * for encryption. (There is no minimum for decryption.) + * + * @return the key length (in bits) * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated + */ + @Deprecated 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 + + /** + * 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 * @deprecated Use SecurityConfiguration.getByteArrayProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - byte[] getMasterSalt(); - - /** - * Gets the allowed executables to run with the Executor. - * - * @return a list of the current allowed file extensions - */ - List getAllowedExecutables(); - - /** - * Gets the allowed file extensions for files that are uploaded to this application. - * - * @return a list of the current allowed file extensions - */ - List getAllowedFileExtensions(); - - /** - * Gets the maximum allowed file upload size. - * - * @return the current allowed file upload size + */ + @Deprecated + byte[] getMasterSalt(); + + /** + * Gets the allowed executables to run with the Executor. + * + * @return a list of the current allowed file extensions + */ + List getAllowedExecutables(); + + /** + * Gets the allowed file extensions for files that are uploaded to this application. + * + * @return a list of the current allowed file extensions + */ + List getAllowedFileExtensions(); + + /** + * Gets the maximum allowed file upload size. + * + * @return the current allowed file upload size * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - int getAllowedFileUploadSize(); - - /** - * Gets the name of the password parameter used during user authentication. - * - * @return the name of the password parameter + */ + @Deprecated + int getAllowedFileUploadSize(); + + /** + * Gets the name of the password parameter used during user authentication. + * + * @return the name of the password parameter * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getPasswordParameterName(); - - /** - * Gets the name of the username parameter used during user authentication. - * - * @return the name of the username parameter + */ + @Deprecated + String getPasswordParameterName(); + + /** + * Gets the name of the username parameter used during user authentication. + * + * @return the name of the username parameter * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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 + */ + @Deprecated + 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 * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getEncryptionAlgorithm(); - - /** - * Retrieve the cipher transformation. 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: - *

    -	 * 		cipher_alg/cipher_mode[bits]/padding_scheme
    -	 * 
    - * where cipher_alg is the JCE cipher algorithm (e.g., "DESede"), - * cipher_mode is the cipher mode (e.g., "CBC", "CFB", "CTR", etc.), - * and padding_scheme is the cipher padding scheme (e.g., "NONE" for - * no padding, "PKCS5Padding" for PKCS#5 padding, etc.) and where - * [bits] 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. - *

    - * Examples are: - *

    -	 * 		"AES/ECB/NoPadding"		// Default for ESAPI Java 1.4 (insecure)
    -	 * 		"AES/CBC/PKCS5Padding"	// Default for ESAPI Java 2.0
    -	 * 		"DESede/OFB32/PKCS5Padding"
    -	 * 
    - * NOTE: 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: - *
    -	 * 		"Blowfish-192/CFB8/PKCS5Padding"
    -	 * 
    - * 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. + */ + @Deprecated + String getEncryptionAlgorithm(); + + /** + * Retrieve the cipher transformation. 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: + *
    +     *         cipher_alg/cipher_mode[bits]/padding_scheme
    +     * 
    + * where cipher_alg is the JCE cipher algorithm (e.g., "DESede"), + * cipher_mode is the cipher mode (e.g., "CBC", "CFB", "CTR", etc.), + * and padding_scheme is the cipher padding scheme (e.g., "NONE" for + * no padding, "PKCS5Padding" for PKCS#5 padding, etc.) and where + * [bits] 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. + *

    + * Examples are: + *

    +     *         "AES/ECB/NoPadding"        // Default for ESAPI Java 1.4 (insecure)
    +     *         "AES/CBC/PKCS5Padding"    // Default for ESAPI Java 2.0
    +     *         "DESede/OFB32/PKCS5Padding"
    +     * 
    + * NOTE: 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: + *
    +     *         "Blowfish-192/CFB8/PKCS5Padding"
    +     * 
    + * 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. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated + */ + @Deprecated 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. BEWARE: - * there is NO 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. + * + * @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. BEWARE: + * there is NO 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. Most likely to be replaced by a new CTOR for @@ -340,12 +340,12 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { * @see org.owasp.esapi.crypto.SecurityProviderLoader * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated 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. +// 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 @@ -356,10 +356,10 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { * * "Why Is OWASP Changing ESAPI Encryption?". *

    - * @return {@code true} if a you want a MAC to be used, otherwise {@code false}. + * @return {@code true} if a you want a MAC to be used, otherwise {@code false}. * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated boolean useMACforCipherText(); /** @@ -373,13 +373,13 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { * Controlled by the property {@code Encryptor.PlainText.overwrite} in * the {@code ESAPI.properties} file. *

    - * @return True if it is OK to overwrite the {@code PlainText} objects - * after encrypting, false otherwise. + * @return True if it is OK to overwrite the {@code PlainText} objects + * after encrypting, false otherwise. * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated boolean overwritePlainText(); - + /** * Get a string indicating how to compute an Initialization Vector (IV). * Currently supported modes are "random" to generate a random IV or @@ -389,35 +389,21 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { * fixed IVs, but the use of non-random IVs is inherently insecure, * especially for any supported cipher mode that is considered a streaming mode * (which is basically anything except CBC for modes that support require an IV). - * For this reason, 'fixed' is considered deprecated and will be - * removed during the next ESAPI point release (tentatively, 2.3). - * However, note that 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" (dereprected). - * - * @see #getFixedIV() + * For this reason, 'fixed' has now been removed (it was considered deprecated + * since release 2.2.0.0). An ESAPI.properties value of {@code fixed} for the property + * {@code Encryptor.ChooseIVMethod} will now result in a {@code ConfigurationException} + * being thrown. + * + * @return A string specifying the IV type. Should be "random". Anything + * else should fail with a {@code ConfigurationException} being thrown. + * * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. + * This method will be removed in a future release as it is now moot since + * it can only legitimately have the single value of "random". */ - @Deprecated + @Deprecated 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. - * @deprecated Short term: use SecurityConfiguration.getByteArrayProp("appropriate_esapi_prop_name") - * instead. Longer term: There will be a more general method in JavaEncryptor - * to explicitly set an IV. This whole concept of a single fixed IV has - * always been a kludge at best, as a concession to those who have used - * a single fixed IV in the past to support legacy applications. This method will be - * killed off in the next ESAPI point release (likely 2.3). It's time to put it to death - * as it was never intended for production in the first place. - */ - @Deprecated - String getFixedIV(); - + /** * Return a {@code List} of strings of combined cipher modes that support * both confidentiality and authenticity. These would be preferred @@ -429,7 +415,7 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { * 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. @@ -445,7 +431,7 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { * 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. @@ -454,185 +440,185 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { */ List getAdditionalAllowedCipherModes(); - /** - * Gets the hashing algorithm used by ESAPI to hash data. - * - * @return the current hashing algorithm + /** + * Gets the hashing algorithm used by ESAPI to hash data. + * + * @return the current hashing algorithm * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getHashAlgorithm(); - - /** - * Gets the hash iterations used by ESAPI to hash data. - * - * @return the current hashing algorithm + */ + @Deprecated + String getHashAlgorithm(); + + /** + * Gets the hash iterations used by ESAPI to hash data. + * + * @return the current hashing algorithm * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - int getHashIterations(); - - /** - * Retrieve the Pseudo Random Function (PRF) used by the ESAPI - * Key Derivation Function (KDF). - * - * @return The KDF PRF algorithm name. + */ + @Deprecated + int getHashIterations(); + + /** + * Retrieve the Pseudo Random Function (PRF) used by the ESAPI + * Key Derivation Function (KDF). + * + * @return The KDF PRF algorithm name. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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. - *

    - * Note: This does not get the configured response content type. That is accessed by calling - * getResponseContentType(). - * - * @return the current character encoding scheme + */ + @Deprecated + 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. + *

    + * Note: This does not get the configured response content type. That is accessed by calling + * getResponseContentType(). + * + * @return the current character encoding scheme * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getCharacterEncoding(); - - /** - * Return true if multiple encoding is allowed - * - * @return whether multiple encoding is allowed when canonicalizing data + */ + @Deprecated + String getCharacterEncoding(); + + /** + * Return true if multiple encoding is allowed + * + * @return whether multiple encoding is allowed when canonicalizing data * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - boolean getAllowMultipleEncoding(); - - /** - * Return true if mixed encoding is allowed - * - * @return whether mixed encoding is allowed when canonicalizing data + */ + @Deprecated + boolean getAllowMultipleEncoding(); + + /** + * Return true if mixed encoding is allowed + * + * @return whether mixed encoding is allowed when canonicalizing data * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - boolean getAllowMixedEncoding(); - - /** - * Returns the List of Codecs to use when canonicalizing data - * - * @return the codec list - */ - List getDefaultCanonicalizationCodecs(); - - /** - * Gets the digital signature algorithm used by ESAPI to generate and verify signatures. - * - * @return the current digital signature algorithm + */ + @Deprecated + boolean getAllowMixedEncoding(); + + /** + * Returns the List of Codecs to use when canonicalizing data + * + * @return the codec list + */ + List getDefaultCanonicalizationCodecs(); + + /** + * Gets the digital signature algorithm used by ESAPI to generate and verify signatures. + * + * @return the current digital signature algorithm * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - String getDigitalSignatureAlgorithm(); - - /** - * Gets the digital signature key length used by ESAPI to generate and verify signatures. - * - * @return the current digital signature key length + */ + @Deprecated + String getDigitalSignatureAlgorithm(); + + /** + * Gets the digital signature key length used by ESAPI to generate and verify signatures. + * + * @return the current digital signature key length * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - int getDigitalSignatureKeyLength(); - - /** - * Gets the random number generation algorithm used to generate random numbers where needed. - * - * @return the current random number generation algorithm + */ + @Deprecated + int getDigitalSignatureKeyLength(); + + /** + * Gets the random number generation algorithm used to generate random numbers where needed. + * + * @return the current random number generation algorithm * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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 + */ + @Deprecated + 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 * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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 + */ + @Deprecated + 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 * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - int getMaxOldPasswordHashes(); - - /** - * Allows for complete disabling of all intrusion detection mechanisms - * - * @return true if intrusion detection should be disabled + */ + @Deprecated + int getMaxOldPasswordHashes(); + + /** + * Allows for complete disabling of all intrusion detection mechanisms + * + * @return true if intrusion detection should be disabled * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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 - */ - Threshold getQuota(String eventName); - - /** - * Gets a file from the resource directory + */ + @Deprecated + 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 + */ + 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. */ File getResourceFile( String filename ); - - /** - * Returns true if session cookies are required to have HttpOnly flag set. + + /** + * Returns true if session cookies are required to have HttpOnly flag set. * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated boolean getForceHttpOnlySession() ; - /** - * Returns true if session cookies are required to have Secure flag set. + /** + * Returns true if session cookies are required to have Secure flag set. * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated boolean getForceSecureSession() ; - /** - * Returns true if new cookies are required to have HttpOnly flag set. + /** + * Returns true if new cookies are required to have HttpOnly flag set. * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated boolean getForceHttpOnlyCookies() ; - /** - * Returns true if new cookies are required to have Secure flag set. + /** + * Returns true if new cookies are required to have Secure flag set. * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ - @Deprecated + @Deprecated boolean getForceSecureCookies() ; - /** - * Returns the maximum allowable HTTP header size. + /** + * Returns the maximum allowable HTTP header size. * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - int getMaxHttpHeaderSize() ; + */ + @Deprecated + int getMaxHttpHeaderSize() ; - /** - * Gets an InputStream to a file in the resource directory + /** + * 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. @@ -640,171 +626,142 @@ public interface SecurityConfiguration extends EsapiPropertyLoader { */ InputStream getResourceStream( String filename ) throws IOException; - - /** - * Sets the ESAPI resource directory. - * - * @param dir The location of the resource directory. - */ - void setResourceDirectory(String dir); - - /** - * Gets the content type for responses used when setSafeContentType() is called. - *

    - * Note: This does not get the configured character encoding scheme. That is accessed by calling - * getCharacterEncoding(). - * - * @return The current content-type set for responses. + /** + * Sets the ESAPI resource directory. + * + * @param dir The location of the resource directory. + */ + void setResourceDirectory(String dir); + + /** + * Gets the content type for responses used when setSafeContentType() is called. + *

    + * Note: This does not get the configured character encoding scheme. That is accessed by calling + * getCharacterEncoding(). + * + * @return The current content-type set for responses. * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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" + */ + @Deprecated + 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" * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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. - */ + */ + @Deprecated + 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. + */ // OPEN ISSUE: Can we replace w/ SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead? - 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. + 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. * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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. + */ + @Deprecated + 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. * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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. + */ + @Deprecated + 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. * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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 + */ + @Deprecated + 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 * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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 + */ + @Deprecated + 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 * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - boolean getLogServerIP(); + */ + @Deprecated + boolean getLogServerIP(); - /** - * Returns the current log level. - * @return An integer representing the current log level. - * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. - */ - @Deprecated - 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. - * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. + /** + * 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. */ - @Deprecated - String getLogFileName(); + 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 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 actions) { + this.name = name; + this.count = count; + this.interval = interval; + this.actions = actions; + } + } /** - * 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). - * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. + * Returns the default working directory for executing native processes with Runtime.exec(). */ - @Deprecated - 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. - */ - 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 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 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(). - */ - File getWorkingDirectory(); + File getWorkingDirectory(); } diff --git a/src/main/java/org/owasp/esapi/StringUtilities.java b/src/main/java/org/owasp/esapi/StringUtilities.java index 634287ad1..ef95a91ce 100644 --- a/src/main/java/org/owasp/esapi/StringUtilities.java +++ b/src/main/java/org/owasp/esapi/StringUtilities.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -20,53 +20,53 @@ /** * String utilities used in various filters. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @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 0x20 && c < 0x7f ) { - sb.append( c ); - } else { - sb.append( ' ' ); - } - } - return sb.toString(); - } - - + 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 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 ( char c : characters ) { - if ( !contains( sb, c ) ) - sb.append( c ); - } - } + StringBuilder sb = new StringBuilder(); + + for (char[] characters : list) { + for ( char c : characters ) { + if ( !contains( sb, c ) ) + sb.append( c ); + } + } char[] toReturn = new char[sb.length()]; sb.getChars(0, sb.length(), toReturn, 0); @@ -75,11 +75,11 @@ public static char[] union(char[]... list) { } - /** + /** * 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. + * @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++) { @@ -90,7 +90,7 @@ public static boolean contains(StringBuilder input, char c) { } /** - * Returns the replace value if the value of test is null, "null", or "" + * Returns {@code replace} if {@code test} is null, "null" (case-insensitive), or blank, otherwise {@code test} * * @param test The value to test * @param replace The replacement value @@ -171,24 +171,24 @@ public static int getLevenshteinDistance (String s, String t) { * trimming of leading and trailing whitespace). Usually used with * assertions, as in *
    -     * 		assert StringUtils.notNullOrEmpty(cipherXform, true) :
    -     * 								"Cipher transformation may not be null or empty!";
    +     *         assert StringUtils.notNullOrEmpty(cipherXform, true) :
    +     *                                 "Cipher transformation may not be null or empty!";
          * 
    * or an equivalent runtime check that throws an {@code IllegalArgumentException}. * - * @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. + * @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("") ); - } + if ( trim ) { + return !( str == null || str.trim().equals("") ); + } else { + return !( str == null || str.equals("") ); + } } /** diff --git a/src/main/java/org/owasp/esapi/User.java b/src/main/java/org/owasp/esapi/User.java index 480e97135..fc5ca980c 100644 --- a/src/main/java/org/owasp/esapi/User.java +++ b/src/main/java/org/owasp/esapi/User.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -34,59 +34,59 @@ * 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 Jeff Williams at Aspect Security * @author Chris Schmidt (chrisisbeef .at. gmail.com) Digital Ritual Software * @since June 1, 2007 */ public interface User extends Principal, Serializable { - /** - * @return the locale - */ - Locale getLocale(); + /** + * @return the locale + */ + Locale getLocale(); - /** - * @param locale the locale to set - */ - void setLocale(Locale locale); + /** + * @param locale the locale to set + */ + void setLocale(Locale locale); /** * Adds a role to this user's account. - * - * @param role - * the role to add - * - * @throws AuthenticationException - * the authentication exception + * + * @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 + * + * @param newRoles + * the new roles to add + * + * @throws AuthenticationException + * the authentication exception */ void addRoles(Set 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 + * + * @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; @@ -102,21 +102,21 @@ public interface User extends Principal, Serializable { /** * 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(); @@ -133,7 +133,7 @@ public interface User extends Principal, Serializable { * 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(); @@ -141,73 +141,73 @@ public interface User extends Principal, Serializable { /** * 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 + * + * @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 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. + * 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. + * The session to remove from being associated with this user. */ void removeSession( HttpSession s ); - + /** * Returns a Set containing the sessions associated with this User. * @return The Set of sessions for this User. */ Set getSessions(); - + /** * Increment failed login count. */ @@ -215,70 +215,70 @@ public interface User extends Principal, Serializable { /** * 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 + * + * @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 - * + * + * @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 + * 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 + * 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. + * + * 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. * * - * 60 + * 60 * - * - * @return true, if user's session has timed out from inactivity based + * + * @return true, if user's session has timed out from inactivity based * on ESAPI configuration */ boolean isSessionTimeout(); @@ -290,11 +290,11 @@ public interface User extends Principal, Serializable { /** * Login with password. - * - * @param password - * the password - * @throws AuthenticationException - * if login fails + * + * @param password + * the password + * @throws AuthenticationException + * if login fails */ void loginWithPassword(String password) throws AuthenticationException; @@ -305,11 +305,11 @@ public interface User extends Principal, Serializable { /** * Removes a role from this user's account. - * - * @param role - * the role to remove - * @throws AuthenticationException - * the authentication exception + * + * @param role + * the role to remove + * @throws AuthenticationException + * the authentication exception */ void removeRole(String role) throws AuthenticationException; @@ -318,42 +318,42 @@ public interface User extends Principal, Serializable { * 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 + * + * @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); + void setExpirationTime(Date expirationTime); - /** + /** * Sets the roles for this account. - * - * @param roles - * the new roles - * - * @throws AuthenticationException - * the authentication exception + * + * @param roles + * the new roles + * + * @throws AuthenticationException + * the authentication exception */ void setRoles(Set roles) throws AuthenticationException; /** * Sets the screen name (username alias) for this user. - * + * * @param screenName the new screen name */ void setScreenName(String screenName); @@ -363,80 +363,80 @@ public interface User extends Principal, Serializable { */ 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 - */ - 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. - */ - User ANONYMOUS = new User() { - - private static final long serialVersionUID = -1850916950784965502L; - - private String csrfToken = ""; - private Set sessions = new HashSet(); - private Locale locale = null; - - /** + /** + * 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 + */ + 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 authenticate 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. + */ + 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"); + 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"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** @@ -445,107 +445,107 @@ public void addRoles(Set newRoles) throws AuthenticationException { public void changePassword(String oldPassword, String newPassword1, String newPassword2) throws AuthenticationException, EncryptionException { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public void disable() { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public void enable() { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public long getAccountId() { - return 0; + return 0; } /** * {@inheritDoc} */ public String getAccountName() { - return "Anonymous"; + return "Anonymous"; } - /** - * Alias method that is equivalent to getAccountName() - * - * @return the name of the current user's account + /** + * Alias method that is equivalent to getAccountName() + * + * @return the name of the current user's account */ public String getName() { - return getAccountName(); + return getAccountName(); } - + /** * {@inheritDoc} */ public String getCSRFToken() { - return csrfToken; + return csrfToken; } /** * {@inheritDoc} */ public Date getExpirationTime() { - return null; + return null; } /** * {@inheritDoc} */ public int getFailedLoginCount() { - return 0; + return 0; } /** * {@inheritDoc} */ public Date getLastFailedLoginTime() throws AuthenticationException { - return null; + return null; } /** * {@inheritDoc} */ public String getLastHostAddress() { - return "unknown"; + return "unknown"; } /** * {@inheritDoc} */ public Date getLastLoginTime() { - return null; + return null; } /** * {@inheritDoc} */ public Date getLastPasswordChangeTime() { - return null; + return null; } /** * {@inheritDoc} */ public Set getRoles() { - return new HashSet(); + return new HashSet(); } /** * {@inheritDoc} */ public String getScreenName() { - return "Anonymous"; + return "Anonymous"; } /** @@ -571,70 +571,70 @@ public Set getSessions() { * {@inheritDoc} */ public void incrementFailedLoginCount() { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public boolean isAnonymous() { - return true; + return true; } /** * {@inheritDoc} */ public boolean isEnabled() { - return false; + return false; } /** * {@inheritDoc} */ public boolean isExpired() { - return false; + return false; } /** * {@inheritDoc} */ public boolean isInRole(String role) { - return false; + return false; } /** * {@inheritDoc} */ public boolean isLocked() { - return false; + return false; } /** * {@inheritDoc} */ public boolean isLoggedIn() { - return false; + return false; } /** * {@inheritDoc} */ public boolean isSessionAbsoluteTimeout() { - return false; + return false; } /** * {@inheritDoc} */ public boolean isSessionTimeout() { - return false; + return false; } /** * {@inheritDoc} */ public void lock() { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** @@ -642,119 +642,119 @@ public void lock() { */ public void loginWithPassword(String password) throws AuthenticationException { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public void logout() { - throw new RuntimeException("Invalid operation for the anonymous user"); + 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"); + 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; + 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"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ - public void setExpirationTime(Date expirationTime) { - throw new RuntimeException("Invalid operation for the anonymous user"); + 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"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public void setScreenName(String screenName) { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public void unlock() { - throw new RuntimeException("Invalid operation for the anonymous user"); + 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"); + throw new RuntimeException("Invalid operation for the anonymous user"); } /** * {@inheritDoc} */ public void setLastFailedLoginTime(Date lastFailedLoginTime) { - throw new RuntimeException("Invalid operation for the anonymous user"); + throw new RuntimeException("Invalid operation for the anonymous user"); } - + /** * {@inheritDoc} */ - public void setLastLoginTime(Date lastLoginTime) { - throw new RuntimeException("Invalid operation for the anonymous user"); - } + 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"); + 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"); + throw new RuntimeException("Invalid operation for the anonymous user"); } - + /** * {@inheritDoc} */ public HashMap getEventMap() { - throw new RuntimeException("Invalid operation for the anonymous user"); + 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; - } + * @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 index 6479ff376..d671f9479 100644 --- a/src/main/java/org/owasp/esapi/ValidationErrorList.java +++ b/src/main/java/org/owasp/esapi/ValidationErrorList.java @@ -1,17 +1,17 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security - * + * * @created 2007 */ package org.owasp.esapi; @@ -23,38 +23,38 @@ import org.owasp.esapi.errors.ValidationException; /** - * The ValidationErrorList class defines a well-formed collection of - * ValidationExceptions so that groups of validation functions can be + * The ValidationErrorList class defines a well-formed collection of + * ValidationExceptions so that groups of validation functions can be * called in a non-blocking fashion. *

    - * To use the ValidationErrorList to execute groups of validation + * To use the ValidationErrorList to execute groups of validation * attempts, your controller code would look something like: - * + * *

    - * ValidationErrorList() errorList = new ValidationErrorList();.
    - * String name  = getValidInput("Name", form.getName(), "SomeESAPIRegExName1", 255, false, errorList);
    - * String address = getValidInput("Address", form.getAddress(), "SomeESAPIRegExName2", 255, false, errorList);
    + * ValidationErrorList() errorList = new ValidationErrorList();
    + * String name  = ESAPI.validator().getValidInput("Name", form.getName(), "SomeESAPIRegExName1", 255, false, errorList);
    + * String address = ESAPI.validator().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 );
      * 
    - * + * * The at your view layer you would be able to retrieve all * of your error messages via a helper function like: - * + * *
    - * public static ValidationErrorList getErrors() {          
    + * 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;
    + *        return errors;
      * }
      * 
    - * + * * You can list all errors like: - * + * *
      * <%
      *      for (Object vo : errorList.errors()) {
    @@ -65,73 +65,73 @@
      *     }
      * %>
      * 
    - * + * * And even check if a specific UI component is in error via calls like: - * + * *
      * ValidationException e = errorList.getError("Name");
      * 
    - * + * * @author Jim Manico (jim@manico.net) Manico.net * @since August 15, 2008 */ public class ValidationErrorList { - /** - * Error list of ValidationException's - */ - private HashMap errorList = new HashMap(); + /** + * Error list of ValidationException's + */ + private HashMap errorList = new HashMap(); + + /** + * 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 cannot be null: " + vex.getLogMessage(), vex ); + if ( vex == null ) throw new RuntimeException( "ValidationException cannot be null for context (" + context + ")" ); + if (getError(context) != null) throw new RuntimeException("Context (" + context + ") already exists, must be unique"); + errorList.put(context, vex); + } - /** - * 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 cannot be null: " + vex.getLogMessage(), vex ); - if ( vex == null ) throw new RuntimeException( "ValidationException cannot be null for context (" + context + ")" ); - 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 errors() { + return new ArrayList( errorList.values() ); + } - /** - * Returns list of ValidationException, or empty list of no errors exist. - * - * @return List - */ - public List errors() { - return new ArrayList( 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); + } - /** - * 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 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(); - } + /** + * 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 index 25cc95ac1..2d93c3dbe 100644 --- a/src/main/java/org/owasp/esapi/ValidationRule.java +++ b/src/main/java/org/owasp/esapi/ValidationRule.java @@ -4,78 +4,97 @@ import org.owasp.esapi.errors.ValidationException; +/** + * A ValidationRule performs syntax and possibly semantic validation of a single piece of data from an untrusted source. + * + * @since ESAPI 2.0GA + */ 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; + /** + * 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 + * Thrown if any validation rules fail, except when the + * {@code ESAPI.properties}> property + * "Validator.HtmlValidationAction" is set to + * {@code clean}. The default {@code ESAPI.properties}> property file + * has "Validator.HtmlValidationAction" is set to {@code throw}, which results + * in a {@code ValidationException} being thrown if any of the validation rules + * fail. + * + * releases. See ESAPI GitHub Issues + * 521 + * for futher details. + * + * @see #getValid(String context, String input, ValidationErrorList errorList) + */ + 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); + /** + * Whether or not a valid valid can be null. {@code getValid} will throw an + * Exception and {#code 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(); + /** + * 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 typeName a name, describing the validator + */ + void setTypeName(String typeName); - /** - * @param encoder the encoder to use - */ - void setEncoder(Encoder encoder); + /** + * @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; + /** + * 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; + /** + * 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); + /** + * Try to call {@code getvalid}, then call a 'sanitize' method for sanitization (if one exists), + * finally return a default value. + */ + Object getSafe(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 list); + /** + * @return true if the input passes validation + */ + boolean isValid(String context, String input); -} \ No newline at end of file + /** + * 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 list); + +} diff --git a/src/main/java/org/owasp/esapi/Validator.java b/src/main/java/org/owasp/esapi/Validator.java index 0118995bb..90eaa35ae 100644 --- a/src/main/java/org/owasp/esapi/Validator.java +++ b/src/main/java/org/owasp/esapi/Validator.java @@ -1,7 +1,7 @@ -/** +/* * OWASP Enterprise Security API (ESAPI) * - * This file is part of the Open Web Application Security Project (OWASP) + * This file is part of the Open Worldwide Application Security Project (OWASP) * Enterprise Security API (ESAPI) project. For details, please see * http://www.owasp.org/index.php/ESAPI. * @@ -36,11 +36,20 @@ * 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. - *

    - * Implementations must adopt a "whitelist" approach to validation where a + *

    + * Implementations must adopt a "allow-list" 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. + *

    + * CAUTION: There are many methods that take multiple (or only!) {@code String} + * arguments. Be careful that you do not mix up the order of these, because for + * several methods that have {@code context} and {@code input} arguments, mixing up + * the order of those likely will result in serious security consequences. + * . When there are 2 these {@code String} parameters—{@code context} and + * {@code input} arguments—the {@code context} argument is always first. + * See the individual method documentation for additional details. + *

    * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security @@ -48,673 +57,1138 @@ */ 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 allowedExtensions, boolean allowNull) throws IntrusionException; - - /** - * Calls getValidFileName and returns true if no exceptions are thrown. - */ - boolean isValidFileName(String context, String input, List 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. + /** + * Add a validation rule to the registry using the "type name" of the rule as the key. + * @param rule The {@link ValidationRule} to add. + */ + void addRule( ValidationRule rule ); + + /** + * Get a validation rule from the registry with the "type name" of the rule as the key. + * @param name The "type" name of a {@link ValidationRule} to retrieve. + */ + ValidationRule getRule( String name ); + + /** + * Returns true if canonicalized input is valid. + *

    + * Calls {@link #getValidInput(String, String, String, int, boolean, boolean)} with {@code canonicalize=true} + * and returns true if no exceptions are thrown. + * + * @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 which maps to the actual regular expression from "ESAPI.properties". + * @param maxLength + * The maximum {@code String} length allowed for {@code input}. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws IntrusionException; + + /** + * Returns true if canonicalized input is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidInput(String, String, String, int, boolean, boolean)} with {@code canonicalize=true} + * and returns true if no exceptions are thrown. + * + * @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 which maps to the actual regular expression from "ESAPI.properties". + * @param maxLength + * The maximum {@code String} length allowed for {@code input}. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * @param errorList The error list to which any {@code ValidationException} messages are added. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidInput(String, String, String, int, boolean, boolean)} + * and returns true if no exceptions are thrown. + * + * @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 which maps to the actual regular expression from "ESAPI.properties". + * @param maxLength + * The maximum {@code String} length allowed for {@code input}. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * @param canonicalize + * If true, the {@code input} if first canonicalized before being validated. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws IntrusionException; + + /** + * Returns true if {@code input} is valid; + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidInput(String, String, String, int, boolean, boolean)} + * and returns true if no exceptions are thrown. + * + * @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 which maps to the actual regular expression from "ESAPI.properties". + * @param maxLength + * The maximum {@code String} length allowed for {@code input}. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * @param canonicalize + * If true, the {@code input} if first canonicalized before being validated. + * @param errorList The error list to which any {@code ValidationException} messages are added. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns the validated, canonicalized {@code input} as a String. + *

    + * Calls {@link #getValidInput(String, String, String, int, boolean, boolean)} + * with {@code canonicalize=true}. + * + * @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 which maps to the actual regular expression from "ESAPI.properties". + * @param maxLength + * The maximum {@code String} length allowed for {@code input}. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @throws ValidationException Input is invalid, based on the regex associated with {@code type}. + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns validated {@code 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 which maps to the actual regular expression from "ESAPI.properties". + * @param maxLength + * The maximum post-canonicalized String length allowed. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code 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 Input is invalid, based on the regex associated with {@code type}. + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws ValidationException, IntrusionException; + + /** + * Returns canonicalized validated {@code input} as a String, + * and adds validation exceptions to the supplied {@code errorList}. + *

    + * Calls {@link #getValidInput(String, String, String, int, boolean, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns validated {@code input} as a String with optional canonicalization, + * and adds validation exceptions to the supplied {@code errorList}. + *

    + * Returns the result of calling {@link #getValidInput(String, String, String, int, boolean, boolean)} + * with {@code canonicalize=true}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidDate(String, String, DateFormat, boolean)}, + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidDate(String context, String input, DateFormat format, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidDate(String, String, DateFormat, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns a valid date as a {@link java.util.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 input data to validate. + * 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. + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @return A valid date as a {@link java.util.Date} + * + * @throws ValidationException Input is invalid, based on the regex associated with {@code type}. + * @throws IntrusionException Input likely indicates an attack. + */ + Date getValidDate(String context, String input, DateFormat format, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns a valid date as a {@link java.util.Date}, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidDate(String, String, DateFormat, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + Date getValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Canonicalize and then sanitize the input so that it is "safe" for rendering in an HTML context (i.e., that + * it does not contain unwanted scripts in the body, attributes, CSS, URLs, or anywhere else). Note that the resulting + * returned value may omit input that is considered dangerous and cannot be safely sanitized and other input + * that gets HTML encoded (e.g., a single quote (') might get changed to """). + *

    + * The default behavior of this check depends on the {@code antisamy-esapi.xml} AntiSamy policy configuration file + * (or an alternate filename, specified via the "Validator.HtmlValidationConfigurationFile" property in your + * {@code ESAPI.properties} file. Implementors wishing to alter the AntiSamy policy configuration file should + * reference the OWASP AntiSamy project for ideas + * on how to do HTML validation in a allow-list 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 {@code String} length allowed for {@code input}. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @return A string representing the canonicalized and sanitized input that is safe for rendering in an HTML context. + * + * @throws ValidationException Input is invalid, based on the regex associated with {@code type}. + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Canonicalize and then sanitize the input so that it is "safe" for rendering in an HTML context (i.e., that + * it does not contain unwanted scripts in the body, attributes, CSS, URLs, or anywhere else). Note that the resulting + * returned value may omit input that is considered dangerous and cannot be safely sanitized and other input + * that gets HTML encoded (e.g., a single quote (') might get changed to """). + *

    + * The default behavior of this check depends on the {@code antisamy-esapi.xml} AntiSamy policy configuration file + * (or an alternate filename, specified via the "Validator.HtmlValidationConfigurationFile" property in your + * {@code ESAPI.properties} file. Implementors wishing to alter the AntiSamy policy configuration file should + * reference the OWASP AntiSamy project for ideas + * on how to do HTML validation in a allow-list way, as this is an extremely difficult problem. + *

    + * Calls {@link #getValidSafeHTML(String, String, int, boolean)}. + * + * @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 {@code String} length allowed for {@code input}. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * @param errorList The error list to which any {@code ValidationException} messages are added. + * + * @return A string representing the canonicalized and sanitized input that is safe for rendering in an HTML context. + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} matches the pattern for a valid credit card number. + *

    + * Calls {@link #getValidCreditCard(String, String, boolean)}, + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidCreditCard(String context, String input, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} matches the pattern for a valid credit card number, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidCreditCard(String, String, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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., PaymentPage_CreditCard). + * 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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @return A valid credit card number + * + * @throws ValidationException Input is invalid because it doesn't appear to be a valid credit card account number. + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidCreditCard(String context, String input, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns a canonicalized and validated credit card number as a String, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidCreditCard(String, String, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidDirectoryPath(String, String, File, boolean)}, + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidDirectoryPath(String, String, File, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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. + * + * @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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @return A valid directory path + * + * @throws ValidationException Input is invalid (e.g., the provided input is not a directory). + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws ValidationException, 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; + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidDirectoryPath(String, String, File, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidFileName(String, String, List, boolean)} + * with allowedExtensions set to the configured {@code ESAPI.securityConfiguration().getAllowedFileExtensions()} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + * + * @see ESAPI#securityConfiguration() + * @see SecurityConfiguration#getAllowedFileExtensions() + */ + boolean isValidFileName(String context, String input, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidFileName(String, String, List, boolean)} + * with allowedExtensions set to the configured {@code ESAPI.securityConfiguration().getAllowedFileExtensions()} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + * + * @see ESAPI#securityConfiguration() + * @see SecurityConfiguration#getAllowedFileExtensions() + */ + boolean isValidFileName(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidFileName(String, String, List, boolean)}, + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + * + * @see ESAPI#securityConfiguration() + * @see SecurityConfiguration#getAllowedFileExtensions() + */ + boolean isValidFileName(String context, String input, List allowedExtensions, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidFileName(String, String, List, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + * + * @see ESAPI#securityConfiguration() + * @see SecurityConfiguration#getAllowedFileExtensions() + */ + boolean isValidFileName(String context, String input, List 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 allowedExtensions + * List of file extensions which will be accepted. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code 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 allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException; - - /** - * Calls getValidFileName with the supplied errorList to capture ValidationExceptions - */ - String getValidFileName(String context, String input, List 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. + * @throws ValidationException Input is invalid (e.g., {@code input} refers to a non-existant file, does not have a + * valid file extension as per {@code allowedExtensions}, does not match the canonicalized path, + * exceeds a maximum length of 255 characters, etc. + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidFileName(String context, String input, List allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns a canonicalized and validated file name as a String, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidFileName(String, String, List, boolean)}, + * the supplied {@code errorList} is used to capture ValidationExceptions. + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidFileName(String context, String input, List allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidNumber(String, String, long, long, boolean)}, + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidNumber(String, String, long, long, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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. + * 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. + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. * @param minValue - * Lowest legal value for input. + * Lowest legal value for input. * @param maxValue - * Highest legal value for input. + * 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. + * @throws ValidationException Input is invalid; that is, not a number in the range + * of [{@code minValue}, {@code maxValue}]. + * @throws IntrusionException Input likely indicates an attack. + */ + Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns a validated number as a double within the range of + * [{@code minValue}, {@code maxValue}]; any validation + * exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidNumber(String, String, long, long, boolean)}. + * + * @param context + * A descriptive name of the parameter that you are validating (e.g., "OrderPage_Quantity"). + * 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 minValue + * Lowest legal value for input. + * @param maxValue + * Highest legal value for input. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * @param errorList The error list to which any {@code ValidationException} messages are added. + * + * @throws IntrusionException Input likely indicates an attack. + */ + Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is a valid integer between {@code minValue} and {@code maxValue} inclusive. + *

    + * Calls {@link #getValidInteger(String, String, int, int, boolean)}, + * and returns true if no exceptions are thrown. + * + * @param context + * A descriptive name of the parameter that you are validating (e.g., "OrderPage_Quantity"). + * 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 minValue + * Lowest legal value for input. + * @param maxValue + * Highest legal value for input. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is a valid integer between {@code minValue} and {@code maxValue} inclusive, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidInteger(String, String, int, int, boolean)} + * and returns true if no exceptions are thrown. + * + * @param context + * A descriptive name of the parameter that you are validating (e.g., "OrderPage_Quantity"). + * 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 minValue + * Lowest legal value for input. + * @param maxValue + * Highest legal value for input. + * @param allowNull + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * @param errorList The error list to which any {@code ValidationException} messages are added. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns a validated integer, + * {@code input} is a valid integer if it is between {@code minValue} and {@code maxValue} inclusive. + * 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., OrderPage_Quantity). + * 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. + * 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. + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. * @param minValue - * Lowest legal value for input. + * Lowest legal value for input. * @param maxValue - * Highest legal value for input. + * 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. + * @throws ValidationException Input is not a valid integer in the range of [{@code minValue}, {@code maxValue}]. + * @throws IntrusionException Input likely indicates an attack. + */ + Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns a validated integer, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidInteger(String, String, int, int, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidDouble(String, String, double, double, boolean)}, + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidDouble(String, String, double, double, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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. + * 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. + * If {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. * @param minValue - * Lowest legal value for input. + * Lowest legal value for input. * @param maxValue - * Highest legal value for input. + * 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 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 allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; - - /** - * Calls getValidListItem and returns true if no exceptions are thrown. - */ - boolean isValidListItem(String context, String input, List list) throws IntrusionException; - - /** - * Calls getValidListItem and returns true if no exceptions are thrown. - */ - boolean isValidListItem(String context, String input, List 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 list) throws ValidationException, IntrusionException; - - /** - * Calls getValidListItem with the supplied errorList to capture ValidationExceptions - */ - String getValidListItem(String context, String input, List list, ValidationErrorList errorList) throws IntrusionException; - - /** - * Calls assertValidHTTPRequestParameterSet and returns true if no exceptions are thrown. - */ - boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set optional) throws IntrusionException; - - /** - * Calls assertValidHTTPRequestParameterSet and returns true if no exceptions are thrown. - */ - boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set 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 required, Set optional) throws ValidationException, IntrusionException; - - /** - * Calls getValidHTTPRequestParameterSet with the supplied errorList to capture ValidationExceptions - */ - void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set 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; - - /** - * Parses and ensures that the URI in question is a valid RFC-3986 URI. This simplifies - * the kind of regex required for subsequent validation to mitigate regex-based DoS attacks. - * - * @see RFC-3986. - * - * @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 True if the URI is valid - * @throws ValidationException - */ - boolean isValidURI(String context, String input, boolean allowNull); - - /** - * Will return a {@code URI} object that will represent a fully parsed and legal URI - * as specified in RFC-3986. - * - * @param input String - * @return URI object representing a parsed URI, or {@code null} if the URI was non-compliant in some way. - */ - URI getRfcCompliantURI(String input); - + * @throws ValidationException Input is invalid. + * @throws IntrusionException Input likely indicates an attack. + */ + Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns a validated real number as a double, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidDouble(String, String, double, double, boolean)}, + * the supplied {@code errorList} is used to capture ValidationExceptions. + * + * @throws IntrusionException Input likely indicates an attack. + */ + Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidFileContent(String, byte[], int, boolean)}, + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidFileContent(String, byte[], int, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @return A byte array containing valid file content. + * + * @throws ValidationException Input is invalid. + * @throws IntrusionException Input likely indicates an attack. + */ + byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns validated file content as a byte array, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidFileContent(String, byte[], int, boolean)}, + * the supplied {@code errorList} is used to capture ValidationExceptions. + * + * @throws IntrusionException Input likely indicates an attack. + */ + byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code filepath}, {@code filename}, and {@code content} of a file are valid. + *

    + * Calls {@link #isValidFileName(String, String, boolean)}, + * {@link #isValidDirectoryPath(String, String, File, boolean)}, + * and {@link #isValidFileContent(String, byte[], int, boolean)}, + * and returns true if all three checks pass. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code filepath}, {@code filename}, and {@code content} of a file are valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #isValidFileName(String, String, boolean, ValidationErrorList)} + * {@link #isValidDirectoryPath(String, String, File, boolean, ValidationErrorList)} + * and {@link #isValidFileContent(String, byte[], int, boolean, ValidationErrorList)}, + * and returns true if all three checks pass. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Validates the {@code filepath}, {@code filename}, and {@code 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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @throws ValidationException Input is invalid. + * @throws IntrusionException Input likely indicates an attack. + */ + void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Validates the {@code filepath}, {@code filename}, and {@code content} of a file, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #assertValidFileUpload(String, String, String, File, byte[], int, List, boolean)}, + * the supplied {@code errorList} is used to capture ValidationExceptions. + * + * @throws IntrusionException Input likely indicates an attack. + */ + void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List allowedExtensions, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidListItem(String, String, List)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidListItem(String context, String input, List list) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidListItem(String, String, List)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidListItem(String context, String input, List 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 Input is invalid. + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidListItem(String context, String input, List list) throws ValidationException, IntrusionException; + + /** + * Returns the list item that exactly matches the canonicalized input, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidListItem(String, String, List)} + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidListItem(String context, String input, List list, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if only required and optional parameters are in the request. + *

    + * Calls {@link #assertValidHTTPRequestParameterSet(String, HttpServletRequest, Set, Set)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set optional) throws IntrusionException; + + /** + * Returns true if only required and optional parameters are in the request, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #assertValidHTTPRequestParameterSet(String, HttpServletRequest, Set, Set)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set 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 Input is invalid. + * @throws IntrusionException Input likely indicates an attack. + */ + void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set optional) throws ValidationException, IntrusionException; + + /** + * Validates that the parameters in the current request contain all required parameters + * and only optional ones in addition, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #assertValidHTTPRequestParameterSet(String, HttpServletRequest, Set, Set)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set optional, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidPrintable(String, char[], int, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidPrintable(String, char[], int, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code 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 Input is invalid. + */ + char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws ValidationException; + + /** + * Returns canonicalized and validated printable characters as a byte array, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidPrintable(String, char[], int, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidPrintable(String, String, int, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull) throws IntrusionException; + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidPrintable(String, String, int, boolean)} + * and returns true if no exceptions are thrown. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code 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 Input is invalid. + */ + String getValidPrintable(String context, String input, int maxLength, boolean allowNull) throws ValidationException; + + /** + * Returns canonicalized and validated printable characters as a String, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidPrintable(String, String, int, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errorList) throws IntrusionException; + + /** + * Returns true if {@code input} is valid. + *

    + * Calls {@link #getValidRedirectLocation(String, String, boolean)} + * and returns true if no exceptions are thrown. + */ + boolean isValidRedirectLocation(String context, String input, boolean allowNull); + + /** + * Returns true if {@code input} is valid, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidRedirectLocation(String, String, boolean)} + * 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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code 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 Input is invalid. + * @throws IntrusionException Input likely indicates an attack. + */ + String getValidRedirectLocation(String context, String input, boolean allowNull) throws ValidationException, IntrusionException; + + /** + * Returns a canonicalized and validated redirect location as a String, + * any validation exceptions are added to the supplied {@code errorList}. + *

    + * Calls {@link #getValidRedirectLocation(String, String, boolean)}. + * + * @throws IntrusionException Input likely indicates an attack. + */ + 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 Input is invalid. + */ + String safeReadLine(InputStream inputStream, int maxLength) throws ValidationException; + + /** + * Parses and ensures that the URI in question is a valid RFC-3986 URI. This simplifies + * the kind of regex required for subsequent validation to mitigate regex-based DoS attacks. + * + * @see RFC-3986. + * + * @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 {@code allowNull} is true then an input that is NULL or an empty string will be legal. + * If {@code allowNull} is false then NULL or an empty String will throw a ValidationException. + * + * @return True if the URI is valid + */ + boolean isValidURI(String context, String input, boolean allowNull); + + /** + * Will return a {@code URI} object that will represent a fully parsed and legal URI + * as specified in RFC-3986. + * + * @param input String + * @return URI object representing a parsed URI, or {@code null} if the URI was non-compliant in some way. + */ + URI getRfcCompliantURI(String input); + } diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java index ed73e4bd9..8595cb6e1 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java @@ -1,18 +1,18 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2017 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Matt Seil (mseil .at. owasp.org) * @created 2017 - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @created 2007 @@ -21,27 +21,27 @@ package org.owasp.esapi.codecs; /** - * - * This abstract Impl is broken off from the original {@code Codec} class and - * provides the {@code Character} parsing logic that has been with ESAPI from the beginning. + * + * This abstract Impl is broken off from the original {@code Codec} class and + * provides the {@code Character} parsing logic that has been with ESAPI from the beginning. * */ public abstract class AbstractCharacterCodec extends AbstractCodec { - /* (non-Javadoc) - * @see org.owasp.esapi.codecs.Codec#decode(java.lang.String) - */ - @Override - public String decode(String input) { - StringBuilder sb = new StringBuilder(); - PushbackSequence 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(); - } + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#decode(java.lang.String) + */ + @Override + public String decode(String input) { + StringBuilder sb = new StringBuilder(); + PushbackSequence 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(); + } } diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java index 7192bae20..5149b5d0a 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2017 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Matt Seil (mseil .at. owasp.org) * @created 2017 */ @@ -17,12 +17,16 @@ /** - * 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 + * The {@code Coded} 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). {@code Coded}s 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. - * + *

    + * Be sure to see the several WARNINGs associated with the detailed + * method descriptions. You will not find that in the "Method Summary" section + * of the javadoc because that only shows the initial sentence. + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @param @@ -31,131 +35,141 @@ */ public abstract class AbstractCodec implements 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 final String[] hex = new String[256]; - - /** - * Default constructor - */ - public AbstractCodec() { - 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(); - } - } - } - - /** - * WARNING!! {@code Character} based Codecs will silently transform code points that are not - * legal UTF code points into garbage data as they will cast them to {@code char}s. - *

    - * If you are implementing an {@code Integer} based codec, these will be silently discarded - * based on the return from {@code Character.isValidCodePoint( int )}. This is the preferred - * behavior moving forward. - * - * - * {@inheritDoc} - */ - @Override - public String encode(char[] immune, String input) { - StringBuilder sb = new StringBuilder(); - for(int offset = 0; offset < input.length(); ) { - final int point = input.codePointAt(offset); - if (Character.isBmpCodePoint(point)) { - //We can then safely cast this to char and maintain legacy behavior. - sb.append(encodeCharacter(immune, new Character((char) point))); - } else { - sb.append(encodeCharacter(immune, point)); - } - offset += Character.charCount(point); - } - return sb.toString(); - } - - /** - * WARNING!!!! Passing a standard char to this method will resolve to the - * @see #encodeCharacter( char[], int ) - * method instead of this one!!! YOU HAVE BEEN WARNED!!!! - * - * {@inheritDoc} - */ - @Override - public String encodeCharacter( char[] immune, Character c ) { - return ""+c; - } - - public String encodeCharacter(char[] immune, char c) { - throw new IllegalArgumentException("You tried to call encodeCharacter with a char. Nope. Use Character instead!"); - } - - /* (non-Javadoc) - * @see org.owasp.esapi.codecs.Codec#encodeCharacter(char[], int) - */ - @Override - public String encodeCharacter( char[] immune, int codePoint ) { - String rval = ""; - if(Character.isValidCodePoint(codePoint)){ - rval = new StringBuilder().appendCodePoint(codePoint).toString(); - } - return rval; - } - - - - /* (non-Javadoc) - * @see org.owasp.esapi.codecs.Codec#decodeCharacter(org.owasp.esapi.codecs.PushbackString) - */ - @Override - public T decodeCharacter( PushbackSequence input ) { - return input.next(); - } - - /** - * {@inheritDoc} - */ - public String getHexForNonAlphanumeric(char c) { - if(c<0xFF) - return hex[c]; - return toHex(c); - } - - /** - * {@inheritDoc} - */ - public String getHexForNonAlphanumeric(int c) { - if (c<0xFF) { - return hex[c]; - } else { - return toHex(c); - } - } - - public String toOctal(char c) { - return Integer.toOctalString(c); - } - - public String toHex(char c) { - return Integer.toHexString(c); - } - - public String toHex(int c) { - return Integer.toHexString(c); - } - - /** - * {@inheritDoc} - */ - public boolean containsCharacter( char c, char[] array ) { - for (char ch : array) { - if (c == ch) return true; - } - return false; - } + /** + * 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 final String[] hex = new String[256]; + + /** + * Default constructor + */ + public AbstractCodec() { + 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(); + } + } + } + + /** + * {@inheritDoc} + *

    + * WARNING!! {@code Character} based {@code Codec}s will only handle the byte range of + * 0-65535 (0x0-0xffff). Passing any data represented by a higher numerical value will result in + * a downcast thus destroying the original data with undefined results. + *

    + * Also, if you are implementing an {@code Integer} based codec, these will be silently discarded + * based on the return from {@code Character.isValidCodePoint( int )}. This is the preferred + * behavior moving forward. + */ + @Override + public String encode(char[] immune, String input) { + StringBuilder sb = new StringBuilder(); + for(int offset = 0; offset < input.length(); ) { + final int point = input.codePointAt(offset); + if (Character.isBmpCodePoint(point)) { + //We can then safely cast this to char and maintain legacy behavior. + sb.append(encodeCharacter(immune, new Character((char) point))); + } else { + sb.append(encodeCharacter(immune, point)); + } + offset += Character.charCount(point); + } + return sb.toString(); + } + + /** + * {@inheritDoc} + *

    + * WARNING!!!! Passing a standard {@code char} rather than {@code Character} to this method will resolve to the + * {@link #encodeCharacter( char[], char )} method, which will throw an {@code IllegalArgumentException} instead. + * YOU HAVE BEEN WARNED!!!! + */ + @Override + public String encodeCharacter( char[] immune, Character c ) { + return ""+c; + } + + + /** + * To prevent accidental usage and calling + * {@link #encodeCharacter( char[], int)} when called with {@code char} and + * {@code char} is first silently converted to {@code int} and then the + * unexpected method is called. + * + * @throws IllegalArgumentException to indicate that you called the incorrect method. + */ + public String encodeCharacter(char[] immune, char c) { + throw new IllegalArgumentException("You tried to call encodeCharacter() with a char. Nope. " + + "Use 'encodeCharacter( char[] immune, Character c)' instead!"); + } + + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#encodeCharacter(char[], int) + */ + @Override + public String encodeCharacter( char[] immune, int codePoint ) { + String rval = ""; + if(Character.isValidCodePoint(codePoint)){ + rval = new StringBuilder().appendCodePoint(codePoint).toString(); + } + return rval; + } + + + + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#decodeCharacter(org.owasp.esapi.codecs.PushbackString) + */ + @Override + public T decodeCharacter( PushbackSequence input ) { + return input.next(); + } + + /** + * {@inheritDoc} + */ + public String getHexForNonAlphanumeric(char c) { + if(c<0xFF) + return hex[c]; + return toHex(c); + } + + /** + * {@inheritDoc} + */ + public String getHexForNonAlphanumeric(int c) { + if (c<0xFF) { + return hex[c]; + } else { + return toHex(c); + } + } + + public String toOctal(char c) { + return Integer.toOctalString(c); + } + + public String toHex(char c) { + return Integer.toHexString(c); + } + + public String toHex(int c) { + return Integer.toHexString(c); + } + + /** + * {@inheritDoc} + */ + public 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/AbstractIntegerCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java index cf522e92f..5bf9038dd 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java @@ -1,18 +1,18 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2017 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Matt Seil (mseil .at. owasp.org) * @created 2017 - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @created 2007 @@ -24,27 +24,30 @@ * data by focusing on {@code int} as opposed to {@code Character}. Because non-BMP code * points cannot be represented by a {@code char}, this class remedies that by parsing string * data as codePoints as opposed to a stream of {@code char}s. - * + * + * WARNING: This class will silently discard an invalid code point according to + * the result of {@code Character.isValidCodePoint( int )} method. + * * @author Matt Seil (mseil .at. owasp.org) - * @since 2017 -- Adapted from Jeff Williams' original {@code Codec} class. + * @since 2017 -- Adapted from Jeff Williams' original {@code Codec} class. */ public class AbstractIntegerCodec extends AbstractCodec { - /** - * {@inheritDoc} - */ - @Override - public String decode(String input) { - StringBuilder sb = new StringBuilder(); - PushbackSequence pbs = new PushBackSequenceImpl(input); - while (pbs.hasNext()) { - Integer c = decodeCharacter(pbs); - if (c != null && Character.isValidCodePoint(c)) { - sb.appendCodePoint(c); - } else { - sb.appendCodePoint(pbs.next()); - } - } - return sb.toString(); - } + /** + * {@inheritDoc} + */ + @Override + public String decode(String input) { + StringBuilder sb = new StringBuilder(); + PushbackSequence pbs = new PushBackSequenceImpl(input); + while (pbs.hasNext()) { + Integer c = decodeCharacter(pbs); + if (c != null && Character.isValidCodePoint(c)) { + sb.appendCodePoint(c); + } else { + sb.appendCodePoint(pbs.next()); + } + } + return sb.toString(); + } } diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java b/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java index 7ebe0f42a..31b338d90 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java @@ -1,70 +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 * http://www.owasp.org/index.php/ESAPI. * * Copyright (c) 2017 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Matt Seil (mseil .at. owasp.org) * @created 2017 - * + * */ package org.owasp.esapi.codecs; /** * This Abstract class provides the generic logic for using a {@link PushbackSequence} - * in regards to iterating strings. The final Impl is intended for the user to supply + * in regard to iterating strings. The final Impl is intended for the user to supply * a type T such that the pushback interface can be utilized for sequences - * of type T. Presently this generic class is limited by the fact that - * input is a String. - * + * of type T. Presently this generic class is limited by the fact that + * input is a String. + * * @author Matt Seil * * @param */ public abstract class AbstractPushbackSequence implements PushbackSequence { - protected String input; - protected T pushback; - protected T temp; - protected int index = 0; - protected int mark = 0; + protected String input; + protected T pushback; + protected T temp; + protected int index = 0; + protected int mark = 0; - public AbstractPushbackSequence(String input) { - this.input = input; - } + public AbstractPushbackSequence(String input) { + this.input = input; + } - /** - * {@inheritDoc} - */ - public void pushback(T c) { - pushback = c; - } + /** + * {@inheritDoc} + */ + public void pushback(T c) { + pushback = c; + } - /** - * {@inheritDoc} - */ - public int index() { - return index; - } + /** + * {@inheritDoc} + */ + public int index() { + return index; + } - /** - * {@inheritDoc} - */ - 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; - } + /** + * {@inheritDoc} + */ + 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; + } } diff --git a/src/main/java/org/owasp/esapi/codecs/Base64.java b/src/main/java/org/owasp/esapi/codecs/Base64.java index 1af941510..0d7097afd 100644 --- a/src/main/java/org/owasp/esapi/codecs/Base64.java +++ b/src/main/java/org/owasp/esapi/codecs/Base64.java @@ -72,7 +72,7 @@ * 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). - *

  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + *
  • v1.5 - Output stream passes 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.
  • *
  • v1.4 - Added helper methods to read/write files.
  • diff --git a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java index 1e27ea8a0..9ffb60f32 100644 --- a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java @@ -1,23 +1,27 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ package org.owasp.esapi.codecs; +import java.util.regex.Pattern; + +import org.owasp.esapi.codecs.ref.EncodingPatternPreservation; + /** * Implementation of the Codec interface for backslash encoding used in CSS. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -25,158 +29,171 @@ */ public class CSSCodec extends AbstractCharacterCodec { - private static final Character REPLACEMENT = '\ufffd'; + private static final Character REPLACEMENT = '\ufffd'; + //rgb (###,###,###) OR rgb(###%,###%,###%) + //([rR][gG][bB])\s*\(\s*\d{1,3}\s*(\%)?\s*,\s*\d{1,3}\s*(\%)?\s*,\s*\d{1,3}\s*(\%)?\s*\) + private static final String RGB_TRPLT = "([rR][gG][bB])\\s*\\(\\s*\\d{1,3}\\s*(\\%)?\\s*,\\s*\\d{1,3}\\s*(\\%)?\\s*,\\s*\\d{1,3}\\s*(\\%)?\\s*\\)"; + private static final Pattern RGB_TRPLT_PATTERN = Pattern.compile(RGB_TRPLT); + + @Override + public String encode(char[] immune, String input) { + EncodingPatternPreservation tripletCheck = new EncodingPatternPreservation(RGB_TRPLT_PATTERN); + String inputChk = tripletCheck.captureAndReplaceMatches(input); + String result = super.encode(immune, inputChk); + + return tripletCheck.restoreOriginalContent(result); + } /** - * {@inheritDoc} - * + * {@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 = super.getHexForNonAlphanumeric(c); - if ( hex == null ) { - return ""+c; - } - - // return the hex and end in whitespace to terminate + // check for immune characters + if ( containsCharacter(c, immune ) ) { + return ""+c; + } + + // check for alphanumeric characters + String hex = super.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. - */ + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, + * or null if no decoding is possible. + */ @SuppressWarnings("fallthrough") public Character decodeCharacter(PushbackSequence 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': // Intentional fall through - case '\f': // Intentional fall through - // bs followed 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); - } - } + { + 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': // Intentional fall through + case '\f': // Intentional fall through + // bs followed 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 index 1d6f2d09a..b46de6d5d 100644 --- a/src/main/java/org/owasp/esapi/codecs/Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/Codec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -22,97 +22,124 @@ * 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. - * + *

    + * Other than the interfaces, very few of these concrete classes are intended to be used directly. + * Rather, most of them are used through implementations of the {@link org.owasp.esapi.Encoder} + * interface. While the OWASP team over the years have made every effort to be extra cautious, the + * various {@code Codec} implementations can offer NO GUARANTEE of safety if the client is + * using these {@code Codec} classes directly. Therefore, if the client is using + * these classes directly, it is highly advised to practice security-in-depth + * and also perform canonicalization, followed by strict input validation, both + * prior to encoding and after decoding, to protect your application from input-based + * attacks. + *

    + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 - * - * @author Matt Seil (mseil .at. owasp.org) + * + * @author Matt Seil (mseil .at. owasp.org) * @since June 1, 2017 * @see org.owasp.esapi.Encoder + * @see org.owasp.esapi.Validator */ public interface 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); + /** + * 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); + + /** + * Default implementation that should be overridden in specific codecs. + * + * @param immune + * array of chars to NOT encode. Use with caution. + * @param c + * the Character to encode + * @return + * the encoded Character + */ + public String encodeCharacter( char[] immune, Character c ); + + /** + * Default codepoint implementation that should be overridden in specific codecs. + * + * @param immune + * @param codePoint + * the integer to encode + * @return + * the encoded Character + */ + public String encodeCharacter( char[] immune, int codePoint ); + + /** + * 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); - /** - * Default implementation that should be overridden in specific codecs. - * - * @param immune - * array of chars to NOT encode. Use with caution. - * @param c - * the Character to encode - * @return - * the encoded Character - */ - public String encodeCharacter( char[] immune, Character c ); - - /** - * Default codepoint implementation that should be overridden in specific codecs. - * - * @param immune - * @param codePoint - * the integer to encode - * @return - * the encoded Character - */ - public String encodeCharacter( char[] immune, int codePoint ); + /** + * Returns the decoded version of the next character from the input string and advances the + * current character in the {@code PushbackSequence}. If the current character is not encoded, this + * method MUST reset the {@code PushbackString}. + * + * @param input the Character to decode + * + * @return the decoded Character + */ + public T decodeCharacter( PushbackSequence input ); - /** - * 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); + /** + * 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 String getHexForNonAlphanumeric(char c); - /** - * Returns the decoded version of the next character from the input string and advances the - * current character in the PushbackSequence. If the current character is not encoded, this - * method MUST reset the PushbackString. - * - * @param input the Character to decode - * - * @return the decoded Character - */ - public T decodeCharacter( PushbackSequence input ); + /** + * 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 String getHexForNonAlphanumeric(int c); - /** - * 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 String getHexForNonAlphanumeric(char c); - - /** - * 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 String getHexForNonAlphanumeric(int c); + /** + * Convert the {@code char} parameter to its octal representation. + * @param c the character for which to return the new representation. + * @return the octal representation. + */ + public String toOctal(char c); - public String toOctal(char c); + /** + * Convert the {@code char} parameter to its hexadecimal representation. + * @param c the character for which to return the new representation. + * @return the hexadecimal representation. + */ + public String toHex(char c); - public String toHex(char c); - - public String toHex(int c); + /** + * Convert the {@code int} parameter to its hexadecimal representation. + * @param c the character for which to return the new representation. + * @return the hexadecimal representation. + */ + public String toHex(int c); - /** - * Utility to search a char[] for a specific char. - * - * @param c - * @param array - * @return True if the supplied array contains the specified character. False otherwise. - */ - public boolean containsCharacter( char c, char[] array ); + /** + * Utility to search a char[] for a specific char. + * + * @param c + * @param array + * @return True if the supplied array contains the specified character. False otherwise. + */ + public boolean containsCharacter( char c, char[] array ); } diff --git a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java index 850d9a6aa..61d1f70cd 100644 --- a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java @@ -14,55 +14,68 @@ /** - * Implementation of the Codec interface for DB2 strings. This function will only protect you from SQLi in limited situations. - * + * Implementation of the Codec interface for IBM Db2 strings. + * This function will only protect you from SQLi in limited situations. + * To improve your chances of success, you made also need to do some + * additional canonicalization and input validation first. Before using this class, + * please be sure to read the "SECURITY WARNING" in + * {@link org.owasp.esapi.Encoder#encodeForSQL} + * before using this particular {@link org.owasp.esapi.codecs.Codec} and raising your hope of finding + * a silver bullet to kill all the SQLi werewolves. + * * @author Sivasankar Tanakala (stanakal@TRS.NYC.NY.US) * @since October 26, 2010 * @see org.owasp.esapi.Encoder + * @see + * ESAPI Security Bulletin #13 + * @deprecated This class is considered dangerous and not easily made safe and thus under strong + * consideration to be removed within 1 years time after the 2.7.0.0 release. Please + * see the referenced ESAPI Security Bulletin #13 for further details. */ +@Deprecated public class DB2Codec extends AbstractCharacterCodec { - public String encodeCharacter(char[] immune, Character c) { + public String encodeCharacter(char[] immune, Character c) { - if (c.charValue() == '\'') - return "\'\'"; + if (c.charValue() == '\'') + return "\'\'"; - if (c.charValue() == ';') - return "."; + if (c.charValue() == ';') + return "."; - return "" + c; - } + return "" + c; + } - public Character decodeCharacter(PushbackString input) { + public Character decodeCharacter(PushbackString input) { - input.mark(); - Character first = input.next(); + input.mark(); + Character first = input.next(); - if (first == null) { - input.reset(); - return null; - } + if (first == null) { + input.reset(); + return null; + } - // if this is not an encoded character, return null + // if this is not an encoded character, return null - if (first.charValue() != '\'') { - input.reset(); - return null; - } + if (first.charValue() != '\'') { + input.reset(); + return null; + } - Character second = input.next(); + Character second = input.next(); - if (second == null) { - input.reset(); - return null; - } + if (second == null) { + input.reset(); + return null; + } - // if this is not an encoded character, return null - if (second.charValue() != '\'') { - 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 + return (Character.valueOf('\'')); + } +} diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index 7fc805938..d71b6e536 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -1,18 +1,18 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2017 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Matt Seil (mseil .at. owasp.org) - * @created 2017 - * + * @created 2017 + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @created 2007 @@ -26,573 +26,573 @@ /** * Implementation of the Codec interface for HTML entity encoding. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 - * - * @author Matt Seil (mseil .at. owasp.org) (mseil .at. owasp.org) - * + * + * @author Matt Seil (mseil .at. owasp.org) (mseil .at. owasp.org) + * * @see org.owasp.esapi.Encoder */ public class HTMLEntityCodec extends AbstractIntegerCodec { - 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 characterToEntityMap = mkCharacterToEntityMap(); + 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 characterToEntityMap = mkCharacterToEntityMap(); - private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); + private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); /** * */ public HTMLEntityCodec() { - } + } /** * Given an array of {@code char}, scan the input {@code String} and encode unsafe - * codePoints, except for codePoints passed into the {@code char} array. + * codePoints, except for codePoints passed into the {@code char} array. *

    - * WARNING: This method will silently discard any code point per the - * call to {@code Character.isValidCodePoint( int )} method. - * + * WARNING: This method will silently discard an invalid code point according to + * the result of {@code Character.isValidCodePoint( int )} method. + * * {@inheritDoc} */ - @Override - public String encode(char[] immune, String input) { - StringBuilder sb = new StringBuilder(); - for(int offset = 0; offset < input.length(); ){ - final int point = input.codePointAt(offset); - if(Character.isValidCodePoint(point)){ - sb.append(encodeCharacter(immune, point)); - } - offset += Character.charCount(point); - } - return sb.toString(); - } - - /** - * {@inheritDoc} - * + @Override + public String encode(char[] immune, String input) { + StringBuilder sb = new StringBuilder(); + for(int offset = 0; offset < input.length(); ){ + final int point = input.codePointAt(offset); + if(Character.isValidCodePoint(point)){ + sb.append(encodeCharacter(immune, point)); + } + offset += Character.charCount(point); + } + return sb.toString(); + } + + /** + * {@inheritDoc} + * * Encodes a codePoint for safe use in an HTML entity field. * @param immune */ - @Override - public String encodeCharacter( char[] immune, int codePoint ) { - - // check for immune characters - // Cast the codePoint to a char because we want to limit immunity to the BMP field only. - if ( containsCharacter( (char) codePoint, immune ) && Character.isValidCodePoint(codePoint)) { - return new StringBuilder().appendCodePoint(codePoint).toString(); - } - - // check for alphanumeric characters - String hex = super.getHexForNonAlphanumeric(codePoint); - if ( hex == null && Character.isValidCodePoint(codePoint)) { - return new StringBuilder().appendCodePoint(codePoint).toString(); - } - // check for illegal characters - if ( ( codePoint <= 0x1f - && codePoint != '\t' - && codePoint != '\n' - && codePoint != '\r' ) - || ( codePoint >= 0x7f && codePoint <= 0x9f ) ) - { - hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it - codePoint = REPLACEMENT_CHAR; - } - - // check if there's a defined entity - String entityName = characterToEntityMap.get(codePoint); - 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 Integer decodeCharacter( PushbackSequence input ) { - input.mark(); - Integer 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 - Integer second = input.next(); - if ( second == null ) { - input.reset(); - return null; - } - - if (second == '#' ) { - // handle numbers - Integer c = getNumericEntity( input ); - if ( c != null ) return c; - } else if ( Character.isLetter( second ) ) { - // handle entities - input.pushback( second ); - Integer 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 Integer getNumericEntity( PushbackSequence input ) { - Integer 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 Integer parseNumber( PushbackSequence input ) { - StringBuilder sb = new StringBuilder(); - while( input.hasNext() ) { - Integer c = input.peek(); - - // if character is a digit then add it on and keep going - if ( Character.isDigit( c ) && Character.isValidCodePoint(c) ) { - sb.appendCodePoint( 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()); + @Override + public String encodeCharacter( char[] immune, int codePoint ) { + + // check for immune characters + // Cast the codePoint to a char because we want to limit immunity to the BMP field only. + if ( containsCharacter( (char) codePoint, immune ) && Character.isValidCodePoint(codePoint)) { + return new StringBuilder().appendCodePoint(codePoint).toString(); + } + + // check for alphanumeric characters + String hex = super.getHexForNonAlphanumeric(codePoint); + if ( hex == null && Character.isValidCodePoint(codePoint)) { + return new StringBuilder().appendCodePoint(codePoint).toString(); + } + // check for illegal characters + if ( ( codePoint <= 0x1f + && codePoint != '\t' + && codePoint != '\n' + && codePoint != '\r' ) + || ( codePoint >= 0x7f && codePoint <= 0x9f ) ) + { + hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it + codePoint = REPLACEMENT_CHAR; + } + + // check if there's a defined entity + String entityName = characterToEntityMap.get(codePoint); + 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 semicolon, upper/lower case: + * &#dddd; + * &#xhhhh; + * &name; + */ + public Integer decodeCharacter( PushbackSequence input ) { + input.mark(); + Integer 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 + Integer second = input.next(); + if ( second == null ) { + input.reset(); + return null; + } + + if (second == '#' ) { + // handle numbers + Integer c = getNumericEntity( input ); + if ( c != null ) return c; + } else if ( Character.isLetter( second ) ) { + // handle entities + input.pushback( second ); + Integer 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 Integer getNumericEntity( PushbackSequence input ) { + Integer 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 Integer parseNumber( PushbackSequence input ) { + StringBuilder sb = new StringBuilder(); + while( input.hasNext() ) { + Integer c = input.peek(); + + // if character is a digit then add it on and keep going + if ( Character.isDigit( c ) && Character.isValidCodePoint(c) ) { + sb.appendCodePoint( c ); + input.next(); + + // if character is a semicolon, 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 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 Integer parseHex( PushbackSequence input ) { - StringBuilder sb = new StringBuilder(); - while( input.hasNext() ) { - Integer c = input.peek(); - - // if character is a hex digit then add it on and keep going - //This statement implicitly tests for Character.isValidCodePoint(int) - if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) { - sb.appendCodePoint( 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); + } 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 Integer parseHex( PushbackSequence input ) { + StringBuilder sb = new StringBuilder(); + while( input.hasNext() ) { + Integer c = input.peek(); + + // if character is a hex digit then add it on and keep going + //This statement implicitly tests for Character.isValidCodePoint(int) + if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) { + sb.appendCodePoint( c ); + input.next(); + + // if character is a semicolon, 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 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 Integer getNamedEntity( PushbackSequence input ) { - StringBuilder possible = new StringBuilder(); - Entry entry; - int len; - - // kludge around PushbackString.... - len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); - for(int i=0;i exactEntry = entityToCharacterTrie.getLongestMatch(possibleStringLowerCase); - if(exactEntry != null) entry = exactEntry; - } - 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 mkCharacterToEntityMap() - { - Map map = new HashMap(252); - - map.put(34, "quot"); /* quotation mark */ - map.put(38, "amp"); /* ampersand */ - map.put(60, "lt"); /* less-than sign */ - map.put(62, "gt"); /* greater-than sign */ - map.put(160, "nbsp"); /* no-break space */ - map.put(161, "iexcl"); /* inverted exclamation mark */ - map.put(162, "cent"); /* cent sign */ - map.put(163, "pound"); /* pound sign */ - map.put(164, "curren"); /* currency sign */ - map.put(165, "yen"); /* yen sign */ - map.put(166, "brvbar"); /* broken bar */ - map.put(167, "sect"); /* section sign */ - map.put(168, "uml"); /* diaeresis */ - map.put(169, "copy"); /* copyright sign */ - map.put(170, "ordf"); /* feminine ordinal indicator */ - map.put(171, "laquo"); /* left-pointing double angle quotation mark */ - map.put(172, "not"); /* not sign */ - map.put(173, "shy"); /* soft hyphen */ - map.put(174, "reg"); /* registered sign */ - map.put(175, "macr"); /* macron */ - map.put(176, "deg"); /* degree sign */ - map.put(177, "plusmn"); /* plus-minus sign */ - map.put(178, "sup2"); /* superscript two */ - map.put(179, "sup3"); /* superscript three */ - map.put(180, "acute"); /* acute accent */ - map.put(181, "micro"); /* micro sign */ - map.put(182, "para"); /* pilcrow sign */ - map.put(183, "middot"); /* middle dot */ - map.put(184, "cedil"); /* cedilla */ - map.put(185, "sup1"); /* superscript one */ - map.put(186, "ordm"); /* masculine ordinal indicator */ - map.put(187, "raquo"); /* right-pointing double angle quotation mark */ - map.put(188, "frac14"); /* vulgar fraction one quarter */ - map.put(189, "frac12"); /* vulgar fraction one half */ - map.put(190, "frac34"); /* vulgar fraction three quarters */ - map.put(191, "iquest"); /* inverted question mark */ - map.put(192, "Agrave"); /* Latin capital letter a with grave */ - map.put(193, "Aacute"); /* Latin capital letter a with acute */ - map.put(194, "Acirc"); /* Latin capital letter a with circumflex */ - map.put(195, "Atilde"); /* Latin capital letter a with tilde */ - map.put(196, "Auml"); /* Latin capital letter a with diaeresis */ - map.put(197, "Aring"); /* Latin capital letter a with ring above */ - map.put(198, "AElig"); /* Latin capital letter ae */ - map.put(199, "Ccedil"); /* Latin capital letter c with cedilla */ - map.put(200, "Egrave"); /* Latin capital letter e with grave */ - map.put(201, "Eacute"); /* Latin capital letter e with acute */ - map.put(202, "Ecirc"); /* Latin capital letter e with circumflex */ - map.put(203, "Euml"); /* Latin capital letter e with diaeresis */ - map.put(204, "Igrave"); /* Latin capital letter i with grave */ - map.put(205, "Iacute"); /* Latin capital letter i with acute */ - map.put(206, "Icirc"); /* Latin capital letter i with circumflex */ - map.put(207, "Iuml"); /* Latin capital letter i with diaeresis */ - map.put(208, "ETH"); /* Latin capital letter eth */ - map.put(209, "Ntilde"); /* Latin capital letter n with tilde */ - map.put(210, "Ograve"); /* Latin capital letter o with grave */ - map.put(211, "Oacute"); /* Latin capital letter o with acute */ - map.put(212, "Ocirc"); /* Latin capital letter o with circumflex */ - map.put(213, "Otilde"); /* Latin capital letter o with tilde */ - map.put(214, "Ouml"); /* Latin capital letter o with diaeresis */ - map.put(215, "times"); /* multiplication sign */ - map.put(216, "Oslash"); /* Latin capital letter o with stroke */ - map.put(217, "Ugrave"); /* Latin capital letter u with grave */ - map.put(218, "Uacute"); /* Latin capital letter u with acute */ - map.put(219, "Ucirc"); /* Latin capital letter u with circumflex */ - map.put(220, "Uuml"); /* Latin capital letter u with diaeresis */ - map.put(221, "Yacute"); /* Latin capital letter y with acute */ - map.put(222, "THORN"); /* Latin capital letter thorn */ - map.put(223, "szlig"); /* Latin small letter sharp sXCOMMAX German Eszett */ - map.put(224, "agrave"); /* Latin small letter a with grave */ - map.put(225, "aacute"); /* Latin small letter a with acute */ - map.put(226, "acirc"); /* Latin small letter a with circumflex */ - map.put(227, "atilde"); /* Latin small letter a with tilde */ - map.put(228, "auml"); /* Latin small letter a with diaeresis */ - map.put(229, "aring"); /* Latin small letter a with ring above */ - map.put(230, "aelig"); /* Latin lowercase ligature ae */ - map.put(231, "ccedil"); /* Latin small letter c with cedilla */ - map.put(232, "egrave"); /* Latin small letter e with grave */ - map.put(233, "eacute"); /* Latin small letter e with acute */ - map.put(234, "ecirc"); /* Latin small letter e with circumflex */ - map.put(235, "euml"); /* Latin small letter e with diaeresis */ - map.put(236, "igrave"); /* Latin small letter i with grave */ - map.put(237, "iacute"); /* Latin small letter i with acute */ - map.put(238, "icirc"); /* Latin small letter i with circumflex */ - map.put(239, "iuml"); /* Latin small letter i with diaeresis */ - map.put(240, "eth"); /* Latin small letter eth */ - map.put(241, "ntilde"); /* Latin small letter n with tilde */ - map.put(242, "ograve"); /* Latin small letter o with grave */ - map.put(243, "oacute"); /* Latin small letter o with acute */ - map.put(244, "ocirc"); /* Latin small letter o with circumflex */ - map.put(245, "otilde"); /* Latin small letter o with tilde */ - map.put(246, "ouml"); /* Latin small letter o with diaeresis */ - map.put(247, "divide"); /* division sign */ - map.put(248, "oslash"); /* Latin small letter o with stroke */ - map.put(249, "ugrave"); /* Latin small letter u with grave */ - map.put(250, "uacute"); /* Latin small letter u with acute */ - map.put(251, "ucirc"); /* Latin small letter u with circumflex */ - map.put(252, "uuml"); /* Latin small letter u with diaeresis */ - map.put(253, "yacute"); /* Latin small letter y with acute */ - map.put(254, "thorn"); /* Latin small letter thorn */ - map.put(255, "yuml"); /* Latin small letter y with diaeresis */ - map.put(338, "OElig"); /* Latin capital ligature oe */ - map.put(339, "oelig"); /* Latin small ligature oe */ - map.put(352, "Scaron"); /* Latin capital letter s with caron */ - map.put(353, "scaron"); /* Latin small letter s with caron */ - map.put(376, "Yuml"); /* Latin capital letter y with diaeresis */ - map.put(402, "fnof"); /* Latin small letter f with hook */ - map.put(710, "circ"); /* modifier letter circumflex accent */ - map.put(732, "tilde"); /* small tilde */ - map.put(913, "Alpha"); /* Greek capital letter alpha */ - map.put(914, "Beta"); /* Greek capital letter beta */ - map.put(915, "Gamma"); /* Greek capital letter gamma */ - map.put(916, "Delta"); /* Greek capital letter delta */ - map.put(917, "Epsilon"); /* Greek capital letter epsilon */ - map.put(918, "Zeta"); /* Greek capital letter zeta */ - map.put(919, "Eta"); /* Greek capital letter eta */ - map.put(920, "Theta"); /* Greek capital letter theta */ - map.put(921, "Iota"); /* Greek capital letter iota */ - map.put(922, "Kappa"); /* Greek capital letter kappa */ - map.put(923, "Lambda"); /* Greek capital letter lambda */ - map.put(924, "Mu"); /* Greek capital letter mu */ - map.put(925, "Nu"); /* Greek capital letter nu */ - map.put(926, "Xi"); /* Greek capital letter xi */ - map.put(927, "Omicron"); /* Greek capital letter omicron */ - map.put(928, "Pi"); /* Greek capital letter pi */ - map.put(929, "Rho"); /* Greek capital letter rho */ - map.put(931, "Sigma"); /* Greek capital letter sigma */ - map.put(932, "Tau"); /* Greek capital letter tau */ - map.put(933, "Upsilon"); /* Greek capital letter upsilon */ - map.put(934, "Phi"); /* Greek capital letter phi */ - map.put(935, "Chi"); /* Greek capital letter chi */ - map.put(936, "Psi"); /* Greek capital letter psi */ - map.put(937, "Omega"); /* Greek capital letter omega */ - map.put(945, "alpha"); /* Greek small letter alpha */ - map.put(946, "beta"); /* Greek small letter beta */ - map.put(947, "gamma"); /* Greek small letter gamma */ - map.put(948, "delta"); /* Greek small letter delta */ - map.put(949, "epsilon"); /* Greek small letter epsilon */ - map.put(950, "zeta"); /* Greek small letter zeta */ - map.put(951, "eta"); /* Greek small letter eta */ - map.put(952, "theta"); /* Greek small letter theta */ - map.put(953, "iota"); /* Greek small letter iota */ - map.put(954, "kappa"); /* Greek small letter kappa */ - map.put(955, "lambda"); /* Greek small letter lambda */ - map.put(956, "mu"); /* Greek small letter mu */ - map.put(957, "nu"); /* Greek small letter nu */ - map.put(958, "xi"); /* Greek small letter xi */ - map.put(959, "omicron"); /* Greek small letter omicron */ - map.put(960, "pi"); /* Greek small letter pi */ - map.put(961, "rho"); /* Greek small letter rho */ - map.put(962, "sigmaf"); /* Greek small letter final sigma */ - map.put(963, "sigma"); /* Greek small letter sigma */ - map.put(964, "tau"); /* Greek small letter tau */ - map.put(965, "upsilon"); /* Greek small letter upsilon */ - map.put(966, "phi"); /* Greek small letter phi */ - map.put(967, "chi"); /* Greek small letter chi */ - map.put(968, "psi"); /* Greek small letter psi */ - map.put(969, "omega"); /* Greek small letter omega */ - map.put(977, "thetasym"); /* Greek theta symbol */ - map.put(978, "upsih"); /* Greek upsilon with hook symbol */ - map.put(982, "piv"); /* Greek pi symbol */ - map.put(8194, "ensp"); /* en space */ - map.put(8195, "emsp"); /* em space */ - map.put(8201, "thinsp"); /* thin space */ - map.put(8204, "zwnj"); /* zero width non-joiner */ - map.put(8205, "zwj"); /* zero width joiner */ - map.put(8206, "lrm"); /* left-to-right mark */ - map.put(8207, "rlm"); /* right-to-left mark */ - map.put(8211, "ndash"); /* en dash */ - map.put(8212, "mdash"); /* em dash */ - map.put(8216, "lsquo"); /* left single quotation mark */ - map.put(8217, "rsquo"); /* right single quotation mark */ - map.put(8218, "sbquo"); /* single low-9 quotation mark */ - map.put(8220, "ldquo"); /* left double quotation mark */ - map.put(8221, "rdquo"); /* right double quotation mark */ - map.put(8222, "bdquo"); /* double low-9 quotation mark */ - map.put(8224, "dagger"); /* dagger */ - map.put(8225, "Dagger"); /* double dagger */ - map.put(8226, "bull"); /* bullet */ - map.put(8230, "hellip"); /* horizontal ellipsis */ - map.put(8240, "permil"); /* per mille sign */ - map.put(8242, "prime"); /* prime */ - map.put(8243, "Prime"); /* double prime */ - map.put(8249, "lsaquo"); /* single left-pointing angle quotation mark */ - map.put(8250, "rsaquo"); /* single right-pointing angle quotation mark */ - map.put(8254, "oline"); /* overline */ - map.put(8260, "frasl"); /* fraction slash */ - map.put(8364, "euro"); /* euro sign */ - map.put(8465, "image"); /* black-letter capital i */ - map.put(8472, "weierp"); /* script capital pXCOMMAX Weierstrass p */ - map.put(8476, "real"); /* black-letter capital r */ - map.put(8482, "trade"); /* trademark sign */ - map.put(8501, "alefsym"); /* alef symbol */ - map.put(8592, "larr"); /* leftwards arrow */ - map.put(8593, "uarr"); /* upwards arrow */ - map.put(8594, "rarr"); /* rightwards arrow */ - map.put(8595, "darr"); /* downwards arrow */ - map.put(8596, "harr"); /* left right arrow */ - map.put(8629, "crarr"); /* downwards arrow with corner leftwards */ - map.put(8656, "lArr"); /* leftwards double arrow */ - map.put(8657, "uArr"); /* upwards double arrow */ - map.put(8658, "rArr"); /* rightwards double arrow */ - map.put(8659, "dArr"); /* downwards double arrow */ - map.put(8660, "hArr"); /* left right double arrow */ - map.put(8704, "forall"); /* for all */ - map.put(8706, "part"); /* partial differential */ - map.put(8707, "exist"); /* there exists */ - map.put(8709, "empty"); /* empty set */ - map.put(8711, "nabla"); /* nabla */ - map.put(8712, "isin"); /* element of */ - map.put(8713, "notin"); /* not an element of */ - map.put(8715, "ni"); /* contains as member */ - map.put(8719, "prod"); /* n-ary product */ - map.put(8721, "sum"); /* n-ary summation */ - map.put(8722, "minus"); /* minus sign */ - map.put(8727, "lowast"); /* asterisk operator */ - map.put(8730, "radic"); /* square root */ - map.put(8733, "prop"); /* proportional to */ - map.put(8734, "infin"); /* infinity */ - map.put(8736, "ang"); /* angle */ - map.put(8743, "and"); /* logical and */ - map.put(8744, "or"); /* logical or */ - map.put(8745, "cap"); /* intersection */ - map.put(8746, "cup"); /* union */ - map.put(8747, "int"); /* integral */ - map.put(8756, "there4"); /* therefore */ - map.put(8764, "sim"); /* tilde operator */ - map.put(8773, "cong"); /* congruent to */ - map.put(8776, "asymp"); /* almost equal to */ - map.put(8800, "ne"); /* not equal to */ - map.put(8801, "equiv"); /* identical toXCOMMAX equivalent to */ - map.put(8804, "le"); /* less-than or equal to */ - map.put(8805, "ge"); /* greater-than or equal to */ - map.put(8834, "sub"); /* subset of */ - map.put(8835, "sup"); /* superset of */ - map.put(8836, "nsub"); /* not a subset of */ - map.put(8838, "sube"); /* subset of or equal to */ - map.put(8839, "supe"); /* superset of or equal to */ - map.put(8853, "oplus"); /* circled plus */ - map.put(8855, "otimes"); /* circled times */ - map.put(8869, "perp"); /* up tack */ - map.put(8901, "sdot"); /* dot operator */ - map.put(8968, "lceil"); /* left ceiling */ - map.put(8969, "rceil"); /* right ceiling */ - map.put(8970, "lfloor"); /* left floor */ - map.put(8971, "rfloor"); /* right floor */ - map.put(9001, "lang"); /* left-pointing angle bracket */ - map.put(9002, "rang"); /* right-pointing angle bracket */ - map.put(9674, "loz"); /* lozenge */ - map.put(9824, "spades"); /* black spade suit */ - map.put(9827, "clubs"); /* black club suit */ - map.put(9829, "hearts"); /* black heart suit */ - map.put(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 mkEntityToCharacterTrie() - { - Trie trie = new HashTrie(); - - for(Map.Entry entry : characterToEntityMap.entrySet()) - trie.put(entry.getValue(),entry.getKey()); - return Trie.Util.unmodifiable(trie); - } + } 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 Integer getNamedEntity( PushbackSequence input ) { + StringBuilder possible = new StringBuilder(); + Entry entry; + int len; + + // kludge around PushbackString.... + len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); + for(int i=0;i exactEntry = entityToCharacterTrie.getLongestMatch(possibleStringLowerCase); + if(exactEntry != null) entry = exactEntry; + } + 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 mkCharacterToEntityMap() + { + Map map = new HashMap(252); + + map.put(34, "quot"); /* quotation mark */ + map.put(38, "amp"); /* ampersand */ + map.put(60, "lt"); /* less-than sign */ + map.put(62, "gt"); /* greater-than sign */ + map.put(160, "nbsp"); /* no-break space */ + map.put(161, "iexcl"); /* inverted exclamation mark */ + map.put(162, "cent"); /* cent sign */ + map.put(163, "pound"); /* pound sign */ + map.put(164, "curren"); /* currency sign */ + map.put(165, "yen"); /* yen sign */ + map.put(166, "brvbar"); /* broken bar */ + map.put(167, "sect"); /* section sign */ + map.put(168, "uml"); /* diaeresis */ + map.put(169, "copy"); /* copyright sign */ + map.put(170, "ordf"); /* feminine ordinal indicator */ + map.put(171, "laquo"); /* left-pointing double angle quotation mark */ + map.put(172, "not"); /* not sign */ + map.put(173, "shy"); /* soft hyphen */ + map.put(174, "reg"); /* registered sign */ + map.put(175, "macr"); /* macron */ + map.put(176, "deg"); /* degree sign */ + map.put(177, "plusmn"); /* plus-minus sign */ + map.put(178, "sup2"); /* superscript two */ + map.put(179, "sup3"); /* superscript three */ + map.put(180, "acute"); /* acute accent */ + map.put(181, "micro"); /* micro sign */ + map.put(182, "para"); /* pilcrow sign */ + map.put(183, "middot"); /* middle dot */ + map.put(184, "cedil"); /* cedilla */ + map.put(185, "sup1"); /* superscript one */ + map.put(186, "ordm"); /* masculine ordinal indicator */ + map.put(187, "raquo"); /* right-pointing double angle quotation mark */ + map.put(188, "frac14"); /* vulgar fraction one quarter */ + map.put(189, "frac12"); /* vulgar fraction one half */ + map.put(190, "frac34"); /* vulgar fraction three quarters */ + map.put(191, "iquest"); /* inverted question mark */ + map.put(192, "Agrave"); /* Latin capital letter a with grave */ + map.put(193, "Aacute"); /* Latin capital letter a with acute */ + map.put(194, "Acirc"); /* Latin capital letter a with circumflex */ + map.put(195, "Atilde"); /* Latin capital letter a with tilde */ + map.put(196, "Auml"); /* Latin capital letter a with diaeresis */ + map.put(197, "Aring"); /* Latin capital letter a with ring above */ + map.put(198, "AElig"); /* Latin capital letter ae */ + map.put(199, "Ccedil"); /* Latin capital letter c with cedilla */ + map.put(200, "Egrave"); /* Latin capital letter e with grave */ + map.put(201, "Eacute"); /* Latin capital letter e with acute */ + map.put(202, "Ecirc"); /* Latin capital letter e with circumflex */ + map.put(203, "Euml"); /* Latin capital letter e with diaeresis */ + map.put(204, "Igrave"); /* Latin capital letter i with grave */ + map.put(205, "Iacute"); /* Latin capital letter i with acute */ + map.put(206, "Icirc"); /* Latin capital letter i with circumflex */ + map.put(207, "Iuml"); /* Latin capital letter i with diaeresis */ + map.put(208, "ETH"); /* Latin capital letter eth */ + map.put(209, "Ntilde"); /* Latin capital letter n with tilde */ + map.put(210, "Ograve"); /* Latin capital letter o with grave */ + map.put(211, "Oacute"); /* Latin capital letter o with acute */ + map.put(212, "Ocirc"); /* Latin capital letter o with circumflex */ + map.put(213, "Otilde"); /* Latin capital letter o with tilde */ + map.put(214, "Ouml"); /* Latin capital letter o with diaeresis */ + map.put(215, "times"); /* multiplication sign */ + map.put(216, "Oslash"); /* Latin capital letter o with stroke */ + map.put(217, "Ugrave"); /* Latin capital letter u with grave */ + map.put(218, "Uacute"); /* Latin capital letter u with acute */ + map.put(219, "Ucirc"); /* Latin capital letter u with circumflex */ + map.put(220, "Uuml"); /* Latin capital letter u with diaeresis */ + map.put(221, "Yacute"); /* Latin capital letter y with acute */ + map.put(222, "THORN"); /* Latin capital letter thorn */ + map.put(223, "szlig"); /* Latin small letter sharp sXCOMMAX German Eszett */ + map.put(224, "agrave"); /* Latin small letter a with grave */ + map.put(225, "aacute"); /* Latin small letter a with acute */ + map.put(226, "acirc"); /* Latin small letter a with circumflex */ + map.put(227, "atilde"); /* Latin small letter a with tilde */ + map.put(228, "auml"); /* Latin small letter a with diaeresis */ + map.put(229, "aring"); /* Latin small letter a with ring above */ + map.put(230, "aelig"); /* Latin lowercase ligature ae */ + map.put(231, "ccedil"); /* Latin small letter c with cedilla */ + map.put(232, "egrave"); /* Latin small letter e with grave */ + map.put(233, "eacute"); /* Latin small letter e with acute */ + map.put(234, "ecirc"); /* Latin small letter e with circumflex */ + map.put(235, "euml"); /* Latin small letter e with diaeresis */ + map.put(236, "igrave"); /* Latin small letter i with grave */ + map.put(237, "iacute"); /* Latin small letter i with acute */ + map.put(238, "icirc"); /* Latin small letter i with circumflex */ + map.put(239, "iuml"); /* Latin small letter i with diaeresis */ + map.put(240, "eth"); /* Latin small letter eth */ + map.put(241, "ntilde"); /* Latin small letter n with tilde */ + map.put(242, "ograve"); /* Latin small letter o with grave */ + map.put(243, "oacute"); /* Latin small letter o with acute */ + map.put(244, "ocirc"); /* Latin small letter o with circumflex */ + map.put(245, "otilde"); /* Latin small letter o with tilde */ + map.put(246, "ouml"); /* Latin small letter o with diaeresis */ + map.put(247, "divide"); /* division sign */ + map.put(248, "oslash"); /* Latin small letter o with stroke */ + map.put(249, "ugrave"); /* Latin small letter u with grave */ + map.put(250, "uacute"); /* Latin small letter u with acute */ + map.put(251, "ucirc"); /* Latin small letter u with circumflex */ + map.put(252, "uuml"); /* Latin small letter u with diaeresis */ + map.put(253, "yacute"); /* Latin small letter y with acute */ + map.put(254, "thorn"); /* Latin small letter thorn */ + map.put(255, "yuml"); /* Latin small letter y with diaeresis */ + map.put(338, "OElig"); /* Latin capital ligature oe */ + map.put(339, "oelig"); /* Latin small ligature oe */ + map.put(352, "Scaron"); /* Latin capital letter s with caron */ + map.put(353, "scaron"); /* Latin small letter s with caron */ + map.put(376, "Yuml"); /* Latin capital letter y with diaeresis */ + map.put(402, "fnof"); /* Latin small letter f with hook */ + map.put(710, "circ"); /* modifier letter circumflex accent */ + map.put(732, "tilde"); /* small tilde */ + map.put(913, "Alpha"); /* Greek capital letter alpha */ + map.put(914, "Beta"); /* Greek capital letter beta */ + map.put(915, "Gamma"); /* Greek capital letter gamma */ + map.put(916, "Delta"); /* Greek capital letter delta */ + map.put(917, "Epsilon"); /* Greek capital letter epsilon */ + map.put(918, "Zeta"); /* Greek capital letter zeta */ + map.put(919, "Eta"); /* Greek capital letter eta */ + map.put(920, "Theta"); /* Greek capital letter theta */ + map.put(921, "Iota"); /* Greek capital letter iota */ + map.put(922, "Kappa"); /* Greek capital letter kappa */ + map.put(923, "Lambda"); /* Greek capital letter lambda */ + map.put(924, "Mu"); /* Greek capital letter mu */ + map.put(925, "Nu"); /* Greek capital letter nu */ + map.put(926, "Xi"); /* Greek capital letter xi */ + map.put(927, "Omicron"); /* Greek capital letter omicron */ + map.put(928, "Pi"); /* Greek capital letter pi */ + map.put(929, "Rho"); /* Greek capital letter rho */ + map.put(931, "Sigma"); /* Greek capital letter sigma */ + map.put(932, "Tau"); /* Greek capital letter tau */ + map.put(933, "Upsilon"); /* Greek capital letter upsilon */ + map.put(934, "Phi"); /* Greek capital letter phi */ + map.put(935, "Chi"); /* Greek capital letter chi */ + map.put(936, "Psi"); /* Greek capital letter psi */ + map.put(937, "Omega"); /* Greek capital letter omega */ + map.put(945, "alpha"); /* Greek small letter alpha */ + map.put(946, "beta"); /* Greek small letter beta */ + map.put(947, "gamma"); /* Greek small letter gamma */ + map.put(948, "delta"); /* Greek small letter delta */ + map.put(949, "epsilon"); /* Greek small letter epsilon */ + map.put(950, "zeta"); /* Greek small letter zeta */ + map.put(951, "eta"); /* Greek small letter eta */ + map.put(952, "theta"); /* Greek small letter theta */ + map.put(953, "iota"); /* Greek small letter iota */ + map.put(954, "kappa"); /* Greek small letter kappa */ + map.put(955, "lambda"); /* Greek small letter lambda */ + map.put(956, "mu"); /* Greek small letter mu */ + map.put(957, "nu"); /* Greek small letter nu */ + map.put(958, "xi"); /* Greek small letter xi */ + map.put(959, "omicron"); /* Greek small letter omicron */ + map.put(960, "pi"); /* Greek small letter pi */ + map.put(961, "rho"); /* Greek small letter rho */ + map.put(962, "sigmaf"); /* Greek small letter final sigma */ + map.put(963, "sigma"); /* Greek small letter sigma */ + map.put(964, "tau"); /* Greek small letter tau */ + map.put(965, "upsilon"); /* Greek small letter upsilon */ + map.put(966, "phi"); /* Greek small letter phi */ + map.put(967, "chi"); /* Greek small letter chi */ + map.put(968, "psi"); /* Greek small letter psi */ + map.put(969, "omega"); /* Greek small letter omega */ + map.put(977, "thetasym"); /* Greek theta symbol */ + map.put(978, "upsih"); /* Greek upsilon with hook symbol */ + map.put(982, "piv"); /* Greek pi symbol */ + map.put(8194, "ensp"); /* en space */ + map.put(8195, "emsp"); /* em space */ + map.put(8201, "thinsp"); /* thin space */ + map.put(8204, "zwnj"); /* zero width non-joiner */ + map.put(8205, "zwj"); /* zero width joiner */ + map.put(8206, "lrm"); /* left-to-right mark */ + map.put(8207, "rlm"); /* right-to-left mark */ + map.put(8211, "ndash"); /* en dash */ + map.put(8212, "mdash"); /* em dash */ + map.put(8216, "lsquo"); /* left single quotation mark */ + map.put(8217, "rsquo"); /* right single quotation mark */ + map.put(8218, "sbquo"); /* single low-9 quotation mark */ + map.put(8220, "ldquo"); /* left double quotation mark */ + map.put(8221, "rdquo"); /* right double quotation mark */ + map.put(8222, "bdquo"); /* double low-9 quotation mark */ + map.put(8224, "dagger"); /* dagger */ + map.put(8225, "Dagger"); /* double dagger */ + map.put(8226, "bull"); /* bullet */ + map.put(8230, "hellip"); /* horizontal ellipsis */ + map.put(8240, "permil"); /* per mille sign */ + map.put(8242, "prime"); /* prime */ + map.put(8243, "Prime"); /* double prime */ + map.put(8249, "lsaquo"); /* single left-pointing angle quotation mark */ + map.put(8250, "rsaquo"); /* single right-pointing angle quotation mark */ + map.put(8254, "oline"); /* overline */ + map.put(8260, "frasl"); /* fraction slash */ + map.put(8364, "euro"); /* euro sign */ + map.put(8465, "image"); /* black-letter capital i */ + map.put(8472, "weierp"); /* script capital pXCOMMAX Weierstrass p */ + map.put(8476, "real"); /* black-letter capital r */ + map.put(8482, "trade"); /* trademark sign */ + map.put(8501, "alefsym"); /* alef symbol */ + map.put(8592, "larr"); /* leftwards arrow */ + map.put(8593, "uarr"); /* upwards arrow */ + map.put(8594, "rarr"); /* rightwards arrow */ + map.put(8595, "darr"); /* downwards arrow */ + map.put(8596, "harr"); /* left right arrow */ + map.put(8629, "crarr"); /* downwards arrow with corner leftwards */ + map.put(8656, "lArr"); /* leftwards double arrow */ + map.put(8657, "uArr"); /* upwards double arrow */ + map.put(8658, "rArr"); /* rightwards double arrow */ + map.put(8659, "dArr"); /* downwards double arrow */ + map.put(8660, "hArr"); /* left right double arrow */ + map.put(8704, "forall"); /* for all */ + map.put(8706, "part"); /* partial differential */ + map.put(8707, "exist"); /* there exists */ + map.put(8709, "empty"); /* empty set */ + map.put(8711, "nabla"); /* nabla */ + map.put(8712, "isin"); /* element of */ + map.put(8713, "notin"); /* not an element of */ + map.put(8715, "ni"); /* contains as member */ + map.put(8719, "prod"); /* n-ary product */ + map.put(8721, "sum"); /* n-ary summation */ + map.put(8722, "minus"); /* minus sign */ + map.put(8727, "lowast"); /* asterisk operator */ + map.put(8730, "radic"); /* square root */ + map.put(8733, "prop"); /* proportional to */ + map.put(8734, "infin"); /* infinity */ + map.put(8736, "ang"); /* angle */ + map.put(8743, "and"); /* logical and */ + map.put(8744, "or"); /* logical or */ + map.put(8745, "cap"); /* intersection */ + map.put(8746, "cup"); /* union */ + map.put(8747, "int"); /* integral */ + map.put(8756, "there4"); /* therefore */ + map.put(8764, "sim"); /* tilde operator */ + map.put(8773, "cong"); /* congruent to */ + map.put(8776, "asymp"); /* almost equal to */ + map.put(8800, "ne"); /* not equal to */ + map.put(8801, "equiv"); /* identical toXCOMMAX equivalent to */ + map.put(8804, "le"); /* less-than or equal to */ + map.put(8805, "ge"); /* greater-than or equal to */ + map.put(8834, "sub"); /* subset of */ + map.put(8835, "sup"); /* superset of */ + map.put(8836, "nsub"); /* not a subset of */ + map.put(8838, "sube"); /* subset of or equal to */ + map.put(8839, "supe"); /* superset of or equal to */ + map.put(8853, "oplus"); /* circled plus */ + map.put(8855, "otimes"); /* circled times */ + map.put(8869, "perp"); /* up tack */ + map.put(8901, "sdot"); /* dot operator */ + map.put(8968, "lceil"); /* left ceiling */ + map.put(8969, "rceil"); /* right ceiling */ + map.put(8970, "lfloor"); /* left floor */ + map.put(8971, "rfloor"); /* right floor */ + map.put(9001, "lang"); /* left-pointing angle bracket */ + map.put(9002, "rang"); /* right-pointing angle bracket */ + map.put(9674, "loz"); /* lozenge */ + map.put(9824, "spades"); /* black spade suit */ + map.put(9827, "clubs"); /* black club suit */ + map.put(9829, "hearts"); /* black heart suit */ + map.put(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 mkEntityToCharacterTrie() + { + Trie trie = new HashTrie(); + + for(Map.Entry 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 index 4c8d41910..cdc9932aa 100644 --- a/src/main/java/org/owasp/esapi/codecs/HashTrie.java +++ b/src/main/java/org/owasp/esapi/codecs/HashTrie.java @@ -18,592 +18,591 @@ * * NOTE:
    *
      - *
    • @see java.util.Map.remove(Object) is not supported.
    • - *
    • - * If deletion support is added, the max key length will need work or removal. - *
    • - *
    • Null values are not supported.
    • + *
    • @see java.util.Map.remove(Object) is not supported.
    • + *
    • + * If deletion support is added, the max key length will need work or removal. + *
    • + *
    • Null values are not supported.
    • *
    * * @author Ed Schaller */ public class HashTrie implements Trie { - private static class Entry implements Map.Entry - { - private CharSequence key; - private T value; - - Entry(CharSequence key, T value) - { - this.key = key; - this.value = value; - } - - /** - * Convenience 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 Entry 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(key,value); - } - - /** - * Convenience 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 Entry newInstanceIfNeeded(CharSequence key, T value) - { - if(value == null || key == null) - return null; - return new Entry(key,value); - } + private static class Entry implements Map.Entry + { + private CharSequence key; + private T value; + + Entry(CharSequence key, T value) + { + this.key = key; + this.value = value; + } + + /** + * Convenience 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 Entry 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(key,value); + } + + /** + * Convenience 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 Entry newInstanceIfNeeded(CharSequence key, T value) + { + if(value == null || key == null) + return null; + return new Entry(key,value); + } /*************/ /* Map.Entry */ /*************/ - public CharSequence getKey() - { - return key; - } + public CharSequence getKey() + { + return key; + } - public T getValue() - { - return value; - } + public T getValue() + { + return value; + } - public T setValue(T value) - { - throw new UnsupportedOperationException(); - } + 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 - { - private T value = null; - private Map> nextMap; - - /** - * Create a new Map for a node level. This is here so - * that if the underlying * Map implementation needs to - * be switched it is easily done. - * @return A new Map for use. - */ - private static Map> newNodeMap() - { - return new HashMap>(); - } - - /** - * Create a new Map for a node level. This is here so - * that if the underlying * Map implementation needs to - * be switched it is easily done. - * @param prev Previous map to use to populate the - * new map. - * @return A new Map for use. - */ - private static Map> newNodeMap(Map> prev) - { - return new HashMap>(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 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 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 associated with the key or null if none exists. - */ - T get(CharSequence key, int pos) - { - Node 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 associated with the longest key match or null if none exists. - */ - Entry getLongestMatch(CharSequence key, int pos) - { - Node nextNode; - Entry 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 associated with the longest key - * match or null if none exists. - */ - Entry getLongestMatch(PushbackReader keyIn, StringBuilder key) throws IOException - { - Node nextNode; - Entry 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 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 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 values(Collection values) - { - if(value != null) - values.add(value); - if(nextMap == null) - return values; - for(Node 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 keySet(StringBuilder key, Set 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> 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> entrySet(StringBuilder key, Set> 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> entry : nextMap.entrySet()) - { - key.setCharAt(len,entry.getKey()); - entry.getValue().entrySet(key,entries); - } - key.setLength(len); - } - return entries; - } - } - - private Node 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 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 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; - } + 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 + { + private T value = null; + private Map> nextMap; + + /** + * Create a new Map for a node level. This is here so + * that if the underlying * Map implementation needs to + * be switched it is easily done. + * @return A new Map for use. + */ + private static Map> newNodeMap() + { + return new HashMap>(); + } + + /** + * Create a new Map for a node level. This is here so + * that if the underlying * Map implementation needs to + * be switched it is easily done. + * @param prev Previous map to use to populate the + * new map. + * @return A new Map for use. + */ + private static Map> newNodeMap(Map> prev) + { + return new HashMap>(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 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 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 associated with the key or null if none exists. + */ + T get(CharSequence key, int pos) + { + Node 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 associated with the longest key match or null if none exists. + */ + Entry getLongestMatch(CharSequence key, int pos) + { + Node nextNode; + Entry 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 key The key that is being looked up at this level. + * @return The Entry associated with the longest key + * match or null if none exists. + */ + Entry getLongestMatch(PushbackReader keyIn, StringBuilder key) throws IOException + { + Node nextNode; + Entry 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 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 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 values(Collection values) + { + if(value != null) + values.add(value); + if(nextMap == null) + return values; + for(Node 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 keySet(StringBuilder key, Set 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> 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> entrySet(StringBuilder key, Set> 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> entry : nextMap.entrySet()) + { + key.setCharAt(len,entry.getKey()); + entry.getValue().entrySet(key,entries); + } + key.setLength(len); + } + return entries; + } + } + + private Node root; + private int maxKeyLen; + private int size; + + public HashTrie() + { + clear(); + } + + /** + * Get the key value entry whose key is the longest prefix match. + * @param key The key to lookup + * @return Entry with the longest matching key. + */ + public Map.Entry getLongestMatch(CharSequence key) + { + if(root == null || key == null) + return null; + return root.getLongestMatch(key, 0); + } + + /** + * Get the key value entry whose 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 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(); - 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 map) - { - for(Map.Entry entry : map.entrySet()) - put(entry.getKey(),entry.getValue()); - } - - /** {@inheritDoc} */ - public Set keySet() - { - Set keys = new HashSet(size); - - if(root == null) - return keys; - return root.keySet(new StringBuilder(), keys); - } - - /** {@inheritDoc} */ - public Collection values() - { - ArrayList values = new ArrayList(size()); - - if(root == null) - return values; - return root.values(values); - } - - /** {@inheritDoc} */ - public Set> entrySet() - { - Set> entries = new HashSet>(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 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); - } + /** + * 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(); + 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 map) + { + for(Map.Entry entry : map.entrySet()) + put(entry.getKey(),entry.getValue()); + } + + /** {@inheritDoc} */ + public Set keySet() + { + Set keys = new HashSet(size); + + if(root == null) + return keys; + return root.keySet(new StringBuilder(), keys); + } + + /** {@inheritDoc} */ + public Collection values() + { + ArrayList values = new ArrayList(size()); + + if(root == null) + return values; + return root.values(values); + } + + /** {@inheritDoc} */ + public Set> entrySet() + { + Set> entries = new HashSet>(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 of 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 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 index 17ab4bf52..5ca0b6890 100644 --- a/src/main/java/org/owasp/esapi/codecs/Hex.java +++ b/src/main/java/org/owasp/esapi/codecs/Hex.java @@ -1,6 +1,6 @@ /* * OWASP Enterprise Security API (ESAPI) - * + * * 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. @@ -13,10 +13,10 @@ 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. + * + * @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(); @@ -37,10 +37,10 @@ public static String toHex(byte[] b, boolean leading0x) { /** * Output byte representation as hexadecimal representation. * Alias for toHex() method. - * - * @param b Bytes to encode to hexadecimal representation. - * @param leading0x If true, return with leading "0x". - * @return Hexadecimal representation of specified bytes. + * + * @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); @@ -53,9 +53,9 @@ public static String encode(byte[] b, boolean leading0x) { * 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. + * + * @param hexStr Hexadecimal-encoded string, with or without leading "0x". + * @return The equivalent byte array. */ public static byte[] fromHex(String hexStr) { String hexRep = hexStr; @@ -74,9 +74,9 @@ public static byte[] fromHex(String hexStr) { /** Decode hexadecimal-encoded string and return raw byte array. * Alias for fromHex() method. - * - * @param hexStr Hexadecimal-encoded string, with or without leading "0x". - * @return The equivalent byte array. + * + * @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/JSONCodec.java b/src/main/java/org/owasp/esapi/codecs/JSONCodec.java new file mode 100644 index 000000000..524b2faac --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/JSONCodec.java @@ -0,0 +1,245 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * 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) 2022 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeffrey Walton (noloader .at. gmail.com) + * @author Kevin Wall (kevin.w.wall .at. gmail.com) + * @author Matt Seil (matt.seil .at. owasp.org) + * @created 2022 + */ +package org.owasp.esapi.codecs; + +/** + * Implementation of the Codec interface for JSON strings. + * This class performs String escaping + * on the entire string according to RFC 8259, Section 7. + * + * RFC 8259 requires conforming implementations use UTF-8. However, the ESAPI interfaces + * utilize Java strings, which are UTF-16. This may cause problems during encoding and + * decoding operations. To avoid some of the problems, convert the string to UTF-8 before + * encoding and from UTF-8 after decoding. Ultimately the ESAPI encoder interfaces will + * need modification to provide byte array arguments and return values. + * + * @see RFC 8259, + * The JavaScript Object Notation (JSON) Data Interchange Format, Section 7 + * + * @author Jeffrey Walton (noloader .at. gmail.com) + * @author Kevin Wall (kevin.w.wall .at. gmail.com) + * @author Matt Seil (matt.seil .at. owasp.org) + * @since July 31, 2022 + * @see org.owasp.esapi.Encoder + */ +public class JSONCodec extends AbstractIntegerCodec { + + + /** + * {@inheritDoc} + * + * Escape special characters in JSON strings. + * + * encodeCharacter will escape the characters Backspace (\b), Form Feed (\f), + * Carriage Return (\r), Line Feed (\n), Tab (\t), Double Quote (") and Backslash (\). + * If the character is a control character (U+0000 through U+001f), then it will be + * Unicode encoded (\u0000 through \u001f). If the character is not special or in the + * user supplied immune list, then the character is returned unescaped. If the + * character is null then an empty string is returned. + * + * WARNING: This method will silently discard an invalid code point according to + * the result of {@code Character.isValidCodePoint( int )} method. + * + * @param immune character array of whitelist characters which should not be encoded + * @param c the character to encode if not in the immune list + * @return encoded character if the character is special, and the character otherwise. + */ + public String encodeCharacter( char[] immune, Character c ) { + if ( c == null ) { + return ""; + } + + return encodeCharacter(immune, charToCodepoint( c )); + } + + /** + * {@inheritDoc} + * + * Escape special characters in JSON strings. + * + * encodeCharacter will escape the characters Backspace (\b), Form Feed (\f), + * Carriage Return (\r), Line Feed (\n), Tab (\t), Double Quote (") and Backslash (\). + * If the character is a control character (U+0000 through U+001f), then it will be + * Unicode encoded (\u0000 through \u001f). If the character is not special or in the + * user supplied immune list, then the character is returned unescaped. If the + * character is null then an empty string is returned. + * + * WARNING: This method will silently discard an invalid code point according to + * the result of {@code Character.isValidCodePoint( int )} method. + * + * @param immune character array of whitelist characters which should not be encoded + * @param codePoint the character codepoint to encode if not in the immune list + * @return encoded character if the character is special, and the character otherwise. + */ + public String encodeCharacter( char[] immune, int codePoint ) + throws IllegalArgumentException { + + // Per the docs for HTMLEntityCodec: "WARNING: This method will silently discard + // invalid code points per the call to Character.isValidCodePoint( int ) method. + + // WARNING!! Character based Codecs will only handle the byte range of 0-65535 (0x0-0xffff). + // Passing any data represented by a higher numerical value will result in a downcast thus + // destroying the original data with undefined results. + if ( Character.isValidCodePoint( codePoint ) == false ) { + // throw new IllegalArgumentException( "Invalid codepoint '" + String.format("0x%04X", codePoint) + "'." ); + return ""; + } + + if ( immune != null ) { + // More efficient than sort and binary search. If the immune array + // was presorted, then this could be O(log n). But we can't add the + // precondition now. It is too late in the game. + for ( Character ch : immune ) { + if ( charToCodepoint( ch ) == codePoint ) { + return new String(Character.toChars(codePoint)); + } + } + } + + // Per the RFC... Two-character sequence escape representations of some + // popular characters + switch ( codePoint ) { + case '\b': return "\\b"; + case '\f': return "\\f"; + case '\r': return "\\r"; + case '\n': return "\\n"; + case '\t': return "\\t"; + case '"': return "\\\""; + case '/': return "\\/"; + case '\\': return "\\\\"; + } + + // Per the RFC... All Unicode characters may be placed within the + // quotation marks, except for the characters that MUST be escaped: + // quotation mark, reverse solidus, and the control characters + // (U+0000 through U+001F). + if ( codePoint <= 0x1f ) { + + return String.format("\\u%04x", codePoint); + } + + return new String(Character.toChars(codePoint)); + } + + + /** + * {@inheritDoc} + * + * Decodes special characters in encoded JSON strings. + * + * decodeCharacter will decode the encoded character sequences for popular characters + * Backspace (\b), Form Feed (\f), Carriage Return (\r), Line Feed (\n), Tab (\t), + * Double Quote ("), Forward slash (/) and Backslash (\). The function will also decode + * six-character sequences of \u0000 - \uffff. If the character is not encoded then a + * null character is returned. + * + * @param input a character sequence to decode + * @return the decoded version of the encoded character starting at index, + * or null otherwise + * + * @throws IllegalArgumentException + * if an invalid character sequence is encountered + */ + public Integer decodeCharacter( PushbackSequence input ) + throws IllegalArgumentException { + + input.mark(); + + Integer first = input.next(), second = null; + + //Ensure both first and second characters are not null before attempting to decode. + if ( first == null || first.intValue() != '\\' ) { + input.reset(); + return null; + } + if ( (second = input.next()) == null ) { + throw new IllegalArgumentException( "Invalid JSON escape representation"); + } + + Integer decodedRef = null; + + // Per the RFC... Two-character sequence escape representations of some popular characters + switch ( second.intValue() ) { + case 'b': + decodedRef = (int)'\b'; + break; + case 'f': + decodedRef = (int)'\f'; + break; + case 'r': + decodedRef = (int)'\r'; + break; + case 'n': + decodedRef = (int)'\n'; + break; + case 't': + decodedRef = (int)'\t'; + break; + case '"': + decodedRef = (int)'\"'; + break; + case '/': + decodedRef = (int)'/'; + break; + case '\\': + decodedRef = (int)'\\'; + break; + case 'u': + // Per the RFC... All characters may be escaped as a six-character sequence: a reverse solidus, + // followed by the lowercase letter u, followed by four hexadecimal digits that encode the + // character's code point. The hexadecimal letters A through F can be uppercase or lowercase. + // So, for example, a string containing only a single reverse solidus character may be represented + // as "\u005C". + decodedRef = (convertToInt( input.next() ) << 12) + + (convertToInt( input.next() ) << 8) + + (convertToInt( input.next() ) << 4) + + (convertToInt( input.next() ) << 0); + break; + default: + input.reset(); + throw new IllegalArgumentException( "Invalid JSON two-character escape representation: " + String.format("'%c%c'", (char) first.intValue(), (char) second.intValue()) ); + } + + return decodedRef; + } + + protected int charToCodepoint( Character ch ) { + + final String s = Character.toString(ch); + assert (s.length() == 1) : "Ooops"; + + return s.codePointAt(0); + } + + protected int convertToInt( Integer hexDigit ) { + + if ( hexDigit == null ) { + throw new IllegalArgumentException( "Cannot convert from '' to int." ); + } + + final int value = Character.digit( hexDigit.intValue(), 16 ); + + if ( value < 0 || value >= 16 ) { + throw new IllegalArgumentException( "Cannot convert from hexadecimal '" + hexDigit.toString() + "' to int." ); + } + + return value; + } + +} diff --git a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java index a43cbd004..39f2d6406 100644 --- a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,7 +18,7 @@ /** * Implementation of the Codec interface for backslash encoding in JavaScript. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -27,191 +27,194 @@ public class JavaScriptCodec extends AbstractCharacterCodec { - /** - * {@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. + /** + * {@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 = super.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 + public String encodeCharacter( char[] immune, Character c ) { + + // check for immune characters + if ( containsCharacter(c, immune ) ) { + return ""+c; + } + + // check for alphanumeric characters + String hex = super.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(); - } + if ( c < 256 ) { + String pad = "00".substring(temp.length() ); + return "\\x" + pad + temp.toUpperCase(); + } - // otherwise encode with \\uHHHH + // 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( PushbackSequence 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); + } + + + /** + * {@inheritDoc} + * + * 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: + *

    +     *   \\a - special characters
    +     *   \\xHH
    +     *   \\uHHHH
    +     *   \\OOO (1, 2, or 3 digits)
    +     * 
    + */ + public Character decodeCharacter( PushbackSequence 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); + } 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(); + } 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 ); + 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 ); - } + 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); + 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 + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + input.reset(); + return null; + } + } + + // ignore the backslash and return the character + input.reset(); + return null; + } + +} diff --git a/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java index 31d6960f4..240cc2ca7 100644 --- a/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java @@ -1,15 +1,15 @@ /** - * OWASP Enterprise Security API (ESAPI) - * + * OWASP Enterprise Security API (ESAPI) + * * 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) 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 Aspect Security * @created 2007 */ @@ -20,14 +20,14 @@ import java.util.Map; /** - * + * * This class is DEPRECATED. It did not correctly handle encoding of non-BMP * unicode code points. This class is provided solely for any fatal bugs * not accounted for in the new version and will be removed entirely in - * a future release. - * + * a future release. + * * Implementation of the Codec interface for HTML entity encoding. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -35,523 +35,523 @@ */ @Deprecated public class LegacyHTMLEntityCodec extends AbstractCharacterCodec { - - 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 characterToEntityMap = mkCharacterToEntityMap(); - private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); - - /** - * {@inheritDoc} - * + + 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 characterToEntityMap = mkCharacterToEntityMap(); + private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); + + /** + * {@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 = super.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 = characterToEntityMap.get(c); - if (entityName != null) { - return "&" + entityName + ";"; - } - - // return the hex entity as suggested in the spec - return "&#x" + hex + ";"; - } - - /** - * 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()); + public String encodeCharacter( char[] immune, Character c ) { + + // check for immune characters + if ( containsCharacter(c, immune ) ) { + return ""+c; + } + + // check for alphanumeric characters + String hex = super.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 = characterToEntityMap.get(c); + if (entityName != null) { + return "&" + entityName + ";"; + } + + // return the hex entity as suggested in the spec + return "&#x" + hex + ";"; + } + + /** + * 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); + } 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 entry; - int len; - - // kludge around PushbackString.... - len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); - for(int i=0;i exactEntry = entityToCharacterTrie.getLongestMatch(possibleStringLowerCase); - if(exactEntry != null) entry = exactEntry; - } - 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 mkCharacterToEntityMap() - { - Map map = new HashMap(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 mkEntityToCharacterTrie() - { - Trie trie = new HashTrie(); - - for(Map.Entry entry : characterToEntityMap.entrySet()) - trie.put(entry.getValue(),entry.getKey()); - return Trie.Util.unmodifiable(trie); - } + } 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 entry; + int len; + + // kludge around PushbackString.... + len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); + for(int i=0;i exactEntry = entityToCharacterTrie.getLongestMatch(possibleStringLowerCase); + if(exactEntry != null) entry = exactEntry; + } + 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 mkCharacterToEntityMap() + { + Map map = new HashMap(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 mkEntityToCharacterTrie() + { + Trie trie = new HashTrie(); + + for(Map.Entry entry : characterToEntityMap.entrySet()) + trie.put(entry.getValue(),entry.getKey()); + return Trie.Util.unmodifiable(trie); + } } diff --git a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java index bdcf26a5c..bd2829521 100644 --- a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,51 +19,64 @@ /** * Codec implementation which can be used to escape string literals in MySQL. - *
    - * Implementation accepts 2 Modes as identified by the OWASP Recommended - * escaping strategies: + * This function will only protect you from SQLi in limited situations. + * To improve your chances of success, you made also need to do some + * additional canonicalization and input validation first. Before using this class, + * please be sure to read the "SECURITY WARNING" in + * {@link org.owasp.esapi.Encoder#encodeForSQL} + * before using this particular {@link org.owasp.esapi.codecs.Codec} and raising your hope of finding + * a silver bullet to kill all the SQLi werewolves. + *

    + * This implementation accepts 2 {@code org.owasp.esapi.codes.MySQLCodec.Mode}s as identified + * by the OWASP recommended escaping strategies: *

      *
    • ANSI
      * Simply encode all ' (single tick) characters with '' (two single ticks)
    • *
      *
    • Standard - * + * *
      - *   NUL (0x00) --> \0  [This is a zero, not the letter O]
      - *   BS  (0x08) --> \b
      - *   TAB (0x09) --> \t
      - *   LF  (0x0a) --> \n
      - *   CR  (0x0d) --> \r
      - *   SUB (0x1a) --> \Z
      - *   "   (0x22) --> \"
      - *   %   (0x25) --> \%
      - *   '   (0x27) --> \'
      - *   \   (0x5c) --> \\
      - *   _   (0x5f) --> \_ 
      + *   NUL (0x00) --> \0  [This is a zero, not the letter O]
      + *   BS  (0x08) --> \b
      + *   TAB (0x09) --> \t
      + *   LF  (0x0a) --> \n
      + *   CR  (0x0d) --> \r
      + *   SUB (0x1a) --> \Z
      + *   "   (0x22) --> \"
      + *   %   (0x25) --> \%
      + *   '   (0x27) --> \'
      + *   \   (0x5c) --> \\
      + *   _   (0x5f) --> \_
        *   
      - * all other non-alphanumeric characters with ASCII values less than 256 --> \c + * all other non-alphanumeric characters with ASCII values less than 256 --> \c * where 'c' is the original non-alphanumeric character. *
      - * + * *
    • - * + * *
    - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) * Aspect Security * @since June 1, 2007 - * + * * MySQL 8.0 String Literals * OWASP * SQL_Injection_Prevention_Cheat_Sheet#MySQL_Escaping + * @see + * ESAPI Security Bulletin #13 + * @deprecated This class is considered dangerous and not easily made safe and thus under strong + * consideration to be removed within 1 years time after the 2.7.0.0 release. Please + * see the referenced ESAPI Security Bulletin #13 for further details. */ +@Deprecated public class MySQLCodec extends AbstractCharacterCodec { /** * 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: + * Currently, the only supported modes are: * ANSI * STANDARD */ @@ -85,31 +98,31 @@ static Mode findByKey(int key) { /** Target MySQL Server is running in Standard MySQL (Default) mode. */ public static final int MYSQL_MODE = 0; - + /** * Target MySQL Server is running in ANSI_QUOTES Mode - * + * * @see * https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_ansi_quotes */ public static final int ANSI_MODE = 1; - - //private int mode = 0; + + //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 + + /** + * 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) - */ + */ @Deprecated - public MySQLCodec( int mode ) { - this.mode = Mode.findByKey(mode); - } + public MySQLCodec( int mode ) { + this.mode = Mode.findByKey(mode); + } /** * Instantiate the MySQL Codec with the given SQL {@link Mode}. @@ -120,182 +133,182 @@ public MySQLCodec( Mode mode ) { } - /** - * {@inheritDoc} + /** + * {@inheritDoc} */ - 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 = super.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - - - switch( mode ) { - case ANSI: return encodeCharacterANSI( c ); - case STANDARD: return encodeCharacterMySQL( c ); - default: return null; - } - } - - /** - * encodeCharacterANSI encodes for ANSI SQL. - * - * Apostrophe is encoded + 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 = super.getHexForNonAlphanumeric( ch ); + if ( hex == null ) { + return ""+ch; + } + + + switch( mode ) { + case ANSI: return encodeCharacterANSI( c ); + case STANDARD: return encodeCharacterMySQL( c ); + default: 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 "\'\'"; - + * + * @param c + * character to encode + * @return + * String encoded to standards of MySQL running in ANSI mode + */ + private String encodeCharacterANSI( Character c ) { + 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( PushbackSequence input ) { - switch( mode ) { - case ANSI: return decodeCharacterANSI( input ); - case STANDARD: return decodeCharacterMySQL( input ); - default: return null; - } - } + /** + * 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; + } - /** - * 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( PushbackSequence 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; - } + /** + * {@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( PushbackSequence input ) { + switch( mode ) { + case ANSI: return decodeCharacterANSI( input ); + case STANDARD: return decodeCharacterMySQL( input ); + default: 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( '\'' ) ); - } + /** + * 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( PushbackSequence input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } - /** - * 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( PushbackSequence 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; - } + // 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; - } - } + 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( PushbackSequence 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; + } + } } diff --git a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java index 0021a8613..2746f9137 100644 --- a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java @@ -18,73 +18,101 @@ /** - * 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 how-to-escape-single-quotes-in-strings - * + * Implementation of the {@link org.owasp.esapi.codecs.Codec} interface for Oracle DB strings. + * This function will only protect you from SQLi in limited situations. + * To improve your chances of success, you may also need to do some + * additional canonicalization and input validation first. Before using this class, + * please be sure to read the "SECURITY WARNING" in + * {@link org.owasp.esapi.Encoder#encodeForSQL} + * before using this particular {@link org.owasp.esapi.codecs.Codec} and raising your hope of finding + * a silver bullet to kill all the SQLi werewolves. + *

    + * CAUTION: This class has some known issues. During the investigation of + * CVE-2025-5878, it was discovered that since this class' inception in + * 2007, that Oracle databases also use \ (backslash) as a default escape char. + * That was fundamental in the vulnerability, since the escape character itself + * was not being escaped. We had originally planned to address this, but while + * researching the issue, we discovered that not only was there a new default + * escape character for Oracle SQL*Plus, but that developers could actually + * override the default to a character of their choosing. (For details see + * SET ESCAPE + * and + * How to Escape Characters in Oracle PL/SQL Queries.) The second instance is + * especially scary, since it illustrates how a developer can potentially can + * the default escape character as part of an ordinary SQL statement. We + * realized that there is no way we can defend against this, so it seemed + * pointless to even bother to try to quote default escape character passed in + * as input when {@code OracleCodec} is used with the {@code Encoder.encodeForSQL} + * interface. Therefore, you really should not use this, but if dead set in + * still using this leg canon, it;s on you. You have been warned. + *

    + * @see org.owasp.esapi.Encoder + * @see + * ESAPI Security Bulletin #13 * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @author Jim Manico (jim@manico.net) Manico.net * @since June 1, 2007 - * @see org.owasp.esapi.Encoder + * @see how-to-escape-single-quotes-in-strings + * @deprecated This class is considered dangerous and not easily made safe and thus under strong + * consideration to be removed within 1 years time after the 2.7.0.0 release. Please + * see the referenced ESAPI Security Bulletin #13 for further details. */ +@Deprecated public class OracleCodec extends AbstractCharacterCodec { - /** - * {@inheritDoc} - * - * Encodes ' to '' + /** + * {@inheritDoc} + * + * Encodes ' to '' * - * Encodes ' to '' + * Encodes ' to '' * * @param immune */ - public String encodeCharacter( char[] immune, Character c ) { - if ( c.charValue() == '\'' ) - return "\'\'"; + 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( PushbackSequence 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; - } + /** + * {@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( PushbackSequence 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; + } - 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( '\'' ) ); - } + // 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 index ac9e9a59e..5775055bb 100644 --- a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -22,7 +22,7 @@ /** * Implementation of the Codec interface for percent encoding (aka URL encoding). - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -30,135 +30,135 @@ */ public class PercentCodec extends AbstractCharacterCodec { - 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 UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR); + 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 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); - } - } + /** + * 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()); - } + /** + * 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 Additional characters not to encode. Note this could + /** + * Encode a character for URLs + * @param immune Additional characters not to encode. Note this could * break URL encoding as referenced in RFC 3986. You should * especially be wary of including '%' in this list of immune * characters since it is used as the "escape" character for * the hex encoding and including it may result in subsequent * and/or dangerous results when decoding. - * @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; + * @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; // check for user specified immune characters if ( immune != null && containsCharacter( c.charValue(), immune ) ) return cStr; // check for standard characters (e.g., alphanumeric, etc.) - if(UNENCODED_SET.contains(c)) - return cStr; + 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(); - } + 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( PushbackSequence input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } + /** + * {@inheritDoc} + * + * Formats all are legal both upper/lower case: + * %hh; + * + * @param input + * encoded character using percent characters (such as URL encoding) + */ + public Character decodeCharacter( PushbackSequence 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; - } + // 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; - } + // 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/PushBackSequenceImpl.java b/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java index adfa9e898..4f0847f1e 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java +++ b/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java @@ -4,7 +4,7 @@ /** * 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) Aspect Security * @since June 1, 2007 @@ -16,121 +16,121 @@ public class PushBackSequenceImpl extends AbstractPushbackSequence{ * @param input */ public PushBackSequenceImpl( String input ) { - super(input); - } - + super(input); + } + /** * * @return The next value in this Sequence, as an Integer. */ public Integer next() { - if ( pushback != null ) { - Integer save = pushback; - pushback = null; - return save; - } - if ( input == null ) return null; - if ( input.length() == 0 ) return null; - if ( index >= input.length() ) return null; - final Integer point = input.codePointAt(index); - index += Character.charCount(point); - return point; - } - + if ( pushback != null ) { + Integer save = pushback; + pushback = null; + return save; + } + if ( input == null ) return null; + if ( input.length() == 0 ) return null; + if ( index >= input.length() ) return null; + final Integer point = input.codePointAt(index); + index += Character.charCount(point); + return point; + } + /** * * @return The next value in this Sequence, as an Integer if it is a hex digit. Null otherwise. */ public Integer nextHex() { - Integer c = next(); - if ( c == null ) return null; - if ( isHexDigit( c ) ) return c; - return null; - } + Integer c = next(); + if ( c == null ) return null; + if ( isHexDigit( c ) ) return c; + return null; + } /** * * @return The next value in this Sequence, as an Integer if it is an octal digit. Null otherwise. */ public Integer nextOctal() { - Integer c = next(); - if ( c == null ) return null; - if ( isOctalDigit( c ) ) return c; - return null; - } + Integer 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 true if it is a hexidecimal digit, false otherwise. - */ - public static boolean isHexDigit( Integer c ) { - if ( c == null ) return false; - Integer ch = Integer.valueOf(c); - return (ch >= '0' && ch <= '9' ) || (ch >= 'a' && ch <= 'f' ) || (ch >= 'A' && ch <= 'F' ); - } + /** + * Returns true if the parameter character is a hexadecimal digit 0 through 9, a through f, or A through F. + * @param c + * @return true if it is a hexadecimal digit, false otherwise. + */ + public static boolean isHexDigit( Integer c ) { + if ( c == null ) return false; + Integer ch = Integer.valueOf(c); + 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 true if it is an octal digit, false otherwise. - */ - public static boolean isOctalDigit( Integer c ) { - if ( c == null ) return false; - Integer ch = Integer.valueOf(c); - return ch >= '0' && ch <= '7'; - } + /** + * Returns true if the parameter character is an octal digit 0 through 7. + * @param c + * @return true if it is an octal digit, false otherwise. + */ + public static boolean isOctalDigit( Integer c ) { + if ( c == null ) return false; + Integer ch = Integer.valueOf(c); + return ch >= '0' && ch <= '7'; + } /** * Return the next codePoint without affecting the current index. * @return the next codePoint */ public Integer peek() { - if ( pushback != null ) return pushback; - if ( input == null ) return null; - if ( input.length() == 0 ) return null; - if ( index >= input.length() ) return null; - return input.codePointAt(index); - } - + if ( pushback != null ) return pushback; + if ( input == null ) return null; + if ( input.length() == 0 ) return null; + if ( index >= input.length() ) return null; + return input.codePointAt(index); + } + /** * Test to see if the next codePoint is a particular value without affecting the current index. * @param c * @return if the next value is equal to the supplied value. */ public boolean peek( Integer c ) { - if ( pushback != null && pushback.intValue() == c ) return true; - if ( input == null ) return false; - if ( input.length() == 0 ) return false; - if ( index >= input.length() ) return false; - return input.codePointAt(index) == c; - } - + if ( pushback != null && pushback.intValue() == c ) return true; + if ( input == null ) return false; + if ( input.length() == 0 ) return false; + if ( index >= input.length() ) return false; + return input.codePointAt(index) == c; + } + /** * {@inheritDoc} */ public void mark() { - temp = pushback; - mark = index; - } + temp = pushback; + mark = index; + } /** * {@inheritDoc} */ public void reset() { - pushback = temp; - index = mark; - } - + pushback = temp; + index = mark; + } + /** * {@inheritDoc} */ public String remainder() { - String output = input.substring( index ); - if ( pushback != null ) { - output = pushback + output; - } - return output; - } + String output = input.substring( index ); + if ( pushback != null ) { + output = pushback + output; + } + return output; + } } diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java index e3aab4d07..d2edcb652 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java @@ -1,89 +1,89 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2017 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Matt Seil (mseil .at. owasp.org) * @created 2017 - * + * */ package org.owasp.esapi.codecs; public interface PushbackSequence { - /** - * - * @param c - */ - void pushback(T c); + /** + * + * @param c + */ + void pushback(T c); - /** - * Get the current index of the PushbackString. Typically used in error messages. - * @return The current index of the PushbackSequence. - */ - int index(); + /** + * Get the current index of the PushbackString. Typically used in error messages. + * @return The current index of the PushbackSequence. + */ + int index(); - /** - * Determine if this sequence has another element. - * - * @return True if there is another element in this sequence. False otherwise. - */ - boolean hasNext(); + /** + * Determine if this sequence has another element. + * + * @return True if there is another element in this sequence. False otherwise. + */ + boolean hasNext(); - /** - * Return the next element in the Sequence and increment the current index. - * @return The next element in the Sequence. - */ - T next(); + /** + * Return the next element in the Sequence and increment the current index. + * @return The next element in the Sequence. + */ + T next(); - /** - * Return the next element in the Sequence in Hex format and increment the current index. - * @return The next element in the Sequence in Hex format (if that makes sense for this Sequence's type). + /** + * Return the next element in the Sequence in Hex format and increment the current index. + * @return The next element in the Sequence in Hex format (if that makes sense for this Sequence's type). */ - T nextHex(); + T nextHex(); - /** - * Return the next element in the Sequence in Octal format and increment the current index. - * @return The next element in the Sequence in Octal format (if that makes sense for this Sequence's type). - */ - T nextOctal(); + /** + * Return the next element in the Sequence in Octal format and increment the current index. + * @return The next element in the Sequence in Octal format (if that makes sense for this Sequence's type). + */ + T nextOctal(); - /** - * Return the next element in the Sequence without affecting the current index. - * @return the next element in the Sequence. - */ - T peek(); + /** + * Return the next element in the Sequence without affecting the current index. + * @return the next element in the Sequence. + */ + T peek(); - /** - * Test to see if the next element in the Sequence matches the supplied value without affecting the current index. - * @param c The value to match against. - * @return True if the next element matches the supplied value. False otherwise. - */ - boolean peek(T c); + /** + * Test to see if the next element in the Sequence matches the supplied value without affecting the current index. + * @param c The value to match against. + * @return True if the next element matches the supplied value. False otherwise. + */ + boolean peek(T c); - /** - * Mark the location of the current index. - */ - void mark(); + /** + * Mark the location of the current index. + */ + void mark(); - /** - * Set the index back to the last marked location. - */ - void reset(); + /** + * Set the index back to the last marked location. + */ + void reset(); - /** - * Not at all sure what this method is intended to do. There - * is a line in HTMLEntityCodec that said calling this method - * is a "kludge around PushbackString..." - * @return Return the remaining portion of the sequence, with any pushback appended to the front (if any). - */ - String remainder(); + /** + * Not at all sure what this method is intended to do. There + * is a line in HTMLEntityCodec that said calling this method + * is a "kludge around PushbackString..." + * @return Return the remaining portion of the sequence, with any pushback appended to the front (if any). + */ + String remainder(); } \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackString.java b/src/main/java/org/owasp/esapi/codecs/PushbackString.java index fb133b54c..377ed829e 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackString.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackString.java @@ -1,18 +1,18 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 2017 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Matt Seil (mseil .at. owasp.org) * @updated 2017 - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @created 2007 @@ -23,7 +23,7 @@ * 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) * Aspect Security * @since June 1, 2007 @@ -31,193 +31,193 @@ */ public class PushbackString extends AbstractPushbackSequence { - /** + /** * @param input Construct a PushbackString with the specified String. - */ - public PushbackString(String input) { - super(input); - } + */ + public PushbackString(String input) { + super(input); + } - /* - * (non-Javadoc) - * - * @see org.owasp.esapi.codecs.PushbackSequence#index() - */ - public int index() { - return index; - } + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#index() + */ + public int index() { + return index; + } - /* - * (non-Javadoc) - * - * @see org.owasp.esapi.codecs.PushbackSequence#hasNext() - */ - 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; - } + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#hasNext() + */ + 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; + } - /* - * (non-Javadoc) - * - * @see org.owasp.esapi.codecs.PushbackSequence#next() - */ - 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++)); - } + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#next() + */ + 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++)); + } - /* - * (non-Javadoc) - * - * @see org.owasp.esapi.codecs.PushbackSequence#nextHex() - */ - public Character nextHex() { - Character c = next(); - if (c == null){ - return null; - } - if (isHexDigit(c)){ - return c; - } - return null; - } + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#nextHex() + */ + public Character nextHex() { + Character c = next(); + if (c == null){ + return null; + } + if (isHexDigit(c)){ + return c; + } + return null; + } - /* - * (non-Javadoc) - * - * @see org.owasp.esapi.codecs.PushbackSequence#nextOctal() - */ - public Character nextOctal() { - Character c = next(); - if (c == null){ - return null; - } - if (isOctalDigit(c)){ - return c; - } - return null; - } + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#nextOctal() + */ + 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 true if it is a hexidecimal digit, false otherwise. - */ - 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 a hexadecimal digit 0 through + * 9, a through f, or A through F. + * + * @param c + * @return true if it is a hexadecimal digit, false otherwise. + */ + 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 true if it is an octal digit, false otherwise. - */ - public static boolean isOctalDigit(Character c) { - if (c == null){ - return false; - } - char ch = c.charValue(); - return ch >= '0' && ch <= '7'; - } + /** + * Returns true if the parameter character is an octal digit 0 through 7. + * + * @param c + * @return true if it is an octal digit, false otherwise. + */ + public static boolean isOctalDigit(Character c) { + if (c == null){ + return false; + } + char ch = c.charValue(); + return ch >= '0' && ch <= '7'; + } - /* - * (non-Javadoc) - * - * @see org.owasp.esapi.codecs.PushbackSequence#peek() - */ - 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)); - } + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#peek() + */ + 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)); + } - /* - * (non-Javadoc) - * - * @see org.owasp.esapi.codecs.PushbackSequence#peek(char) - */ - public boolean peek(Character 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; - } + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#peek(char) + */ + public boolean peek(Character 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; + } - /** - * {@inheritDoc} - */ - public void mark() { - temp = pushback; - mark = index; - } + /** + * {@inheritDoc} + */ + public void mark() { + temp = pushback; + mark = index; + } - /** - * {@inheritDoc} - */ - public void reset() { - pushback = temp; - index = mark; - } + /** + * {@inheritDoc} + */ + public void reset() { + pushback = temp; + index = mark; + } - /** - * {@inheritDoc} - */ - public String remainder() { - String output = input.substring(index); - if (pushback != null) { - output = pushback + output; - } - return output; - } + /** + * {@inheritDoc} + */ + public 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 index da11268d3..fbb8c99ec 100644 --- a/src/main/java/org/owasp/esapi/codecs/Trie.java +++ b/src/main/java/org/owasp/esapi/codecs/Trie.java @@ -9,164 +9,164 @@ public interface Trie extends Map { - public Map.Entry getLongestMatch(CharSequence key); - public Map.Entry getLongestMatch(PushbackReader keyIn) throws IOException; - public int getMaxKeyLength(); - - static class TrieProxy implements Trie - { - private Trie wrapped; - - TrieProxy(Trie toWrap) - { - wrapped = toWrap; - } - - protected Trie getWrapped() - { - return wrapped; - } - - public Map.Entry getLongestMatch(CharSequence key) - { - return wrapped.getLongestMatch(key); - } - - public Map.Entry 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 t) - { - wrapped.putAll(t); - } - - public void clear() - { - wrapped.clear(); - } - - public Set keySet() - { - return wrapped.keySet(); - } - - public Collection values() - { - return wrapped.values(); - } - - public Set> entrySet() - { - return wrapped.entrySet(); - } - - public boolean equals(Object other) - { - return wrapped.equals(other); - } - - public int hashCode() - { - return wrapped.hashCode(); - } - } - - static class Unmodifiable extends TrieProxy - { - Unmodifiable(Trie 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 t) - { - throw new UnsupportedOperationException("Unmodifiable Trie"); - } - - public void clear() - { - throw new UnsupportedOperationException("Unmodifiable Trie"); - } - - public Set keySet() - { - return Collections.unmodifiableSet(super.keySet()); - } - - public Collection values() - { - return Collections.unmodifiableCollection(super.values()); - } - - public Set> entrySet() - { - return Collections.unmodifiableSet(super.entrySet()); - } - } - - public static class Util - { - private Util() - { - } - - static Trie unmodifiable(Trie toWrap) - { - return new Unmodifiable(toWrap); - } - } + public Map.Entry getLongestMatch(CharSequence key); + public Map.Entry getLongestMatch(PushbackReader keyIn) throws IOException; + public int getMaxKeyLength(); + + static class TrieProxy implements Trie + { + private Trie wrapped; + + TrieProxy(Trie toWrap) + { + wrapped = toWrap; + } + + protected Trie getWrapped() + { + return wrapped; + } + + public Map.Entry getLongestMatch(CharSequence key) + { + return wrapped.getLongestMatch(key); + } + + public Map.Entry 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 t) + { + wrapped.putAll(t); + } + + public void clear() + { + wrapped.clear(); + } + + public Set keySet() + { + return wrapped.keySet(); + } + + public Collection values() + { + return wrapped.values(); + } + + public Set> entrySet() + { + return wrapped.entrySet(); + } + + public boolean equals(Object other) + { + return wrapped.equals(other); + } + + public int hashCode() + { + return wrapped.hashCode(); + } + } + + static class Unmodifiable extends TrieProxy + { + Unmodifiable(Trie 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 t) + { + throw new UnsupportedOperationException("Unmodifiable Trie"); + } + + public void clear() + { + throw new UnsupportedOperationException("Unmodifiable Trie"); + } + + public Set keySet() + { + return Collections.unmodifiableSet(super.keySet()); + } + + public Collection values() + { + return Collections.unmodifiableCollection(super.values()); + } + + public Set> entrySet() + { + return Collections.unmodifiableSet(super.entrySet()); + } + } + + public static class Util + { + private Util() + { + } + + static Trie unmodifiable(Trie toWrap) + { + return new Unmodifiable(toWrap); + } + } } diff --git a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java index faeebad65..aa513b94e 100644 --- a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -17,8 +17,8 @@ /** - * Implementation of the Codec interface for '\' encoding from Unix command shell. - * + * Implementation of the {@code Codec} interface for '\' encoding from Unix command shell (bash lineage, not csh lineage). + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -26,57 +26,61 @@ */ public class UnixCodec extends AbstractCharacterCodec { - /** - * {@inheritDoc} - * - * Returns backslash-encoded character + /** + * {@inheritDoc} * - * @param immune + * @return the backslash-encoded character + * + * @param immune Array of characters that should not be encoded. Use with caution! All + * alphanumeric characters are "immune" by default so you needn't + * include them. */ - 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 = super.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - + 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 = super.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. - *

    - * Formats all are legal both upper/lower case: - * \x - all special characters - * - */ - public Character decodeCharacter( PushbackSequence 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; - } + } + + + /** + * {@inheritDoc} + * + *

    + * Formats all are legal both upper/lower case: + *

    +     *   \x - all special characters
    +     * 
    + * + * @return the decoded version of the character starting at index, or + * null if no decoding is possible. + */ + public Character decodeCharacter( PushbackSequence 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; - } + 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 index 122e90aad..5f768ec4f 100644 --- a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -20,7 +20,7 @@ /** * Implementation of the Codec interface for 'quote' encoding from VBScript. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -28,90 +28,90 @@ */ public class VBScriptCodec extends AbstractCharacterCodec { - /** - * Encode a String so that it can be safely used in a specific context. - * + /** + * 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 - */ + * 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 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(); + StringBuilder sb = new StringBuilder(); + boolean encoding = false; + boolean inquotes = false; + for ( int i=0; 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 + /** + * 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 = super.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - + 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 = super.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( PushbackSequence 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; - } + } + + + + /** + * 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( PushbackSequence 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 index be22640ad..d3338b1be 100644 --- a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,7 +18,7 @@ /** * Implementation of the Codec interface for '^' encoding from Windows command shell. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 @@ -26,57 +26,57 @@ */ public class WindowsCodec extends AbstractCharacterCodec { - - /** - * {@inheritDoc} - * - * Returns Windows shell encoded character (which is ^) + + /** + * {@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 = super.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - + 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 = super.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. + *

    + * Formats all are legal both upper/lower case: + * ^x - all special characters + */ + public Character decodeCharacter( PushbackSequence input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } - /** - * {@inheritDoc} - * - * 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 - */ - public Character decodeCharacter( PushbackSequence 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; - } + // if this is not an encoded character, return null + if ( first.charValue() != '^' ) { + input.reset(); + return null; + } - Character second = input.next(); - return second; - } + 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 index 418f4c5a7..c28734387 100644 --- a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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; @@ -23,11 +23,11 @@ * This differes from HTML entity encoding in that only the following * named entities are predefined: *

      - *
    • lt
    • - *
    • gt
    • - *
    • amp
    • - *
    • apos
    • - *
    • quot
    • + *
    • lt
    • + *
    • gt
    • + *
    • amp
    • + *
    • apos
    • + *
    • quot
    • *
    * However, the XML Specification 1.0 states in section 4.6 "Predefined * Entities" that these should still be declared for interoperability @@ -43,256 +43,256 @@ */ public class XMLEntityCodec extends AbstractCharacterCodec { - private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t"; - private static final Set UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR); - private static final HashTrie entityToCharacterMap; + private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t"; + private static final Set UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR); + private static final HashTrie entityToCharacterMap; - static - { // populate entitites - entityToCharacterMap = new HashTrie(); - entityToCharacterMap.put("lt", '<'); - entityToCharacterMap.put("gt", '>'); - entityToCharacterMap.put("amp", '&'); - entityToCharacterMap.put("apos", '\''); - entityToCharacterMap.put("quot", '"'); - } + static + { // populate entitites + entityToCharacterMap = new HashTrie(); + 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(); + /** + * {@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(); + // check for unencoded characters + if(UNENCODED_SET.contains(c)) + return c.toString(); - return "&#x" + Integer.toHexString(c.charValue()) + ";"; - } + 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: - *
      - *
    • &#dddd;
    • - *
    • &#xhhhh;
    • - *
    • &name;
    • - *
    - */ - public Character decodeCharacter(PushbackSequence input) - { - Character ret = null; - Character first; - Character second; + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Legal formats: + *
      + *
    • &#dddd;
    • + *
    • &#xhhhh;
    • + *
    • &name;
    • + *
    + */ + public Character decodeCharacter(PushbackSequence input) + { + Character ret = null; + Character first; + Character second; - input.mark(); - try - { - first = input.next(); - if(first == null) - return null; + input.mark(); + try + { + first = input.next(); + if(first == null) + return null; - // if this is not an encoded character, return null - if(first != '&') - 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; + // 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; - } + 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(PushbackSequence input) - { - Character first = input.peek(); + /** + * 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(PushbackSequence input) + { + Character first = input.peek(); - if(first == null) - return null; + if(first == null) + return null; - if(first=='x'||first=='X') - { - input.next(); // nuke X - return parseHex(input); - } - return parseNumber(input); - } + 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; - } + /** + * 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(PushbackSequence input) - { - StringBuilder sb = new StringBuilder(); - Character c; - while((c=input.next())!=null) - { - // end of entity? - if(c==';') - break; + /** + * 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(PushbackSequence 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; - } - } + // 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(PushbackSequence 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 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(PushbackSequence 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(PushbackSequence input) - { - StringBuilder possible = new StringBuilder(); - Map.Entry entry; - int len; + /** + * + * 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(PushbackSequence input) + { + StringBuilder possible = new StringBuilder(); + Map.Entry entry; + int len; - // kludge around PushbackString.... - len = Math.min(input.remainder().length(), entityToCharacterMap.getMaxKeyLength()+1); - for(int i=0;i replacedContentList = new ArrayList<>(); + + /** + * Constructor. + * + * @param pattern Pattern identifying content being replaced. + */ + public EncodingPatternPreservation(Pattern pattern) { + noEncodeContent = pattern; + } + + /** + * Replaces each matching instance of this instance's Pattern with an + * identifiable replacement marker.
    + * + *
    + * After the encoding process is complete, use + * {@link #restoreOriginalContent(String)} to re-insert the original data. + * + * @param input String to adjust + * @return The adjusted String + */ + public String captureAndReplaceMatches(String input) { + if (!replacedContentList.isEmpty()) { + // This may seem odd, but this will prevent programmer error that would result + // in being unable to restore a previously tokenized String. + String message = "Previously captured state is still present in instance. Call PatternContentPreservation.reset() to clear out preserved state and to reuse the reference."; + throw new IllegalStateException(message); + } + String inputCpy = input; + Matcher matcher = noEncodeContent.matcher(input); + + while (matcher.find()) { + String replaceContent = matcher.group(0); + if (replaceContent != null) { + replacedContentList.add(replaceContent); + inputCpy = inputCpy.replaceFirst(noEncodeContent.pattern(), replacementMarker); + } + } + + return inputCpy; + } + + /** + * Replaces each instance of the {@link #replacementMarker} with the original + * content, as captured by {@link #captureAndReplaceMatches(String)} + * + * @param input String to restore. + * @return String reference with all values replaced. + */ + public String restoreOriginalContent(String input) { + String result = input; + while (replacedContentList.size() > 0) { + String origValue = replacedContentList.remove(0); + result = result.replaceFirst(replacementMarker, origValue); + } + + return result; + + } + + /** + * Allows the marker used as a replacement to be altered. + * + * @param marker String replacement to use for regex matches. + */ + public void setReplacementMarker(String marker) { + if (!replacedContentList.isEmpty()) { + // This may seem odd, but this will prevent programmer error that would result + // in being unable to restore a previously tokenized String. + String message = "Previously captured state is still present in instance. Call PatternContentPreservation.reset() to clear out preserved state and to alter the marker."; + throw new IllegalStateException(message); + } + this.replacementMarker = marker; + } + + /** + * Clears any stored replacement values out of the instance. + */ + public void reset() { + replacedContentList.clear(); + } + +} diff --git a/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java index ee358f12b..2ca897707 100644 --- a/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java @@ -2,10 +2,12 @@ import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Properties; /** - * Abstrace class that supports two "levels" of priorities for ESAPI properties. + * Abstract class that supports two "levels" of priorities for ESAPI properties. * The higher level is the property file supported by an "operations" team and * the lower level is the property file intended to be supported by the * "development" team. ESAPI properties defined in the lower level properties @@ -32,7 +34,7 @@ public abstract class AbstractPrioritizedPropertyLoader implements EsapiProperty private final int priority; - public AbstractPrioritizedPropertyLoader(String filename, int priority) { + public AbstractPrioritizedPropertyLoader(String filename, int priority) throws IOException { this.priority = priority; this.filename = filename; initProperties(); @@ -40,7 +42,7 @@ public AbstractPrioritizedPropertyLoader(String filename, int priority) { /** * Get priority of this property loader. If two and more loaders can return value for the same property key, - * the one with highest priority will be chosen. + * the one with the highest priority will be chosen. * @return priority of this property loader */ public int priority() { @@ -64,13 +66,17 @@ public String name() { /** * Initializes properties object and fills it with data from configuration file. */ - private void initProperties() { + private void initProperties() throws IOException { properties = new Properties(); File file = new File(filename); if (file.exists() && file.isFile()) { - loadPropertiesFromFile(file); + if ( file.canRead() ) { + loadPropertiesFromFile(file); + } else { + throw new IOException("Can't read specificied configuration file: " + filename); + } } else { - logSpecial("Configuration file " + filename + " does not exist"); + throw new FileNotFoundException("Specified configuration file " + filename + " does not exist or not regular file"); } } diff --git a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java index 32acbf5cc..d6a1dffcd 100644 --- a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java +++ b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java @@ -3,7 +3,7 @@ import org.owasp.esapi.configuration.consts.EsapiConfiguration; import org.owasp.esapi.errors.ConfigurationException; -import java.io.FileNotFoundException; +import java.io.IOException; import static org.owasp.esapi.configuration.consts.EsapiConfigurationType.PROPERTIES; import static org.owasp.esapi.configuration.consts.EsapiConfigurationType.XML; @@ -17,22 +17,43 @@ public class EsapiPropertyLoaderFactory { public static AbstractPrioritizedPropertyLoader createPropertyLoader(EsapiConfiguration cfg) - throws ConfigurationException, FileNotFoundException { + throws ConfigurationException, IOException { String cfgPath = System.getProperty(cfg.getConfigName()); - if (cfgPath == null) { - throw new ConfigurationException("System property [" + cfg.getConfigName() + "] is not set"); + if ( cfgPath == null || cfgPath.equals("") ) { + // TODO / FIXME: + // This case was previously a warning, but it should NOT have been + // since these system properties are optional. Most people just use + // the traditional ESAPI.properties file and not these prioritized ones. + // A warning gets logged in EsapiPropertyManager if logSpecial output + // has not been discarded. + // + // Note also there were a LOT of cases in our JUnit tests where the + // file extension was empty, causing the ConfigurationException to + // be thrown with the error message: + // "Configuration storage type [] is not supported" + // I don't think that was intentional, but because prior to the + // changes for this commit, these were all ConfigurationExceptions + // and they all were just being caught and not re-thrown by + // DefaultSecurityConfigurator. I think that is an error, probably + // in the tests, but I don't have timed to chase it down right now + // because of the pending 2.2.0.0 release. + // + // Also, I made several fixes in DefaultSecurityConfiguration + // related to this clean-up where IOExceptions were being silently + // caught when they should not have been. -kwwall + return null; } String fileExtension = cfgPath.substring(cfgPath.lastIndexOf('.') + 1); - if (XML.getTypeName().equals(fileExtension)) { + if (XML.getTypeName().equalsIgnoreCase(fileExtension)) { return new XmlEsapiPropertyLoader(cfgPath, cfg.getPriority()); } - if (PROPERTIES.getTypeName().equals(fileExtension)) { + if (PROPERTIES.getTypeName().equalsIgnoreCase(fileExtension)) { return new StandardEsapiPropertyLoader(cfgPath, cfg.getPriority()); } else { - throw new ConfigurationException("Configuration storage type [" + fileExtension + "] is not " + - "supported"); + throw new ConfigurationException("The extension of given configuration path [ " + cfgPath + " ] is not supported." + + "Only .xml or .properties file extensions are supported."); } } - + } diff --git a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java index 9609ec0be..94b5e4d5a 100644 --- a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java +++ b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java @@ -4,9 +4,15 @@ import org.owasp.esapi.errors.ConfigurationException; import java.util.TreeSet; +import java.io.IOException; import static org.owasp.esapi.configuration.EsapiPropertyLoaderFactory.createPropertyLoader; +// Have dependency like this on a reference implmentation is majorly ugly, I know, but I +// don't want to refactor code and delay the 2.2.0.0 release further and this class +// is WAY too noisy. - kwwall +import static org.owasp.esapi.reference.DefaultSecurityConfiguration.logToStdout; + /** * Manager used for loading security configuration properties. Does all the logic to obtain the correct property from * correct source. Uses following system properties to find configuration files: @@ -21,7 +27,7 @@ public class EsapiPropertyManager implements EsapiPropertyLoader { protected TreeSet loaders; - public EsapiPropertyManager() { + public EsapiPropertyManager() throws IOException { initLoaders(); } @@ -34,7 +40,7 @@ public int getIntProp(String propertyName) throws ConfigurationException { try { return loader.getIntProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property not found in " + loader.name()); + logToStdout("Integer property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); @@ -49,7 +55,7 @@ public byte[] getByteArrayProp(String propertyName) throws ConfigurationExceptio try { return loader.getByteArrayProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property not found in " + loader.name()); + logToStdout("Byte array property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); @@ -64,7 +70,7 @@ public Boolean getBooleanProp(String propertyName) throws ConfigurationException try { return loader.getBooleanProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property not found in " + loader.name()); + logToStdout("Boolean property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); @@ -79,25 +85,37 @@ public String getStringProp(String propertyName) throws ConfigurationException { try { return loader.getStringProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property : " + propertyName + " not found in " + loader.name()); + logToStdout("Property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); } - private void initLoaders() { + private void initLoaders() throws IOException { loaders = new TreeSet(); try { - loaders.add(createPropertyLoader(EsapiConfiguration.OPSTEAM_ESAPI_CFG)); - } catch (Exception e) { - System.err.println(e.getMessage()); + AbstractPrioritizedPropertyLoader appl = createPropertyLoader(EsapiConfiguration.OPSTEAM_ESAPI_CFG); + if ( appl == null ) { + String msg = "WARNING: System property [" + EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName() + "] is not set"; + logToStdout(msg, null); + } else { + loaders.add( appl ); + } + } catch (IOException e) { + logToStdout("WARNING: Exception encountered while setting up ESAPI configuration manager for OPS team", e); + throw e; } try { - loaders.add(createPropertyLoader(EsapiConfiguration.DEVTEAM_ESAPI_CFG)); - } catch (Exception e) { - System.err.println(e.getMessage()); + AbstractPrioritizedPropertyLoader appl = createPropertyLoader(EsapiConfiguration.DEVTEAM_ESAPI_CFG); + if ( appl == null ) { + String msg = "WARNING: System property [" + EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName() + "] is not set"; + logToStdout(msg, null); + } else { + loaders.add( appl ); + } + } catch (IOException e) { + logToStdout("WARNING: Exception encountered while setting up ESAPI configuration manager for DEV team", e); + throw e; } } - - } diff --git a/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java index 4be9ba759..fe50e02d7 100644 --- a/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java @@ -12,7 +12,7 @@ */ public class StandardEsapiPropertyLoader extends AbstractPrioritizedPropertyLoader { - public StandardEsapiPropertyLoader(String filename, int priority) { + public StandardEsapiPropertyLoader(String filename, int priority) throws IOException { super(filename, priority); } @@ -83,7 +83,7 @@ public String getStringProp(String propertyName) throws ConfigurationException { } /** - * Methods loads configuration from .properties file. + * Methods loads configuration from .properties file. * @param file */ protected void loadPropertiesFromFile(File file) { diff --git a/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java index beddf93ba..3b3dc8ebc 100644 --- a/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java @@ -6,10 +6,12 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; @@ -26,7 +28,7 @@ */ public class XmlEsapiPropertyLoader extends AbstractPrioritizedPropertyLoader { - public XmlEsapiPropertyLoader(String filename, int priority) { + public XmlEsapiPropertyLoader(String filename, int priority) throws IOException { super(filename, priority); } @@ -80,7 +82,7 @@ public Boolean getBooleanProp(String propertyName) throws ConfigurationException return false; } else { throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + - "cannot be converted to boolean"); + "cannot be converted to boolean; legal values are: true, false, yes, no"); } } @@ -97,14 +99,16 @@ public String getStringProp(String propertyName) throws ConfigurationException { } /** - * Methods loads configuration from .xml file. + * Methods loads configuration from .xml file. * @param file + * @throws ConfigurationException if there is a problem loading the specified configuration file. */ protected void loadPropertiesFromFile(File file) throws ConfigurationException { - try { - validateAgainstXSD(new FileInputStream(file)); + try ( InputStream configFile = new FileInputStream(file); ) { + validateAgainstXSD(configFile); - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl", null); + dbFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(file); doc.getDocumentElement().normalize(); @@ -119,18 +123,20 @@ protected void loadPropertiesFromFile(File file) throws ConfigurationException { properties.put(propertyKey, propertyValue); } } - } catch (Exception e) { - throw new ConfigurationException("Configuration file : " + filename + " has invalid schema." + e.getMessage(), e); + } catch (IOException | SAXException | ParserConfigurationException e) { + logSpecial("XML config file " + filename + " doesn't exist or has invalid schema", e); + throw new ConfigurationException("Configuration file : " + filename + " has invalid schema." + + e.getMessage(), e); } } - private void validateAgainstXSD(InputStream xml) throws Exception { - InputStream xsd = getClass().getResourceAsStream("/ESAPI-properties.xsd"); - SchemaFactory factory = - SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - Schema schema = factory.newSchema(new StreamSource(xsd)); - Validator validator = schema.newValidator(); - validator.validate(new StreamSource(xml)); + private void validateAgainstXSD(InputStream xml) throws IOException, SAXException { + try ( InputStream xsd = getClass().getResourceAsStream("/ESAPI-properties.xsd"); ) { + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = factory.newSchema(new StreamSource(xsd)); + Validator validator = schema.newValidator(); + validator.validate(new StreamSource(xml)); + } } } diff --git a/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java b/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java index 7428123e1..43df2fa1c 100644 --- a/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java +++ b/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java @@ -1,7 +1,7 @@ package org.owasp.esapi.configuration.consts; /** - * Enum used for initialization of esapi configuration files. + * Enum used for initialization of esapi configuration files. * * @since 2.2 */ @@ -11,12 +11,12 @@ public enum EsapiConfiguration { DEVTEAM_ESAPI_CFG("org.owasp.esapi.devteam", 2); /** - * Key of system property pointing to path esapi to configuration file. + * Key of system property pointing to path esapi to configuration file. */ String configName; /** - * Priority of configuration (higher numer - higher priority). + * Priority of configuration (higher number - higher priority). */ int priority; diff --git a/src/main/java/org/owasp/esapi/crypto/CipherSpec.java b/src/main/java/org/owasp/esapi/crypto/CipherSpec.java index d0c291749..10b89b4d2 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherSpec.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherSpec.java @@ -1,6 +1,6 @@ /* * OWASP Enterprise Security API (ESAPI) - * + * * 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. @@ -33,312 +33,312 @@ * by application developers, but rather only by those either extending ESAPI * or in the ESAPI reference implementation. Use directly by application * code is not recommended or supported. - * + * * @author kevin.w.wall@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; + 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; + + private boolean blockSizeExplicitlySet = false; // Used for check in setIV(). - private boolean blockSizeExplicitlySet = false; // Used for check in setIV(). - - // Cipher transformation component. Format is ALG/MODE/PADDING + // 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 checks 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; + /** + * 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 checks 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; // Assertion should be okay here as these conditions are checked // elsewhere and this method is private. - 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 ) { + 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, but there are explicit checks elsewhere. - ; // 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\""); - } + ; // 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\""); + } // Assertion should also be okay here as these conditions are checked // elsewhere and this method is private. - 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) { - if ( keySize <= 0 ) { - throw new IllegalArgumentException("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 appropriate - * for the specified cipher algorithm. - * @return This current {@code CipherSpec} object. - */ - public CipherSpec setBlockSize(int blockSize) { - if ( blockSize <= 0 ) { - throw new IllegalArgumentException("blockSize must be > 0; blockSize=" + blockSize); - } - this.blockSize_ = blockSize; - blockSizeExplicitlySet = true; - 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) { - if ( ! ( requiresIV() && (iv != null && iv.length != 0) ) ) { - throw new IllegalArgumentException("Required IV cannot be null or 0 length."); - } - - // Don't store a reference, but make a copy! When an IV is provided, it generally should - // be the same length as the block size of the cipher. - if ( iv != null ) { // Allow null IV for ECB mode. - // TODO: FIXME: As per email from Jeff Walton to Kevin Wall dated 12/03/2013, - // this is not always true. E.g., for CCM, the IV length is supposed - // to be 7, 8, 9, 10, 11, 12, or 13 octets because of - // it's formatting function. + 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) { + if ( keySize <= 0 ) { + throw new IllegalArgumentException("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 appropriate + * for the specified cipher algorithm. + * @return This current {@code CipherSpec} object. + */ + public CipherSpec setBlockSize(int blockSize) { + if ( blockSize <= 0 ) { + throw new IllegalArgumentException("blockSize must be > 0; blockSize=" + blockSize); + } + this.blockSize_ = blockSize; + blockSizeExplicitlySet = true; + 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) { + if ( ! ( requiresIV() && (iv != null && iv.length != 0) ) ) { + throw new IllegalArgumentException("Required IV cannot be null or 0 length."); + } + + // Don't store a reference, but make a copy! When an IV is provided, it generally should + // be the same length as the block size of the cipher. + if ( iv != null ) { // Allow null IV for ECB mode. + // TODO: FIXME: As per email from Jeff Walton to Kevin Wall dated 12/03/2013, + // this is not always true. E.g., for CCM, the IV length is supposed + // to be 7, 8, 9, 10, 11, 12, or 13 octets because of + // it's formatting function. /*** - if ( iv.length != this.getBlockSize() && blockSizeExplicitlySet ) { - throw new IllegalArgumentException("IV must be same length as cipher block size (" + - this.getBlockSize() + " bytes)"); - } + if ( iv.length != this.getBlockSize() && blockSizeExplicitlySet ) { + throw new IllegalArgumentException("IV must be same length as cipher block size (" + + this.getBlockSize() + " bytes)"); + } ***/ - 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(); - } - + 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} */ @@ -399,19 +399,19 @@ public int hashCode() { */ 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) { + } + + /** + * 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("/"); + String[] parts = getCipherTransformation().split("/"); // Assertion should also be okay here as these conditions are checked // elsewhere and this method is private. - assert parts.length == 3 : "Invalid cipher transformation: " + getCipherTransformation(); - return parts[part]; - } + 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 index aacf488a9..185ac5e14 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -1,6 +1,6 @@ /* * OWASP Enterprise Security API (ESAPI) - * + * * 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. @@ -71,9 +71,9 @@ public final class CipherText implements Serializable { 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; @@ -94,7 +94,7 @@ private enum CipherTextFlags { 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 fromCipherSpec = EnumSet.of(CipherTextFlags.ALGNAME, CipherTextFlags.CIPHERMODE, @@ -103,7 +103,7 @@ private enum CipherTextFlags { // How much we've collected so far. We start out with having collected nothing. private EnumSet progress = EnumSet.noneOf(CipherTextFlags.class); - + // Check if versions of KeyDerivationFunction, CipherText, and // CipherTextSerializer are all the same. { @@ -118,7 +118,7 @@ private enum CipherTextFlags { } /////////////////////////// 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) @@ -128,12 +128,12 @@ 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) { @@ -143,10 +143,10 @@ public CipherText(final CipherSpec cipherSpec) { 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 @@ -162,7 +162,7 @@ public CipherText(final CipherSpec cipherSpec, byte[] cipherText) 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 @@ -194,39 +194,39 @@ public static CipherText fromPortableSerializedBytes(byte[] bytes) * The cipher transformation name is usually sufficient to be passed to * {@link javax.crypto.Cipher#getInstance(String)} to create a * Cipher 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.) @@ -234,10 +234,10 @@ public int getKeySize() { 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. @@ -245,10 +245,10 @@ public int getBlockSize() { 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. @@ -256,11 +256,11 @@ public String getCipherMode() { 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, null is returned. @@ -273,8 +273,8 @@ public byte[] getIV() { 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. @@ -282,11 +282,11 @@ public byte[] getIV() { 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() { @@ -299,11 +299,11 @@ public byte[] getRawCipherText() { 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() { @@ -320,29 +320,23 @@ public int getRawCipherTextByteLength() { * base64-encoding is performed. *

    * If there is a need to store an encrypted value, say in a database, this - * is not the method you should use unless you are using a fixed - * IV or are planning on retrieving the IV and storing it somewhere separately - * (e.g., a different database column). If you are not using a fixed IV - * (which is highly discouraged), you should normally use - * {@link #getEncodedIVCipherText()} instead. + * is not the method you should use unless you are using are storing the + * IV separately (i.e., in a separate DB column), which doesn't make a lot of sense. + * Normally, you should prefer the method {@link #getEncodedIVCipherText()} instead as + * it will return the IV prepended to the ciphertext. *

    * @see #getEncodedIVCipherText() */ public String getBase64EncodedRawCipherText() { return ESAPI.encoder().encodeForBase64(getRawCipherText(),false); } - + /** * Return the ciphertext as a base64-encoded String. 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()}. *

    - * 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. - *

    * @return The base64-encoded ciphertext or base64-encoded IV + ciphertext. * @see #getBase64EncodedRawCipherText() */ @@ -388,7 +382,7 @@ public String getEncodedIVCipherText() { * 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 @@ -430,7 +424,7 @@ public void computeAndStoreMAC(SecretKey authKey) { } // 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.) @@ -443,14 +437,14 @@ void storeSeparateMAC(byte[] macValue) { assert macComputed() : "MAC failed to compute correctly!"; } } - + /** * 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 * not 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 @@ -496,14 +490,14 @@ public boolean validateMAC(SecretKey authKey) { 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 not the same as - * returning a serialized object using Java serialization. Instead this + * 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??? @@ -515,7 +509,7 @@ public byte[] asPortableSerializedByteArray() throws EncryptionException { "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(); @@ -528,11 +522,11 @@ public byte[] asPortableSerializedByteArray() throws EncryptionException { 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. @@ -561,7 +555,7 @@ public void setCiphertext(byte[] ciphertext) throw new EncryptionException("MAC already set; cannot store new raw ciphertext", logMsg); } } - + /** * Set the IV and raw ciphertext. * @param iv The initialization vector. @@ -591,8 +585,8 @@ public void setIVandCiphertext(byte[] iv, byte[] ciphertext) // TODO: FIXME: As per email from Jeff Walton to Kevin Wall dated 12/03/2013, // this is not always true. E.g., for CCM, the IV length is supposed // to be 7, 8, 7, 8, 9, 10, 11, 12, or 13 octets because of -// it's formatting function, the restof the octets used by the -// nonce/counter. +// it's formatting function, the rest of the octets are used by the +// nonce/counter. E.g., see RFCs 4309, 8750, and related RFCs. 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()); } @@ -605,7 +599,7 @@ public void setIVandCiphertext(byte[] iv, byte[] ciphertext) throw new EncryptionException("Validation of decryption failed.", logMsg); } } - + public int getKDFVersion() { return kdfVersion_; } @@ -614,7 +608,7 @@ public void setKDFVersion(int vers) { CryptoHelper.isValidKDFVersion(vers, false, true); kdfVersion_ = vers; } - + public KeyDerivationFunction.PRF_ALGORITHMS getKDF_PRF() { return KeyDerivationFunction.convertIntToPRF(kdfPrfSelection_); } @@ -622,19 +616,19 @@ public KeyDerivationFunction.PRF_ALGORITHMS getKDF_PRF() { int kdfPRFAsInt() { return kdfPrfSelection_; } - + public void setKDF_PRF(int prfSelection) { if ( prfSelection < 0 || prfSelection > 15 ) { throw new IllegalArgumentException("kdfPrf == " + prfSelection + " must be between 0 and 15, inclusive."); } 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 @@ -653,12 +647,12 @@ private void setEncryptionTimestamp() { } encryption_timestamp_ = System.currentTimeMillis(); } - + /** * Set the encryption timestamp to the time stamp specified by the parameter. *

    * 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. @@ -672,7 +666,7 @@ void setEncryptionTimestamp(long timestamp) { } encryption_timestamp_ = timestamp; } - + /** 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. @@ -683,9 +677,9 @@ public byte[] getSeparateMAC() { } byte[] copy = new byte[ separate_mac_.length ]; System.arraycopy(separate_mac_, 0, copy, 0, separate_mac_.length); - return copy; + return copy; } - + /** * More useful {@code toString()} method. */ @@ -697,9 +691,12 @@ public String 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("KDF Version: ").append( kdfVersion_ ); + sb.append(", KDF PRF: ").append( kdfPRFAsInt() ); + 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(); } @@ -785,16 +782,26 @@ protected boolean canEqual(Object other) { } //////////////////////////////////// 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: *

          *      HMAC-SHA1(nonce, IV + plaintext)
          * 
    - * @param ciphertext The ciphertext value for which the MAC is computed. + * Note that only HMAC-SHA1 is used for the MAC calcuation. Unlike + * the PRF used for derived key generation in the {@code KeyDerivationFunction} + * class, the user cannot change the algorithm used to compute the MAC itself. + * One reason for that is that we don't want the MAC value to be excessively + * long; 128 bits is already quite long when only encrypting short strings. + * Also while the NSA reviewed this and were okay with it, Bellare, Canetti & Krawczyk + * proved in 1996 [see http://pssic.free.fr/Extra%20Reading/SEC+/SEC+/hmac-cb.pdf] that + * HMAC security doesn’t require that the underlying hash function be collision resistant, + * but only that it acts as a pseudo-random function, which SHA1 satisfies. + * @param authKey The {@code SecretKey} used with the computed HMAC-SHA1 + * to ensure authenticity. * @return The value for the MAC. - */ + */ private byte[] computeMAC(SecretKey authKey) { // These assertions are okay and leaving them as assertions rather than // changing the to conditional statements that throw should be all right @@ -833,7 +840,7 @@ private byte[] computeMAC(SecretKey authKey) { return null; } } - + /** * Return true if the MAC has already been computed (i.e., not null). */ @@ -852,7 +859,7 @@ private boolean collectedAll() { EnumSet initVector = EnumSet.of(CipherTextFlags.INITVECTOR); ctFlags = EnumSet.complementOf(initVector); } - boolean result = progress.containsAll(ctFlags); + boolean result = progress.containsAll(ctFlags); return result; } @@ -871,7 +878,7 @@ private boolean isCollected(CipherTextFlags flag) { 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} containing all the flags diff --git a/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java b/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java index 547068c11..3e155f440 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherTextSerializer.java @@ -26,8 +26,8 @@ * 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.) - * + * serialization technique, so they could exchange encrypted data.) + * * @author kevin.w.wall@gmail.com * @since 2.0 * @@ -44,9 +44,9 @@ public class CipherTextSerializer { 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. { @@ -59,14 +59,14 @@ public class CipherTextSerializer { throw new ExceptionInInitializerError("Versions of CipherTextSerializer and KeyDerivationFunction are not compatible."); } } - + public CipherTextSerializer(CipherText cipherTextObj) { if ( cipherTextObj == null ) { throw new IllegalArgumentException("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. @@ -116,7 +116,7 @@ public byte[] asSerializedByteArray() { throw new IllegalArgumentException("MAC length too large. Max is " + Short.MAX_VALUE + " bytes"); } short macLen = (short) mac.length; - + byte[] serializedObj = computeSerialization(kdfInfo, timestamp, cipherXform, @@ -129,10 +129,10 @@ public byte[] asSerializedByteArray() { macLen, mac ); - + return serializedObj; } - + /** * Return the actual {@code CipherText} object. * @return The {@code CipherText} object that we are serializing. @@ -143,7 +143,7 @@ public CipherText asCipherText() { } 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. @@ -204,10 +204,10 @@ private byte[] computeSerialization(int kdfInfo, long timestamp, 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 + // 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; @@ -230,7 +230,7 @@ private void writeString(ByteArrayOutputStream baos, String str) { "converting string to UTF8 encoding. Results suspect. Corrupt rt.jar????"); } } - + private String readString(ByteArrayInputStream bais, short sz) throws NullPointerException, IOException { @@ -242,7 +242,7 @@ private String readString(ByteArrayInputStream bais, short sz) } return new String(bytes, "UTF8"); } - + private void writeShort(ByteArrayOutputStream baos, short s) { byte[] shortAsByteArray = ByteConversionUtil.fromShort(s); if ( shortAsByteArray.length != 2 ) { @@ -250,7 +250,7 @@ private void writeShort(ByteArrayOutputStream baos, short s) { } baos.write(shortAsByteArray, 0, 2); } - + private short readShort(ByteArrayInputStream bais) throws NullPointerException, IndexOutOfBoundsException { @@ -261,12 +261,12 @@ private short readShort(ByteArrayInputStream bais) } 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 { @@ -277,7 +277,7 @@ private int readInt(ByteArrayInputStream bais) } return ByteConversionUtil.toInt(intAsByteArray); } - + private void writeLong(ByteArrayOutputStream baos, long l) { byte[] longAsByteArray = ByteConversionUtil.fromLong(l); if ( longAsByteArray.length != 8 ) { @@ -285,7 +285,7 @@ private void writeLong(ByteArrayOutputStream baos, long l) { } baos.write(longAsByteArray, 0, 8); } - + private long readLong(ByteArrayInputStream bais) throws NullPointerException, IndexOutOfBoundsException { @@ -296,7 +296,7 @@ private long readLong(ByteArrayInputStream bais) } return ByteConversionUtil.toLong(longAsByteArray); } - + /** Convert the serialized ciphertext byte array to a {@code CipherText} * object. * @param cipherTextSerializedBytes The serialized ciphertext as a byte array. @@ -337,11 +337,11 @@ private CipherText convertToCipherText(byte[] cipherTextSerializedBytes) 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 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 " + + "KDF version " + kdfVers + " from serialized ciphertext not compatibile with current KDF version of " + KeyDerivationFunction.kdfVersion); } long timestamp = readLong(bais); @@ -405,7 +405,7 @@ private CipherText convertToCipherText(byte[] cipherTextSerializedBytes) // 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 + // are two versions, and we could be decrypting data encrypted using the previous // version. ct.setKDF_PRF(kdfPrf); ct.setKDFVersion(kdfVers); @@ -424,12 +424,12 @@ private CipherText convertToCipherText(byte[] cipherTextSerializedBytes) * 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. */ @@ -437,7 +437,7 @@ private static boolean versionIsCompatible(int readKdfVers) { if ( readKdfVers <= 0 ) { throw new IllegalArgumentException("Extracted KDF version is <= 0. Must be integer >= 1."); } - + switch ( readKdfVers ) { case KeyDerivationFunction.originalVersion: // First version return true; diff --git a/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java b/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java index e42c8cf1f..53ab1c7c0 100644 --- a/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java +++ b/src/main/java/org/owasp/esapi/crypto/CryptoDiscoverer.java @@ -22,8 +22,8 @@ import java.util.regex.Pattern; public class CryptoDiscoverer { - private static String EOL = System.getProperty("line.separator", "\n"); - + private static String EOL = System.getProperty("line.separator", "\n"); + public static void main(String... args) { String provider = ".*"; String algorithm = ".*"; @@ -69,7 +69,7 @@ public static void main(String... args) { 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 \t\tSearch for particular Provider" + EOL + + "\t--provider \t\tSearch for particular Provider" + EOL + "\t--algorithm \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 index 4ba553911..2254837ce 100644 --- a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java +++ b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java @@ -1,6 +1,6 @@ /* * OWASP Enterprise Security API (ESAPI) - * + * * 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. @@ -31,165 +31,173 @@ * 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@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 - { - if ( alg == null || alg.equals("") ) { - throw new IllegalArgumentException("Algorithm must not be null or empty."); // Avoid later possibly ambiguous NPE. - } - if ( keySize <= 0 ) { - throw new IllegalArgumentException("Key size must be positive."); // Usually should be an 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); - } - } + 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 + { + if ( alg == null || alg.equals("") ) { + throw new IllegalArgumentException("Algorithm must not be null or empty."); // Avoid later possibly ambiguous NPE. + } + if ( keySize <= 0 ) { + throw new IllegalArgumentException("Key size must be positive."); // Usually should be an 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. - *

    - * CAUTION: If this algorithm for computing derived keys from the - * key derivation key is ever 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!!! - *

    - * NOTE: 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. - * Note: 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 + /** + * 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. + *

    + * CAUTION: If this algorithm for computing derived keys from the + * key derivation key is ever 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!!! + *

    + * NOTE: 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. + * Note: 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.3 so if you are using this, please change your code. - */ + * @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.) This exception is also thrown if the + * requested {@code keySize} parameter exceeds the length of the number of + * bytes provided in the {@code keyDerivationKey} parameter. + * @throws InvalidKeyException Likely indicates a coding error. Should not happen. + * @throws EncryptionException Throw for some precondition violations. + * @deprecated Use same method in {@code KeyDerivationFunction} instead. This method will be removed as of + * ESAPI release 2.3 so if you are using this, please CHANGE YOUR CODE. Note that the replacement + * is not a static method, so create your own wrapper if you wish, but this will soon disappear. + */ @Deprecated - 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. - if ( keyDerivationKey == null ) { + public static SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, String purpose) + throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException + { + // Fingers cross; maybe this will help. + logger.warning(Logger.SECURITY_AUDIT, + "Your code is using the deprecated CryptoHelper.computeDerivedKey() method which will be removed next release"); + + if ( keyDerivationKey == null ) { throw new IllegalArgumentException("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. - if ( keySize < 56 ) { + // We would choose a larger minimum key size, but we want to be + // able to accept DES for legacy encryption needs. + if ( keySize < 56 ) { throw new IllegalArgumentException("Key has size of " + keySize + ", which is less than minimum of 56-bits."); } - if ( (keySize % 8) != 0 ) { + if ( (keySize % 8) != 0 ) { throw new IllegalArgumentException("Key size (" + keySize + ") must be a even multiple of 8-bits."); } - if ( purpose == null ) { + if ( purpose == null ) { throw new IllegalArgumentException("'purpose' may not be null."); } - if ( ! ( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { - throw new IllegalArgumentException("Purpose must be \"encryption\" or \"authenticity\"."); + if ( ! ( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { + throw new IllegalArgumentException("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); - } + // 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. + // ANSWER: This is deprecated and will be removed in 2.3.0.0, so it really matter + // that much. However, Since the property Encryptor.KDF.PRF is (and has + // been) "HMacSHA256". changing this could unintentionally break code. + 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 - * and 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) - { - if ( cipherMode == null ) { + /** + * Return true if specified cipher mode is one of those specified in the + * {@code ESAPI.properties} file that supports both confidentiality + * and 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) + { + if ( cipherMode == null ) { throw new IllegalArgumentException("Cipher mode may not be null"); } - if ( cipherMode.equals("") ) { + if ( cipherMode.equals("") ) { throw new IllegalArgumentException("Cipher mode may not be empty string"); } - List combinedCipherModes = - ESAPI.securityConfiguration().getCombinedCipherModes(); - return combinedCipherModes.contains( cipherMode ); - } + List 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 @@ -201,16 +209,16 @@ public static boolean isCombinedCipherMode(String cipherMode) * @see org.owasp.esapi.SecurityConfiguration#getCombinedCipherModes() * @see org.owasp.esapi.SecurityConfiguration#getAdditionalAllowedCipherModes() */ - public static boolean isAllowedCipherMode(String cipherMode) - { - if ( isCombinedCipherMode(cipherMode) ) { - return true; - } - List extraCipherModes = - ESAPI.securityConfiguration().getAdditionalAllowedCipherModes(); - return extraCipherModes.contains( cipherMode ); - } - + public static boolean isAllowedCipherMode(String cipherMode) + { + if ( isCombinedCipherMode(cipherMode) ) { + return true; + } + List 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 @@ -239,17 +247,17 @@ public static boolean isMACRequired(CipherText ct) { // 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 + * @param sk The {@code SecretKey} used to derive a key to check * the authenticity via the MAC. * @param ct The {@code CipherText} that we are checking for a - * valid MAC. + * valid MAC. * * @return True is returned if a MAC is required and it is valid as * verified using a key derived from the specified @@ -260,7 +268,8 @@ public static boolean isCipherTextMACvalid(SecretKey sk, CipherText ct) { if ( CryptoHelper.isMACRequired( ct ) ) { try { - SecretKey authKey = CryptoHelper.computeDerivedKey( sk, ct.getKeySize(), "authenticity"); + KeyDerivationFunction kdf = new KeyDerivationFunction( ct.getKDF_PRF() ); + SecretKey authKey = kdf.computeDerivedKey(sk, ct.getKeySize(), "authenticity"); boolean validMAC = ct.validateMAC( authKey ); return validMAC; } catch (Exception ex) { @@ -273,35 +282,35 @@ public static boolean isCipherTextMACvalid(SecretKey sk, CipherText ct) } 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 - *

    -	 * 		overwrite(bytes, (byte)'*');
    -	 * 
    - * @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)}. - * + + /** + * 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 + *
    +     *         overwrite(bytes, (byte)'*');
    +     * 
    + * @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. @@ -309,89 +318,97 @@ public static void overwrite(byte[] bytes) * access of data outside array bounds. * @exception NullPointerException if either src or * dest is null. - */ - 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)}. + */ + 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 src or * dest is null. - */ - 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 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. * @deprecated Use java.security.MessageDigest#isEqual(byte[], byte[]) instead. - */ + */ @Deprecated - public static boolean arrayCompare(byte[] b1, byte[] b2) { - // Note: See GitHub issue #246 + public static boolean arrayCompare(byte[] b1, byte[] b2) { + // Note: See GitHub issue #246 and #554. + // If we make Java 8 the minimal ESAPI baseline before we remove this + // method, we can at least remove these next 6 lines. (Issue 554.) + if ( b1 == null && b2 == null ) { // Must test this first! + return true; // Prevent NPE; compatibility with Java 8 and later. + } + if ( b1 == null || b2 == null ) { + return false; // Prevent NPE; compatibility with Java 8 and later. + } return java.security.MessageDigest.isEqual(b1, b2); - } - - /** - * 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: - *
    -	 *     [20110203, 99991231]
    -	 * 
    - * @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; - } - + } + + /** + * 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: + *
    +     *     [20110203, 99991231]
    +     * 
    + * @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. + ; // 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 index b8dc983d8..c6171b75a 100644 --- a/src/main/java/org/owasp/esapi/crypto/CryptoToken.java +++ b/src/main/java/org/owasp/esapi/crypto/CryptoToken.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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 © 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; @@ -68,10 +68,10 @@ * * 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 + * 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 - * names must conform the the Java regular expression + * names must conform to the Java regular expression *
      *          [A-Za-z0-9_\.-]+
      * 
    @@ -79,7 +79,7 @@ * is, the value is not checked, so beware! When rendering these values, output * encoding may be required to prevent XSS. Use ESAPI's {@code Encoder} for that. *

    - * This entire semicolon-separated string is then encrypted via one of the + * 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 @@ -100,17 +100,17 @@ public class CryptoToken { /** Represents an anonymous user. */ public static final String ANONYMOUS_USER = ""; - + // 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_.@-]*"; - + private static Logger logger = ESAPI.getLogger("CryptoToken"); private String username = ANONYMOUS_USER; // Default user name if not set. Always lower case. @@ -121,10 +121,10 @@ public class CryptoToken { 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 - * ESAPI.properties property Encryptor.MasterKey. + * ESAPI.properties property Encryptor.MasterKey. */ public CryptoToken() { secretKey = getDefaultSecretKey( @@ -137,7 +137,7 @@ public CryptoToken() { // 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) { @@ -149,7 +149,7 @@ public CryptoToken(SecretKey skey) { expirationTime = now + DEFAULT_EXP_TIME; } - /** + /** * Create using previously encrypted token encrypted with default secret * key from ESAPI.properties. * @param token A previously encrypted token returned by one of the @@ -180,10 +180,10 @@ public CryptoToken(String token) throws EncryptionException { } } - /** + /** * 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 @@ -227,7 +227,7 @@ public CryptoToken(SecretKey skey, String token) throws EncryptionException { 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. @@ -243,10 +243,10 @@ public void setUserAccountName(String userAccountName) throws ValidationExceptio if ( userAccountName == null ) { throw new IllegalArgumentException("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() ) { @@ -265,7 +265,7 @@ public void setUserAccountName(String userAccountName) throws ValidationExceptio 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 @@ -274,7 +274,7 @@ public boolean isExpired() { 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. @@ -288,7 +288,7 @@ public void setExpiration(int intervalSecs) throws IllegalArgumentException 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 @@ -304,10 +304,10 @@ public void setExpiration(Date expirationDate) throws IllegalArgumentException 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). @@ -320,7 +320,7 @@ 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. @@ -344,7 +344,7 @@ public void setAttribute(String name, String value) throws ValidationException { } if ( value == null ) { throw new ValidationException("Null attribute VALUE encountered for attr name " + name, - "Attribute VALUE may not be null; 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. @@ -359,12 +359,12 @@ public void setAttribute(String name, String value) throws ValidationException { 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 @@ -387,7 +387,7 @@ public void addAttributes(final Map attrs) throws ValidationExce } return; } - + /** * Retrieve the attribute with the specified name. * @param name The attribute name. @@ -397,13 +397,13 @@ public void addAttributes(final Map attrs) throws ValidationExce public String getAttribute(String name) { return attributes.get(name); } - + /** * Retrieve a {@code Map} that is a clone of all the attributes. A copy * 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) */ @@ -412,7 +412,7 @@ public Map getAttributes() { // Unfortunately, this requires a cast, which requires us to supress warnings. return (Map) 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. @@ -440,7 +440,7 @@ public void clearAttributes() { * 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 ... @@ -466,12 +466,12 @@ public void clearAttributes() { 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} @@ -493,7 +493,7 @@ public String updateToken(int additionalSecs) throws EncryptionException, Valida 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. @@ -503,14 +503,14 @@ public String updateToken(int additionalSecs) throws EncryptionException, Valida // '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(); @@ -519,14 +519,14 @@ public String updateToken(int additionalSecs) throws EncryptionException, Valida /** * 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. @@ -537,7 +537,7 @@ private String createEncryptedToken(SecretKey skey) throws EncryptionException { // 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 = @@ -545,7 +545,7 @@ private String createEncryptedToken(SecretKey skey) throws EncryptionException { 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. @@ -563,7 +563,7 @@ private String getQuotedAttributes() { } 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 @@ -572,8 +572,8 @@ private String getQuotedAttributes() { /* * public String toString() { return null; } */ - - + + // Quote any special characters in value. private static String quoteAttributeValue(String value) { if ( value == null ) { @@ -592,7 +592,7 @@ private static String quoteAttributeValue(String value) { } return sb.toString(); } - + // Parse the possibly quoted value and return the unquoted value. private static String parseQuotedValue(String quotedValue) { StringBuilder sb = new StringBuilder(); @@ -608,7 +608,7 @@ private static String parseQuotedValue(String quotedValue) { } 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 @@ -682,9 +682,9 @@ private void decryptToken(SecretKey skey, String b64token) throws EncryptionExce fields.add( record ); fieldNo++; prevPos = curPos; - } + } } - + Object[] objArray = fields.toArray(); if ( fieldNo != objArray.length ) { String exm = "Programming error???: Mismatch of delimited field count."; @@ -698,7 +698,7 @@ private void decryptToken(SecretKey skey, String b64token) throws EncryptionExce 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("="); @@ -726,7 +726,7 @@ private void decryptToken(SecretKey skey, String b64token) throws EncryptionExce } return; } - + private SecretKey getDefaultSecretKey(String encryptAlgorithm) { if ( encryptAlgorithm == null ) { throw new IllegalArgumentException("Encryption algorithm cannot be null"); @@ -745,7 +745,7 @@ private SecretKey getDefaultSecretKey(String encryptAlgorithm) { // 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: diff --git a/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java b/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java index ccf8a0ba0..02f925bfb 100644 --- a/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java +++ b/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java @@ -1,6 +1,6 @@ /* * OWASP Enterprise Security API (ESAPI) - * + * * 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. @@ -21,7 +21,7 @@ import org.owasp.esapi.Logger; import org.owasp.esapi.errors.ConfigurationException; import org.owasp.esapi.errors.EncryptionException; -import org.owasp.esapi.reference.DefaultSecurityConfiguration; +import static org.owasp.esapi.PropNames.KDF_PRF_ALG; import org.owasp.esapi.util.ByteConversionUtil; /** @@ -41,67 +41,67 @@ * @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. - *

    -	 * Previous versions:	20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
    -	 *					    20130830 - Fix to issue #306 (release 2.1.0)
    -	 * 
    - * @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 - + /** + * 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. + *
    +     * Previous versions:    20110203 - Original version (ESAPI releases 2.0 & 2.0.1)
    +     *                        20130830 - Fix to issue #306 (release 2.1.0)
    +     * 
    + * @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!!! + // Note that HmacMD5 is intentionally omitted here!!! public enum PRF_ALGORITHMS { - // SHA-1, 160-bits + // SHA-1, 160-bits HmacSHA1(0, 160, "HmacSHA1"), - // SHA-2 candidates, 256-, 384-, and 512-bits + // 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 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 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; + 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 static final Logger logger = ESAPI.getLogger("KeyDerivationFunction"); - private String prfAlg_ = null; - private int version_ = kdfVersion; - private String context_ = ""; + private String prfAlg_ = null; + private int version_ = kdfVersion; + private String context_ = ""; // Check if versions of KeyDerivationFunction, CipherText, and // CipherTextSerializer are all the same. @@ -115,194 +115,195 @@ public enum PRF_ALGORITHMS { throw new ExceptionInInitializerError("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 - * ESAPI.property 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": - *
    - * "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." - *
    - * 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. - *

    - * CAUTION: If this algorithm for computing derived keys from the - * key derivation key is ever 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!!! - *

    - * NOTE: 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. - * Note: 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 must 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 checks used to be assertions prior to ESAPI 2.1.0.1 - if ( keyDerivationKey == null ) { - throw new IllegalArgumentException("Key derivation key cannot be null."); - } - // We would choose a larger minimum key size, but we want to allow + + /** + * 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 + * ESAPI.property 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 " + + 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 " + 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.3), + // 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": + *

    + * "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." + *
    + * 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. + *

    + * CAUTION: If this algorithm for computing derived keys from the + * key derivation key is ever 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!!! + *

    + * NOTE: 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. + * Note: The derived key will have the same size as this. + * @param purpose The purpose for the derived key. IMPORTANT: For the ESAPI reference implementation, + * {@code JavaEncryptor}, this must 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 ESAPI's {@code JavaEncryptor} reference implementation (but you must consistent). + * @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 requested + * Hmac algorithrm used for the PRF for key generation. + * @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.) This exception is also thrown if the + * requested {@code keySize} parameter exceeds the length of the number of + * bytes provded in the {@code keyDerivationKey} parameter. + * @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 checks used to be assertions prior to ESAPI 2.1.0.1 + if ( keyDerivationKey == null ) { + throw new IllegalArgumentException("Key derivation key cannot be null."); + } + // We would choose a larger minimum key size, but we want to allow // this KDF to be able to accept DES for legacy encryption needs. (Note that // elsewhere there are checks that disallow *encryption* for key size // less than Encryptor.EncryptionKeyLength bits, so if they want @@ -310,66 +311,88 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri // be 56 bits. But I can't think of any valid symmetric encryption // algorithm whose key size is less than 56 bits that we would ever // want to allow. - if ( keySize < 56 ) { - throw new IllegalArgumentException("Key has size of " + keySize + - ", which is less than minimum of 56-bits."); - } - if ( (keySize % 8) != 0 ) { - throw new IllegalArgumentException("Key size (" + keySize + - ") must be a even multiple of 8-bits."); - } - if ( purpose == null || "".equals(purpose) ) { - throw new IllegalArgumentException("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 + if ( keySize < 56 ) { + throw new IllegalArgumentException("Key has size of " + keySize + + ", which is less than minimum of 56-bits."); + } + if ( (keySize % 8) != 0 ) { + throw new IllegalArgumentException("Key size (" + keySize + + ") must be a even multiple of 8-bits."); + } + if ( purpose == null || "".equals(purpose) ) { + throw new IllegalArgumentException("Purpose may not be null or empty."); + } + + // + // No longer, since we no longer wish to restrict this to use only by JavaEncryptor, so + // we no longer test for this. For details, see the javadoc for '@param purpose', above. + // + /* + * + * if ( ! ( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { + * throw new IllegalArgumentException("Purpose must be \"encryption\" or \"authenticity\"."); + * } + * + */ + + int providedKeyLen = 8 * keyDerivationKey.getEncoded().length; + // assert providedKeyLen >= 56 : "Coding error? Length of keyDerivationKey < 56 bits!"; // Ugh. DES compatible. + + if ( providedKeyLen < keySize ) { + throw new EncryptionException("KeyDerivationFunction.computeDerivedKey() not intended for key stretching: " + + "provided key too short (" + providedKeyLen + " bits) to provide " + keySize + " bits.", + "Key stretching not supported: Provided key, keyDerivationKey, has insufficient entropy (" + + providedKeyLen + " bits) to generate key of requested size of " + keySize + " bits."); + } + + keySize = calcKeySize( keySize ); // Safely convert from bits to a 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 + SecretKey sk = new SecretKeySpec(keyDerivationKey.getEncoded(), prfAlg_ ); + Mac mac = null; + + try { + mac = Mac.getInstance( prfAlg_ ); + mac.init(sk); + } catch( InvalidKeyException ex ) { + logger.error(Logger.SECURITY_FAILURE, + "Created " + prfAlg_ + " Mac but SecretKey sk has alg " + + sk.getAlgorithm(), ex); + throw ex; + } + + // Repeatedly call of PRF Hmac 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 @@ -385,78 +408,87 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri // 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. - + // + 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. + // Of course, in reality, trying to stomp these bits out is probably not + // realistic because the JIT is likely toing to be smart enough to + // optimze this loop away. We probably could try to outsmart it, by + // (say) writing out the overwritten bits to /dev/null, but then even + // then we'd still probably have to overwrite with random bits rather + // than all null chars. How much is enough? Who knows? But it does point + // to a serious limitation in Java and many other languages that one + // cannot arbitrarily disable the optimizer either at compile time or + // run time because of security reasons. Sigh. At least we've tried. + 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); - } - + 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 @@ -489,11 +521,11 @@ private static int calcKeySize(int ks) { * * @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()); - } - } + 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 index d70dd98f5..4866b8048 100644 --- a/src/main/java/org/owasp/esapi/crypto/PlainText.java +++ b/src/main/java/org/owasp/esapi/crypto/PlainText.java @@ -16,7 +16,7 @@ * 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 + * in order 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. @@ -29,74 +29,74 @@ */ 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 { - 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. - if ( b == null ) { + 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 { + 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. + if ( b == null ) { throw new IllegalArgumentException("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) { + 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; @@ -108,42 +108,42 @@ public byte[] asBytes() { ); } 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. - } - + } + + /** + * 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 diff --git a/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java b/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java index 6639f1357..a5cbd708c 100644 --- a/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java +++ b/src/main/java/org/owasp/esapi/crypto/SecurityProviderLoader.java @@ -8,7 +8,7 @@ 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. @@ -43,14 +43,14 @@ public class SecurityProviderLoader { // 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. + // 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"); @@ -79,9 +79,9 @@ public class SecurityProviderLoader { 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. + * either by some generic name or by the class name. *

    * The following generic JCE provider names are built-in: *

      @@ -209,13 +209,13 @@ public static int insertProviderAt(String algProvider, int pos) // 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. + // 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. + // As per NSA suggestion. logger.always(Logger.SECURITY_AUDIT, "Successfully loaded preferred JCE provider " + algProvider + " at position " + pos); @@ -223,12 +223,12 @@ public static int insertProviderAt(String algProvider, int 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); + // 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, @@ -236,8 +236,8 @@ public static int insertProviderAt(String algProvider, int pos) // // 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. + // 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 " + @@ -245,7 +245,7 @@ public static int insertProviderAt(String algProvider, int pos) "; exception msg: " + ex.toString()); } } - + /** * Load the preferred JCE provider for ESAPI based on the ESAPI.properties * property {@code Encryptor.PreferredJCEProvider}. If this property is null @@ -272,7 +272,7 @@ public static int loadESAPIPreferredJCEProvider() throws NoSuchProviderException 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. + // Always log, per NSA suggestion. logger.always(Logger.SECURITY_AUDIT, "No Encryptor.PreferredJCEProvider specified."); return -1; // Unchanged; it is, whatever it is. } else { @@ -280,8 +280,8 @@ public static int loadESAPIPreferredJCEProvider() throws NoSuchProviderException } } 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. + 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/errors/AccessControlException.java b/src/main/java/org/owasp/esapi/errors/AccessControlException.java index 96eaf80c5..fbc537f3d 100644 --- a/src/main/java/org/owasp/esapi/errors/AccessControlException.java +++ b/src/main/java/org/owasp/esapi/errors/AccessControlException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,40 +18,40 @@ /** * An AccessControlException should be thrown when a user attempts to access a * resource that they are not authorized for. - * + * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ public class AccessControlException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new access control exception. - */ - protected AccessControlException() { - // hidden - } + /** + * Instantiates a new access control exception. + */ + protected AccessControlException() { + // hidden + } - /** - * Creates a new instance of {@code AccessControlException}. + /** + * Creates a new instance of {@code AccessControlException}. * * @param userMessage * @param logMessage */ - public AccessControlException(String userMessage, String logMessage) { - super(userMessage, 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); - } + /** + * 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 index c81ff844b..a08b02c88 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,41 +19,41 @@ * 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@aspectsecurity.com) */ public class AuthenticationAccountsException extends AuthenticationException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationAccountsException() { - // hidden - } + /** + * 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); - } + /** + * 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); - } + /** + * 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 index e48187739..ad3d8df6b 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,40 +19,40 @@ * 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@aspectsecurity.com) */ public class AuthenticationCredentialsException extends AuthenticationException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationCredentialsException() { - // hidden - } + /** + * 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); - } + /** + * 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); - } + /** + * 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 index c8198f57f..55abbc370 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,24 +19,24 @@ * 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@aspectsecurity.com) */ public class AuthenticationException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationException() { - // hidden - } + /** + * 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 */ @@ -46,7 +46,7 @@ public AuthenticationException(String userMessage, String logMessage) { /** * Instantiates a new authentication exception. - * + * * @param userMessage the message displayed to the user * @param logMessage the message logged * @param cause the root cause diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java index 1deac4b72..4010d79b1 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,40 +18,40 @@ /** * 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@aspectsecurity.com) */ public class AuthenticationHostException extends AuthenticationException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationHostException() { - // hidden - } + /** + * 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); - } + /** + * 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); - } + /** + * 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 index d389591bc..0f7a3d855 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,40 +19,40 @@ * 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@aspectsecurity.com) */ public class AuthenticationLoginException extends AuthenticationException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationLoginException() { - // hidden - } + /** + * 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); - } + /** + * 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); - } + /** + * 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 index 3ec04b677..98099155d 100644 --- a/src/main/java/org/owasp/esapi/errors/AvailabilityException.java +++ b/src/main/java/org/owasp/esapi/errors/AvailabilityException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,24 +19,24 @@ * 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@aspectsecurity.com) */ public class AvailabilityException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new availability exception. - */ - protected AvailabilityException() { - // hidden - } + /** + * 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 */ @@ -46,7 +46,7 @@ public AvailabilityException(String userMessage, String logMessage) { /** * Instantiates a new AvailabilityException. - * + * * @param userMessage the message displayed to the user * @param logMessage the message logged * @param cause the cause diff --git a/src/main/java/org/owasp/esapi/errors/CertificateException.java b/src/main/java/org/owasp/esapi/errors/CertificateException.java index 2f84b2c01..561d41179 100644 --- a/src/main/java/org/owasp/esapi/errors/CertificateException.java +++ b/src/main/java/org/owasp/esapi/errors/CertificateException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,24 +18,24 @@ /** * A CertificateException should be thrown for any problems that arise during * processing of digital certificates. - * + * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ public class CertificateException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new certificate exception. - */ - protected CertificateException() { - // hidden - } + /** + * 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 */ @@ -45,7 +45,7 @@ public CertificateException(String userMessage, String logMessage) { /** * Instantiates a new CertificateException. - * + * * @param userMessage the message displayed to the user * @param logMessage the message logged * @param cause the cause diff --git a/src/main/java/org/owasp/esapi/errors/ConfigurationException.java b/src/main/java/org/owasp/esapi/errors/ConfigurationException.java index 4e6a38130..17d6a6b3e 100644 --- a/src/main/java/org/owasp/esapi/errors/ConfigurationException.java +++ b/src/main/java/org/owasp/esapi/errors/ConfigurationException.java @@ -14,21 +14,21 @@ */ public class ConfigurationException extends RuntimeException { - protected static final long serialVersionUID = 1L; + protected static final long serialVersionUID = 1L; - public ConfigurationException(Exception e) { - super(e); - } + 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); - } + 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 index f2b269445..cee3abf2b 100644 --- a/src/main/java/org/owasp/esapi/errors/EncodingException.java +++ b/src/main/java/org/owasp/esapi/errors/EncodingException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,28 +18,28 @@ /** * An EncodingException should be thrown for any problems that occur when * encoding or decoding data. - * + * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ public class EncodingException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new service exception. - */ - protected EncodingException() { - // hidden - } + /** + * 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 + * the message logged */ public EncodingException(String userMessage, String logMessage) { super(userMessage, logMessage); @@ -47,11 +47,11 @@ public EncodingException(String userMessage, String logMessage) { /** * Instantiates a new EncodingException. - * + * * @param userMessage * the message displayed to the user * @param logMessage - * the message logged + * the message logged * @param cause * the cause */ diff --git a/src/main/java/org/owasp/esapi/errors/EncryptionException.java b/src/main/java/org/owasp/esapi/errors/EncryptionException.java index 163a1d228..895d34eb7 100644 --- a/src/main/java/org/owasp/esapi/errors/EncryptionException.java +++ b/src/main/java/org/owasp/esapi/errors/EncryptionException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,28 +18,28 @@ /** * An EncryptionException should be thrown for any problems related to * encryption, hashing, or digital signatures. - * + * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ public class EncryptionException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new EncryptionException. - */ - protected EncryptionException() { - // hidden - } + /** + * 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 + * the message logged */ public EncryptionException(String userMessage, String logMessage) { super(userMessage, logMessage); @@ -47,11 +47,11 @@ public EncryptionException(String userMessage, String logMessage) { /** * Instantiates a new EncryptionException. - * + * * @param userMessage * the message displayed to the user * @param logMessage - * the message logged + * the message logged * @param cause * the cause */ diff --git a/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java b/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java index 466ef5cb1..cdc484324 100644 --- a/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java +++ b/src/main/java/org/owasp/esapi/errors/EncryptionRuntimeException.java @@ -1,12 +1,12 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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 - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * @@ -18,30 +18,30 @@ /** * An EncryptionRuntimeException should be thrown for any problems related to * encryption, hashing, or digital signatures. - * + * * @author August Detlefsen (augustd at codemagi dot com) * CodeMagi, Inc. * @since October 8, 2010 */ public class EncryptionRuntimeException extends EnterpriseSecurityRuntimeException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new EncryptionException. - */ - protected EncryptionRuntimeException() { - // hidden - } + /** + * 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 + * the message logged */ public EncryptionRuntimeException(String userMessage, String logMessage) { super(userMessage, logMessage); @@ -49,11 +49,11 @@ public EncryptionRuntimeException(String userMessage, String logMessage) { /** * Instantiates a new EncryptionException. - * + * * @param userMessage * the message displayed to the user * @param logMessage - * the message logged + * the message logged * @param cause * the cause */ diff --git a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java index da03ef29f..6b150bb42 100644 --- a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java +++ b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,12 +18,7 @@ import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; -// At some point, all these property names will be moved to a new class named -// org.owasp.esapi.PropNames -// but until then, while this is an ugly kludge, we are importing it via a -// reference implementation class until we have a chance to clean it up. -// (Note: kwwall's Bitbucket code already has that class.) -import static org.owasp.esapi.reference.DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION; +import static org.owasp.esapi.PropNames.DISABLE_INTRUSION_DETECTION; /** @@ -95,47 +90,47 @@ protected EnterpriseSecurityException(String userMessage, Throwable cause) * 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 userMessage + * the message displayed to the user * @param logMessage - * the message logged + * the message logged */ public EnterpriseSecurityException(String userMessage, String logMessage) { - super(userMessage); + super(userMessage); this.logMessage = logMessage; if (!ESAPI.securityConfiguration().getBooleanProp( DISABLE_INTRUSION_DETECTION )) { - ESAPI.intrusionDetector().addException(this); + 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 + * the message displayed to the user * @param logMessage - * the message logged + * the message logged * @param cause the cause */ public EnterpriseSecurityException(String userMessage, String logMessage, Throwable cause) { super(userMessage, cause); this.logMessage = logMessage; if (!ESAPI.securityConfiguration().getBooleanProp( DISABLE_INTRUSION_DETECTION )) { - ESAPI.intrusionDetector().addException(this); + 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() { @@ -146,7 +141,7 @@ public String getUserMessage() { * 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. */ diff --git a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java index 6dab2a54c..3b511c321 100644 --- a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java +++ b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java @@ -1,12 +1,12 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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 - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * @@ -32,7 +32,7 @@ * 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) * CodeMagi, Inc. * @since October 8, 2010 @@ -91,47 +91,47 @@ protected EnterpriseSecurityRuntimeException(String userMessage, * 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 userMessage + * the message displayed to the user * @param logMessage - * the message logged + * the message logged */ public EnterpriseSecurityRuntimeException(String userMessage, String logMessage) { - super(userMessage); + super(userMessage); this.logMessage = logMessage; if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) { - ESAPI.intrusionDetector().addException(this); + 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 + * the message displayed to the user * @param logMessage - * the message logged + * 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); + 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() { @@ -142,7 +142,7 @@ public String getUserMessage() { * 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. */ diff --git a/src/main/java/org/owasp/esapi/errors/ExecutorException.java b/src/main/java/org/owasp/esapi/errors/ExecutorException.java index a27c74fa3..9adda9296 100644 --- a/src/main/java/org/owasp/esapi/errors/ExecutorException.java +++ b/src/main/java/org/owasp/esapi/errors/ExecutorException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -18,28 +18,28 @@ /** * An ExecutorException should be thrown for any problems that arise during the * execution of a system executable. - * + * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ public class ExecutorException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new ExecutorException. - */ - protected ExecutorException() { - // hidden - } + /** + * 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 + * the message logged */ public ExecutorException(String userMessage, String logMessage) { super(userMessage, logMessage); @@ -47,11 +47,11 @@ public ExecutorException(String userMessage, String logMessage) { /** * Instantiates a new ExecutorException. - * + * * @param userMessage * the message to display to users * @param logMessage - * the message logged + * the message logged * @param cause * the cause */ diff --git a/src/main/java/org/owasp/esapi/errors/IntegrityException.java b/src/main/java/org/owasp/esapi/errors/IntegrityException.java index 59d97a189..92a546a2a 100644 --- a/src/main/java/org/owasp/esapi/errors/IntegrityException.java +++ b/src/main/java/org/owasp/esapi/errors/IntegrityException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,28 +19,28 @@ * 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@aspectsecurity.com) */ public class IntegrityException extends EnterpriseSecurityException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new availability exception. - */ - protected IntegrityException() { - // hidden - } + /** + * 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 + * the message logged */ public IntegrityException(String userMessage, String logMessage) { super(userMessage, logMessage); @@ -48,11 +48,11 @@ public IntegrityException(String userMessage, String logMessage) { /** * Instantiates a new IntegrityException. - * + * * @param userMessage * the message to display to users * @param logMessage - * the message logged + * the message logged * @param cause * the cause */ diff --git a/src/main/java/org/owasp/esapi/errors/IntrusionException.java b/src/main/java/org/owasp/esapi/errors/IntrusionException.java index b5253ea28..72261d9e1 100644 --- a/src/main/java/org/owasp/esapi/errors/IntrusionException.java +++ b/src/main/java/org/owasp/esapi/errors/IntrusionException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -25,7 +25,7 @@ *

      * 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@aspectsecurity.com) */ public class IntrusionException extends EnterpriseSecurityRuntimeException { @@ -43,11 +43,11 @@ public class IntrusionException extends EnterpriseSecurityRuntimeException { /** * Creates a new instance of IntrusionException. - * + * * @param userMessage * the message to display to users * @param logMessage - * the message logged + * the message logged */ public IntrusionException(String userMessage, String logMessage) { super(userMessage); @@ -57,13 +57,13 @@ public IntrusionException(String userMessage, String logMessage) { /** * Instantiates a new intrusion exception. - * + * * @param userMessage * the message to display to users * @param logMessage - * the message logged - * @param cause - * the cause + * the message logged + * @param cause + * the cause */ public IntrusionException(String userMessage, String logMessage, Throwable cause) { super(userMessage, cause); @@ -73,7 +73,7 @@ public IntrusionException(String userMessage, String logMessage, Throwable 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() { @@ -82,7 +82,7 @@ public String getUserMessage() { /** * 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() { diff --git a/src/main/java/org/owasp/esapi/errors/NotConfiguredByDefaultException.java b/src/main/java/org/owasp/esapi/errors/NotConfiguredByDefaultException.java new file mode 100644 index 000000000..58a0cbc98 --- /dev/null +++ b/src/main/java/org/owasp/esapi/errors/NotConfiguredByDefaultException.java @@ -0,0 +1,34 @@ +package org.owasp.esapi.errors; + +/** + * A {@code NotConfiguredByDefaultException} should be thrown when a method that + * is disabled by default is invoked. + *

      + + * See the ESAPI properties "ESAPI.dangerouslyAllowUnsafeMethods.methodNames" + * and "ESAPI.dangerouslyAllowUnsafeMethods.justification" in the + * ESAPI.properties file for additional details. + *

      + */ +public class NotConfiguredByDefaultException extends ConfigurationException { + + protected static final long serialVersionUID = 1L; + private static final String defaultMsg = "Unknown unsafe ESAPI method invoked without being explicitly allowed. " + + "Check exception stack trace for method name."; + + public NotConfiguredByDefaultException(Exception e) { + super(e); + } + + public NotConfiguredByDefaultException(String s) { + super( (s == null || s.trim().isEmpty()) ? defaultMsg : s); + } + + public NotConfiguredByDefaultException(String s, Throwable cause) { + super( (s == null || s.trim().isEmpty()) ? defaultMsg : s, cause); + } + + public NotConfiguredByDefaultException(Throwable cause) { + super(defaultMsg, cause); + } +} diff --git a/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java b/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java index 4e4a1d48b..5274da5d8 100644 --- a/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java +++ b/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -20,38 +20,38 @@ */ public class ValidationAvailabilityException extends ValidationException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new validation exception. - */ - protected ValidationAvailabilityException() { - // hidden - } + /** + * 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 + * the message logged */ - public ValidationAvailabilityException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } + 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 + * the message logged * @param cause - * the cause - */ - public ValidationAvailabilityException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, 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 index a33562f96..c185574fb 100644 --- a/src/main/java/org/owasp/esapi/errors/ValidationException.java +++ b/src/main/java/org/owasp/esapi/errors/ValidationException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -19,30 +19,30 @@ * 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@aspectsecurity.com) */ public class ValidationException extends EnterpriseSecurityException { protected static final long serialVersionUID = 1L; - /** The UI reference that caused this ValidationException */ - private String context; + /** The UI reference that caused this ValidationException */ + private String context; - /** - * Instantiates a new validation exception. - */ - protected ValidationException() { - // hidden - } + /** + * 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 + * the message logged */ public ValidationException(String userMessage, String logMessage) { super(userMessage, logMessage); @@ -50,25 +50,25 @@ public ValidationException(String userMessage, String logMessage) { /** * Instantiates a new ValidationException. - * + * * @param userMessage * the message to display to users * @param logMessage - * the message logged + * 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 + * the message logged * @param context * the source that caused this exception */ @@ -76,14 +76,14 @@ 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 + * the message logged * @param cause * the cause * @param context @@ -91,25 +91,25 @@ public ValidationException(String userMessage, String logMessage, String context */ public ValidationException(String userMessage, String logMessage, Throwable cause, String context) { super(userMessage, logMessage, cause); - setContext(context); + 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; } - - /** - * 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; - } + /** + * 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 index 4a04de2eb..825f15430 100644 --- a/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java +++ b/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created 2007 */ @@ -20,40 +20,40 @@ */ public class ValidationUploadException extends ValidationException { - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; - /** - * Instantiates a new validation exception. - */ - protected ValidationUploadException() { - // hidden - } + /** + * 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 + * the message logged */ - public ValidationUploadException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } + 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 + * the message logged * @param cause - * the cause + * the cause */ - public ValidationUploadException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } + public ValidationUploadException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } } diff --git a/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java index c57b836b5..e10a4e163 100644 --- a/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java +++ b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * 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) 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 Aspect Security * @created February 6, 2009 */ @@ -26,83 +26,89 @@ import javax.servlet.http.HttpServletResponse; /** - * The {@code ClickjackFilter} is discussed at - * @link http://www.owasp.org/index.php/ClickjackFilter_for_Java_EE + * The {@code ClickjackFilter} is configured as follows: *
      - *     
      - *            ClickjackFilterDeny
      - *            org.owasp.filters.ClickjackFilter
      - *            
      - *                mode
      - *                 DENY
      - *             
      - *         
      - *         
      - *         
      - *             ClickjackFilterSameOrigin
      - *             org.owasp.filters.ClickjackFilter
      - *             
      - *                 mode
      - *                 SAMEORIGIN
      - *             
      - *         
      - *        
      - *        
      - *         
      - *            ClickjackFilterDeny
      - *            /*
      - *        
      - *         
      - *         ",
      +        "-->",
      +            // Note:  is a deprecated tag, but some browsers may still support.
      +        "<xmp/><!---->"
      +      };
      +  
      +      Validator instance = ESAPI.validator();
      +      int testCase = 0;
      +      for (String payload : payloads) {
      +          String testId = "";
      +          try {
      +              testId = "testAntiSamy_CVE_2023_43643- " + testCase++;
      +              String output = instance.getValidSafeHTML(testId, payload, 250, false);
      +              assertThat(testId + ": payload not cleansed from JS mxss()...", output, not(containsString("mxss")));
      +          } catch( ValidationException vex ) {
      +              fail(testId + " caused ValidationException: " + vex);
      +          }
      +      }
      +    }
      +    ////////////////////////////////////////
      +
      +    // This test has been significantly changed because as on AntiSamy 1.7.4
      +    // (first used with ESAPI 2.5.3.0) has changed the results of
      +    // Validator.getValidSafeHTMLfor this output. Prior to AntiSamy 1.7.4, the
      +    // expected output was:
      +    //      b</style><a href=javascript:alert(1)>test
      +    // but with AntiSamy 1.7.4, it now is:
      +    //      b<![cdata[test
      +    // which is still safe, but as a result, this test had to change.
      +    //
      +    // See AntiSamy GitHub issue #380 (https://github.com/nahsra/antisamy/issues/389) for more details.
      +    //
      +    // Also, this test, which originally used Validator.isValidSafeHTML(), has been
      +    // changed to use Validator.getValidSafeHTML() instead because Validator.isValidSafeHTML()
      +    // has been removed as of ESAPI 2.6.0.0. See GitHub Security Advisory
      +    // https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-r68h-jhhj-9jvm
      +    // and the referenced ESAPI Security Bulletin mentioned therein.
      +    @Test
      +    public void testAntiSamyRegressionCDATAWithJavascriptURL() throws Exception {
      +    	System.out.println("testAntiSamyRegressionCDATAWithJavascriptURL");
      +        Validator instance = ESAPI.validator();
      +        ValidationErrorList errors = new ValidationErrorList();
      +        String input = "test";
      +        // String expected = "b<script>alert(1)</script>";      // Before AntiSamy 1.7.4
      +        String expected = "b<![cdata[test";                              // AntiSamy 1.7.4 (and later?)
      +        String output = instance.getValidSafeHTML("javascript Link", input, 250, false, errors);
      +        assertEquals(expected, output);
      +        assertTrue(errors.size() == 0);
      +    }
      +
      +    // This test has been significantly changed because as on AntiSamy 1.7.4
      +    // (first used with ESAPI 2.5.3.0) has changed the results of
      +    // Validator.getValidSafeHTMLfor this output. Prior to AntiSamy 1.7.4, the
      +    // expected output was:
      +    //      W<script>alert(1)</script>
      +    // but with AntiSamy 1.7.4, it now is:
      +    //      W<xmp<script>alert(1)</script>
      +    // which is still safe, but as a result, this test had to change.
      +    //
      +    // See AntiSamy GitHub issue #380 (https://github.com/nahsra/antisamy/issues/389) for more details.
      +    //
      +    // The output has changed again as of AntiSamy 1.7.5. The expected output is now:
      +    //      Walert(1)
      +    // See AntiSamy Release notes for 1.7.5 (https://github.com/nahsra/antisamy/releases/tag/v1.7.5)
      +    //
      +    // Also, this test, which originally used Validator.isValidSafeHTML(), has been
      +    // changed to use Validator.getValidSafeHTML() instead because Validator.isValidSafeHTML()
      +    // has been removed as of ESAPI 2.6.0.0. See GitHub Security Advisory
      +    // https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-r68h-jhhj-9jvm
      +    // and the referenced ESAPI Security Bulletin mentioned therein.
      +    @Test
      +    public void testScriptTagAfterStyleClosing() throws Exception {
      +    	System.out.println("testScriptTagAfterStyleClosing");
      +
      +        Validator instance = ESAPI.validator();
      +        ValidationErrorList errors = new ValidationErrorList();
      +        String input = "Walert(1)";
      +        // String expected = "W<script>alert(1)</script>";        // Before AntiSamy 1.7.4
      +        // String expected = "W<xmp<script>alert(1)</script>";    // AntiSamy 1.7.4
      +        String expected = "Walert(1)";                                      // AntiSamy 1.7.5 (and later?)
      +        String output = instance.getValidSafeHTML("escaping style tag attack with script tag", input, 250, false, errors);
      +        assertEquals(expected, output);
      +        assertTrue(errors.size() == 0);
      +    }
      +
      +    // This test has been significantly changed because as on AntiSamy 1.7.4
      +    // (first used with ESAPI 2.5.3.0) has changed the results of
      +    // Validator.getValidSafeHTMLfor this output. Prior to AntiSamy 1.7.4, the
      +    // expected output was:
      +    //      k<input/onfocus=alert(1)>
      +    // but with AntiSamy 1.7.4, it now is:
      +    //      k<input<</>input/onfocus=alert(1)>
      +    // which is still safe, but as a result, this test had to change.
      +    //
      +    // See AntiSamy GitHub issue #380 (https://github.com/nahsra/antisamy/issues/389) for more details.
      +    //
      +    // The output has changed again as of AntiSamy 1.7.5. The expected output is now:
      +    //      kinput/onfocus=alert(1)>
      +    // See AntiSamy Release notes for 1.7.5 (https://github.com/nahsra/antisamy/releases/tag/v1.7.5)
      +    //
      +    // Also, this test, which originally used Validator.isValidSafeHTML(), has been
      +    // changed to use Validator.getValidSafeHTML() instead because Validator.isValidSafeHTML()
      +    // has been removed as of ESAPI 2.6.0.0. See GitHub Security Advisory
      +    // https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-r68h-jhhj-9jvm
      +    // and the referenced ESAPI Security Bulletin mentioned therein.
      +    @Test
      +    public void testOnfocusAfterStyleClosing() throws Exception {
      +    	System.out.println("testOnfocusAfterStyleClosing");
      +        Validator instance = ESAPI.validator();
      +        ValidationErrorList errors = new ValidationErrorList();
      +        String input = "kinput/onfocus=alert(1)>";
      +
      +        // String expected = "k<input/onfocus=alert(1)>";    // Before AntiSamy 1.7.4
      +        // String expected = "k<input<</>input/onfocus=alert(1)>";    // AntiSamy 1.7.4
      +        String expected = "kinput/onfocus=alert(1)>";    // AntiSamy 1.7.5 (and later?)
      +        String output = instance.getValidSafeHTML("escaping style tag attack with onfocus attribute", input, 250, false, errors);
      +        assertEquals(expected, output);
      +        assertTrue(errors.size() == 0);
      +    }
      +
      +    // This test was a DoS issue (CVE-2022-28366) within a transitive dependency (Neko-HtmlUnit) that AntiSamy uses.
      +    // It is fixed only in Neko-HtmlUnit 2.27 and later, but all those releases are only available for Java 8 and later.
      +    //
      +    // When the input here is called with AntiSamy.scan().getCleanHtml(), AntiSamy throws a ScanException.
      +    // (For details, see the AntiSamy JUnit test case "testMalformedPIScan" in
      +    // https://github.com/nahsra/antisamy/blob/main/src/test/java/org/owasp/validator/html/test/AntiSamyTest.java.)
      +    //
      +    @Test
      +    public void testNekoDOSWithAnHTMLComment() throws Exception {
      +        System.out.println("testNekoDOSWithAnHTMLComment");
      +
      +        Validator instance = ESAPI.validator();
      +        ValidationErrorList errors = new ValidationErrorList();
      +        String input = "\r\n \r\n";
      +        String expectEmpty = "";
      +        String output = instance.getValidSafeHTML("escaping IE conditional comment", input, 250, true);
      +        assertEquals(expectEmpty, output);  // Expect AntiSamy to return empty string here.
      +    }
      +}
      diff --git a/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java
      new file mode 100644
      index 000000000..01235325d
      --- /dev/null
      +++ b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.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
      + * http://www.owasp.org/index.php/ESAPI.
      + *
      + * Copyright (c) 2019 - The OWASP Foundation
      + *
      + * The ESAPI is 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@gmail.com
      + * @since 2019
      + */
      +package org.owasp.esapi.reference.validation;
      +
      +import org.owasp.esapi.ESAPI;
      +import org.owasp.esapi.SecurityConfiguration;
      +import org.owasp.esapi.SecurityConfigurationWrapper;
      +import org.owasp.esapi.ValidationErrorList;
      +import org.owasp.esapi.ValidationRule;
      +import org.owasp.esapi.Validator;
      +import org.owasp.esapi.errors.ValidationException;
      +import org.owasp.esapi.reference.validation.HTMLValidationRule;
      +import static org.owasp.esapi.PropNames.VALIDATOR_HTML_VALIDATION_ACTION;
      +
      +import org.junit.Test;
      +import org.junit.Before;
      +import org.junit.After;
      +import org.junit.Rule;
      +import org.junit.rules.ExpectedException;
      +import static org.junit.Assert.*;
      +
      +/**
      + * The Class HTMLValidationRuleThrowsTest.
      + *
      + * Based on original test cases, testGetValidSafeHTML() and
      + * testIsValidSafeHTML() from ValidatorTest by
      + *      Mike Fauzy (mike.fauzy@aspectsecurity.com) and
      + *      Jeff Williams (jeff.williams@aspectsecurity.com)
      + * that were originally part of src/test/java/org/owasp/esapi/reference/ValidatorTest.java.
      + *
      + * This class tests the cases where the new ESAPI.property
      + *      Validator.HtmlValidationAction
      + * is set to "throw", which causes certain calls to
      + *      {@code ESAPI.validator().getValidSafeHTML()}
      + * to throw a ValidationException rather than simply logging a warning and returning
      + * the cleansed (sanitizied) output when certain unsafe input is encountered.
      + */
      +public class HTMLValidationRuleThrowsTest {
      +    private static class ConfOverride extends SecurityConfigurationWrapper {
      +        private String desiredReturn = "clean";
      +
      +        ConfOverride(SecurityConfiguration orig, String desiredReturn) {
      +            super(orig);
      +            this.desiredReturn = desiredReturn;
      +        }
      +
      +        @Override
      +        public String getStringProp(String propName) {
      +            // Would it be better making this file a static import?
      +            if ( propName.equals( VALIDATOR_HTML_VALIDATION_ACTION ) ) {
      +                return desiredReturn;
      +            } else {
      +                return super.getStringProp( propName );
      +            }
      +        }
      +    }
      +
      +    // Must be public!
      +    @Rule
      +    public ExpectedException thrownEx = ExpectedException.none();
      +
      +    @After
      +    public void tearDown() throws Exception {
      +        ESAPI.override(null);
      +        thrownEx = ExpectedException.none();
      +    }
      +
      +    @Before
      +    public void setUp() throws Exception {
      +        ESAPI.override(
      +            new ConfOverride( ESAPI.securityConfiguration(), "throw" )
      +        );
      +
      +    }
      +
      +    @Test
      +    public void testGetValid() throws Exception {
      +        System.out.println("getValid");
      +        Validator instance = ESAPI.validator();
      +        HTMLValidationRule rule = new HTMLValidationRule("test");
      +        ESAPI.validator().addRule(rule);
      +
      +        thrownEx.expect(ValidationException.class);
      +        thrownEx.expectMessage("test: Invalid HTML input");
      +
      +        instance.getRule("test").getValid("test", "Test. ");
      +    }
      +
      +    @Test
      +    public void testGetValidSafeHTML() throws Exception {
      +        System.out.println("getValidSafeHTML");
      +        Validator instance = ESAPI.validator();
      +
      +        HTMLValidationRule rule = new HTMLValidationRule("test");
      +        ESAPI.validator().addRule(rule);
      +
      +        String[] testInput = {
      +                                // These first two don't cause AntiSamy to throw.
      +                        // "Test. Aspect Security",
      +                        // "Test. <
      load=alert()", + "Test. ", + "Test. ", + "Test.
      b
      ", + "Test. alert(document.cookie)", + "Test. alert(document.cookie)", + "Test. alert(document.cookie)" + }; + + int errors = 0; + for( int i = 0; i < testInput.length; i++ ) { + try { + String result = instance.getValidSafeHTML("test", testInput[i], 100, false); + errors++; + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + "' failed to throw."); + } + catch( ValidationException vex ) { + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + "' returned:"); + System.out.println("\t" + i + ": logMsg =" + vex.getLogMessage()); + assertEquals( vex.getUserMessage(), "test: Invalid HTML input"); + } + catch( Exception ex ) { + errors++; + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + + "' threw wrong exception type: " + ex.getClass().getName() ); + } + } + + if ( errors > 0 ) { + fail("testGetValidSafeHTML() encountered " + errors + " failures."); + } + } + +} diff --git a/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java b/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java index e172e2ebb..4028e5f8e 100644 --- a/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java +++ b/src/test/java/org/owasp/esapi/reference/validation/StringValidationRuleTest.java @@ -11,170 +11,170 @@ 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