diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 723666891c607e485ea5867320453c8ed72c4bcb..b263fddf9e822b03c2413c45e39b2d511c38a531 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -19,7 +19,7 @@ jobs: # Runs the Super-Linter action and ignore errors - name: Run Super-Linter - uses: github/super-linter@v3 + uses: github/super-linter@v4 env: DEFAULT_BRANCH: develop DISABLE_ERRORS: true diff --git a/.snyk b/.snyk new file mode 100644 index 0000000000000000000000000000000000000000..79fa52da9087790f52b3ebb4bf1fc15ab182e08e --- /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 9d24cc91d67fbd4ed2104f21fe0b746ec93d82cb..df79adc5a75ce95e844d9ef60bfd7652462e4357 100644 --- a/CONTRIBUTING-TO-ESAPI.txt +++ b/CONTRIBUTING-TO-ESAPI.txt @@ -46,10 +46,9 @@ 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.) [Note: If you use JDK 9 or later, there will be multiple + 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.] diff --git a/README.md b/README.md index 338e697a0a0c4b80ff25d028997b7cc2ee88b1df..700ad6c289f78c5d23a3c4d4991b13e0ce13ab05 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Enterprise Security API for Java (Legacy) [](https://travis-ci.org/bkimminich/esapi-java-legacy) [](https://coveralls.io/github/bkimminich/esapi-java-legacy?branch=develop) [](https://scan.coverity.com/projects/bkimminich-esapi-java-legacy) -[](https://bestpractices.coreinfrastructure.org/projects/137) +[](https://bestpractices.coreinfrastructure.org/projects/137) <table border=0> <tr> @@ -14,6 +14,11 @@ OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web ap </tr> </table> +# A word about ESAPI and Log4J vulnerabilities +This is way too detailed to litter the README file with, but several of you have +been asking about this, so I wrote up something on it and posted it to the ESAPI +Users Google group. You can find it 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). + # Where is the OWASP ESAPI wiki page? You can find the OWASP ESAPI 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 a few useful wiki pages. @@ -24,7 +29,7 @@ You will find that GitHub repository at [https://github.com/ESAPI/esapi-java-leg <b>IMPORTANT NOTES:</b> The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'main' (formerly 'master') branch), where future development, bug fixes, etc. will now be done. The 'main' 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. -Also, the <i>minimal</i> baseline Java version to use ESAPI is Java 7. (This was changed from Java 6 during the 2.2.0.0 release.) +Also, the <i>minimal</i> baseline Java version to use ESAPI is Java 8. (This was changed from Java 7 during the 2.4.0.0 release.) # Where can I find ESAPI 3.x? [https://github.com/ESAPI/esapi-java](https://github.com/ESAPI/esapi-java) @@ -34,10 +39,10 @@ Note however that work on ESAPI 3 has not yet become in earnest and is only in i # 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). ## IMPORTANT -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** the release notes for the 2.2.3.0 release (at least the beginning portion) for some important notes that likely will affect your use of ESAPI! You have been warned!!! +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** the [release notes for the 2.2.3.0 release](https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/esapi4java-core-2.2.3.0-release-notes.txt) (at least the beginning portion) for some important notes that likely will affect your use of ESAPI! You have been warned!!! # Locating ESAPI Jar files -The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.3.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.3.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.3.0/esapi-2.2.3.0-configuration.jar) and [esapi-2.2.3.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.3.0/esapi-2.2.3.0-configuration.jar.asc) respectively. +The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.3.1. The default configuration jar and its GPG signature can be found at [esapi-2.2.3.1-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.3.1/esapi-2.2.3.1-configuration.jar) and [esapi-2.2.3.1-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.3.1/esapi-2.2.3.1-configuration.jar.asc) respectively. The latest *regular* ESAPI jars can are available from Maven Central. diff --git a/SECURITY.md b/SECURITY.md index f23c9566ef57bf32eab7a8b013db8bad8e744709..afed85464ef9a46fa9c7021d8f8273495fc95655 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,8 +4,8 @@ | Version | Supported | | ------- | ------------------ | -| 2.2.0.0 | :white_check_mark: | -| 2.1.0.1 | :x:, upgrade to 2.2.0.0| +| 2.2.3.1 (latest) | :white_check_mark: | +| 2.1.0.1-2.2.3.0 | :x:, upgrade to latest release | | <= 1.4.x | :x:, no longer supported AT ALL | ## Reporting a Vulnerability diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 828144b456e1d99da88441798f275fc5b8491453..bbb7531cd4438003a5aa8aa6f46ba97276f2412b 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -194,9 +194,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 @@ -223,37 +220,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 @@ -469,7 +456,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()\\-=\\*\\.\\?;,+\\/:&_ ]*$ diff --git a/configuration/esapi/antisamy-esapi.xml b/configuration/esapi/antisamy-esapi.xml index c45fee06efd131e0302f0f72a514e6b303d074da..b6edfb3cd1c34d90611b92b25a723d16c5f08c04 100644 --- a/configuration/esapi/antisamy-esapi.xml +++ b/configuration/esapi/antisamy-esapi.xml @@ -31,9 +31,9 @@ Slashdot allowed tags taken from "Reply" page: space characters. --> - <regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' --> - <regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/> - <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/> + <regexp name="htmlTitle" value="[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*"/> + <regexp name="onsiteURL" value="^(?!//)(?![\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*(&colon))[\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*"/> + <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[\p{L}\p{N}\p{Zs}\.\#@\$%\+&;:\-_~,\?=/!\(\)]*(\s)*"/> </common-regexps> diff --git a/documentation/ESAPI-release-steps.odt b/documentation/ESAPI-release-steps.odt index 0e55259ae15648e8fd4a4ed565711c9db7e95806..ea02433b621f9eb0609195f451e4dc567f536317 100644 Binary files a/documentation/ESAPI-release-steps.odt and b/documentation/ESAPI-release-steps.odt differ diff --git a/documentation/ESAPI-release-steps.pdf b/documentation/ESAPI-release-steps.pdf index 1b2a03d06a282e8d2a7e3390dcdc6ed16c233a3e..405dc1dff75c1a4d34f9f6397bb6102bd35c55dc 100644 Binary files a/documentation/ESAPI-release-steps.pdf and b/documentation/ESAPI-release-steps.pdf differ diff --git a/documentation/ESAPI-security-bulletin1.docx b/documentation/ESAPI-security-bulletin1.docx index de1cf53dac670041f85e8d96bf62c87318b8ee01..55cc3ba39f1935e098c04ee18bbe4dc68d37532f 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 b1ca473b08013244394ed10056963ce43cc3e758..ba2f68a8323251a6e961b407a09d342a14097462 100644 Binary files a/documentation/ESAPI-security-bulletin1.pdf and b/documentation/ESAPI-security-bulletin1.pdf differ diff --git a/documentation/ESAPI-security-bulletin2.odt b/documentation/ESAPI-security-bulletin2.odt index c65623126fc7cab343fded85c86ac6b1b636ca78..793f0ffbfae52af0878b7707421b9913ee4c0448 100644 Binary files a/documentation/ESAPI-security-bulletin2.odt and b/documentation/ESAPI-security-bulletin2.odt differ diff --git a/documentation/ESAPI-security-bulletin2.pdf b/documentation/ESAPI-security-bulletin2.pdf index 7db6b755eff55b28136af02fd87fe92f1480be98..6925ad568f97a19ae32d411884c902a350552f70 100644 Binary files a/documentation/ESAPI-security-bulletin2.pdf and b/documentation/ESAPI-security-bulletin2.pdf differ diff --git a/documentation/ESAPI-security-bulletin3.odt b/documentation/ESAPI-security-bulletin3.odt index dc3352743830ed081a3c048435c7298163a5796a..8026a3936ccd5550af22749c1618f3fb46bc1908 100644 Binary files a/documentation/ESAPI-security-bulletin3.odt and b/documentation/ESAPI-security-bulletin3.odt differ diff --git a/documentation/ESAPI-security-bulletin3.pdf b/documentation/ESAPI-security-bulletin3.pdf index abb7458a45b8833f069500c3d362cf815d543b84..98af7cfd3f3bcd35153a3bb9cb1c41114409e784 100644 Binary files a/documentation/ESAPI-security-bulletin3.pdf and b/documentation/ESAPI-security-bulletin3.pdf differ diff --git a/documentation/ESAPI-security-bulletin4.odt b/documentation/ESAPI-security-bulletin4.odt index 41fb6366154d7bc846c501bd10dbcb9257972724..59c930f2735990c926c544dc96d3a69338257bba 100644 Binary files a/documentation/ESAPI-security-bulletin4.odt and b/documentation/ESAPI-security-bulletin4.odt differ diff --git a/documentation/ESAPI-security-bulletin4.pdf b/documentation/ESAPI-security-bulletin4.pdf index 12ab1dc41f9cca9b21ed495f99698991178fad1c..7a125584d2fe0bcc67552817f800f5296bbf9417 100644 Binary files a/documentation/ESAPI-security-bulletin4.pdf 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 0000000000000000000000000000000000000000..9edbd72d19dea31245625dc22fc7c00863c2943a 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 0000000000000000000000000000000000000000..b3a215f05063799f9b59101a008ebab70ba43bb4 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 0000000000000000000000000000000000000000..88d0c98a58b40a1c930cc8a33a25c52dc703b74d 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 0000000000000000000000000000000000000000..970d79d9632fe11105af6aa3eb616995fe9f879c 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 0000000000000000000000000000000000000000..6688b3b87f09c99924d133ff24fecb7251e01e4b 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 0000000000000000000000000000000000000000..38d60ebdae7f4db3f14625cb9f6c20ed5df7d483 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 0000000000000000000000000000000000000000..81a1d9812abd9b68ec1ca30fffcea08c6f5fa200 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 0000000000000000000000000000000000000000..04f8767daddde984b0c7920e11b0bbcf7897fef5 Binary files /dev/null and b/documentation/ESAPI-security-bulletin8.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 0000000000000000000000000000000000000000..bc4956ab03eab207c19f5d321aa6f58dd8c8190f --- /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 0000000000000000000000000000000000000000..c77efef05587aef994a33dea552b3f40e5c3d5af Binary files /dev/null and b/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.pdf differ diff --git a/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html b/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html index b0c8bf9451ce5e497c31a1e9fbc36daf450875e5..d0ecfb2f9436fac10b407784b386c5aa77c8abf9 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 @@ are ones that you would replace.</P> 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. - <B>NOTE:</B> "fixed" is deprecated and will be removed by - release 2.3. + <B>NOTE:</B> "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 <code>ConfigurationException</code> being thrown. </FONT></P><P><FONT SIZE=2> <B>CAUTION:</B> While it is not required that the IV be kept secret, encryption relying on fixed IVs can lead to a known 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 0000000000000000000000000000000000000000..5d2a5b4d699f951a403a5699c56be7d5e03d94a7 --- /dev/null +++ b/documentation/esapi4java-core-2.3.0.0-release-notes.txt @@ -0,0 +1,210 @@ +Release notes for ESAPI 2.3.0.0 + Release date: 2022-04-17 + Project leaders: + -Kevin W. Wall <kevin.w.wall@gmail.com> + -Matt Seil <matt.seil@owasp.org> + +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 does not yet have an assigned CVE) 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. + +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. This currently does not have a CVE associated with it. We likely will leave it to GHSL to determine if they want to file a CVE for it or not. + +6) There remains one known unpatched, potentially exploitable vulnerability (a DoS vulnerability in the transitive dependency Neko HtmlUnit) in ESAPI 2.3.0.0. To our knowledge, that vulnerability has not yet been assigned a CVE, 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.) + + +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: + <regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/> + + The corrected line should look like: + <regexp name="onsiteURL" value="^(?!//)(?![\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*(&colon))[\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*"/> + +We have also updated the other regular expressions in the '<common-regexps>' 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: + <regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> + <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/> + + The updated regular expression values for them: + <regexp name="htmlTitle" value="[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*"/> + <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[\p{L}\p{N}\p{Zs}\.\#@\$%\+&;:\-_~,\?=/!\(\)]*(\s)*"/> + +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 "<common-regexps>" 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 0000000000000000000000000000000000000000..44ae6a2e9b1cff87221055d4b40116b374bdbcb2 --- /dev/null +++ b/documentation/esapi4java-core-2.4.0.0-release-notes.txt @@ -0,0 +1,148 @@ +Release notes for ESAPI 2.4.0.0 + Release date: 2022-04-24 + Project leaders: + -Kevin W. Wall <kevin.w.wall@gmail.com> + -Matt Seil <matt.seil@owasp.org> + +Previous release: ESAPI 2.3.0.0, 2022-04-18 + +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 + 4325 JUnit tests in 136 Java source files (1 test ignored) + +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%3D2021-05-07] + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- +682 Update baseline to java 1.8 +679 Completely remove support for fixed IVs and throw a ConfigurationException if encountered. +672 (wontfix) HTMLEntityCodec Bug Decoding "Left Angular Bracket" Symbol +644 Do not include a logging implementation as a dependency slf4j-simple + + + +----------------------------------------------------------------------------- + + 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 youre application is that old. + + !!!!! VULNERABILITY ALERTS !!!!! + +2) This release closes our implementation of fixes outlined in CVE-2022-28366 from AntiSamy. This was a DoS vulnerability discovered in HtmlUnit-Neko affecting all versions up to 2.26. Details from MITRE are here: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2022-28366 + +----------------------------------------------------------------------------- + +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 close CVE-2022-28366. +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=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] 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 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/pom.xml b/pom.xml index 3c042c4997917b18134b0f5ade3505310aa32380..4ae5b5a6dc939a92a740ed9dbe99217c9e55e07e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.owasp.esapi</groupId> <artifactId>esapi</artifactId> - <version>2.2.3.1</version> + <version>2.4.0.0-SNAPSHOT</version> <packaging>jar</packaging> <distributionManagement> @@ -92,28 +92,35 @@ <name>Jeff Williams</name> <organization>Contrast Security</organization> <roles> - <role>Project Inventor</role> + <role>Project Founder</role> </roles> </developer> <developer> <name>Kevin W. Wall</name> - <organization>Wells Fargo</organization> + <organization>Verisign</organization> <roles> - <role>Project Co-owner</role> + <role>Project Co-leader</role> </roles> </developer> <developer> <name>Matt Seil</name> <organization>OWASP</organization> <roles> - <role>Project Co-owner</role> + <role>Project Co-leader</role> </roles> </developer> + <developer> + <name>Jeremiah J. Stacey</name> + <roles> + <role>JUnit SME</role> + <role>Jack of all trades, master of many</role> + </roles> + </developer> <developer> <name>Chris Schmidt</name> - <organization>Synopsys</organization> + <organization>Fluid Truck</organization> <roles> - <role>Former project co-owner</role> + <role>Former project co-leader</role> </roles> </developer> </developers> @@ -125,27 +132,28 @@ <contributor> <name>Jim Manico</name> </contributor> - <contributor> - <name>Jeremiah J. Stacey</name> - </contributor> </contributors> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <version.jmh>1.28</version.jmh> - <!-- Note: powermock v2.0.8 doesn't exist. v2.0.9+ requires mockito-core v3+, which requires Java 8 --> - <version.powermock>2.0.7</version.powermock> - <version.spotbugs>4.2.2</version.spotbugs> - <version.spotbugs.maven>4.2.2</version.spotbugs.maven> - <version.surefire>3.0.0-M5</version.surefire> + <version.jmh>1.35</version.jmh> + <version.powermock>2.0.9</version.powermock> + <version.spotbugs>4.6.0</version.spotbugs> + <version.findsecbugs>1.12.0</version.findsecbugs> + <version.spotbugs.maven>4.6.0.0</version.spotbugs.maven> + <version.surefire>3.0.0-M6</version.surefire> + <project.java.target>1.8</project.java.target> + <!-- TODO: Be sure to update. Should be date of previous official release --> + <!-- Exact date in the form 'yyyy-dd-yy 00:00:00' should be used. You can find the previous release date --> + <!-- n the previous release notes file under the 'documentation/' directory. --> + <date.prev_release>2021-05-07 00:00:00</date.prev_release> </properties> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> - <!-- Note: v3.1.0+ causes compilation errors. So would have to fix to upgrade. --> - <version>3.0.1</version> + <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> @@ -153,6 +161,12 @@ <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> + <exclusions> + <exclusion> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>com.io7m.xom</groupId> @@ -198,6 +212,10 @@ <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> + <exclusion> + <groupId>xml-apis</groupId> + <artifactId>xml-apis</artifactId> + </exclusion> </exclusions> </dependency> <dependency> @@ -208,8 +226,7 @@ <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> - <!-- Upgrading to 1.4 causes this test case failure: [ERROR] HTTPUtilitiesTest.testGetFileUploads:259. TODO: Figure out why, and fix. --> - <version>1.3.3</version> + <version>1.4</version> <exclusions> <!-- excluded because we directly import newer version. --> <exclusion> @@ -226,8 +243,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> - <!-- Using 4.2 because 4.3 requires Java 8. Trying to make sure ESAPI supports Java 7+ --> - <version>4.2</version> + <version>4.4</version> </dependency> <dependency> <groupId>org.apache-extras.beanshell</groupId> @@ -237,23 +253,22 @@ <dependency> <groupId>org.owasp.antisamy</groupId> <artifactId>antisamy</artifactId> - <version>1.6.3</version> - <exclusions> - <!-- excluded because we pick up much newer version --> - <exclusion> - <groupId>commons-io</groupId> - <artifactId>commons-io</artifactId> - </exclusion> - </exclusions> + <version>1.6.8</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> - <version>1.7.30</version> + <version>1.7.36</version> </dependency> <dependency> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> + <!-- 1.4.01 is NEWER THAN 2.0.2 + Check the release dates in maven central + https://mvnrepository.com/artifact/xml-apis/xml-apis + + DO NOT UPDATE THIS DEPENDENCY + --> <version>1.4.01</version> </dependency> @@ -264,12 +279,9 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <!-- Note: commons-io:2.7 and later require Java 8, so can't upgrade past 2.6 --> - <!-- This means still possible exposure to CVE-2021-29425. --> - <version>2.6</version> + <version>2.11.0</version> </dependency> - <!-- SpotBugs dependencies --> <dependency> <groupId>com.github.spotbugs</groupId> @@ -290,17 +302,23 @@ <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> - <version>1.68</version> + <version>1.70</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> - <version>1.3</version> + <version>2.2</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito --> @@ -311,29 +329,20 @@ <scope>test</scope> <exclusions> <exclusion> - <!-- A subdependency: org.powermock:powermock-core:2.0.0, results in the import of: org.javassist:javassist:3.24.0-GA - which was compiled for Java 8 by accident. org.javassist:javassist:3.25.0-GA was released for Java 7. - So we exclude it here, and import the version we need below. --> - <groupId>org.javassist</groupId> - <artifactId>javassist</artifactId> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> </exclusion> </exclusions> </dependency> - <!-- The following imported solely so we can exclude its dependency on: org.objenesis:objenesis, which conflicts with + <!-- The following imported solely so we can exclude its dependency on: org.objenesis:objenesis, which conflicts with another import by a dependency of powermock-api-mockito2. --> - <dependency> - <!-- This version is compatible with Java 7, the previous version was not (by accident). --> - <!-- Versions 3.26.0-GA and later require Java 8. --> - <groupId>org.javassist</groupId> - <artifactId>javassist</artifactId> - <version>3.25.0-GA</version> - <scope>test</scope> - </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <!-- mockito-core v3.0.0+ requires Java 8 --> - <version>2.28.2</version> + <!-- Cannot upgrade past 3.x due to PowerMock compatibility + https://github.com/powermock/powermock/issues/1109 + --> + <version>3.12.4</version> <scope>test</scope> </dependency> <dependency> @@ -342,10 +351,13 @@ <version>${version.powermock}</version> <scope>test</scope> <exclusions> + <exclusion> + <groupId>net.bytebuddy</groupId> + <artifactId>byte-buddy</artifactId> + </exclusion> <exclusion> - <!-- We exclude this here, because we import the version we need above, and this imports a newer version. --> - <groupId>org.javassist</groupId> - <artifactId>javassist</artifactId> + <groupId>net.bytebuddy</groupId> + <artifactId>byte-buddy-agent</artifactId> </exclusion> </exclusions> </dependency> @@ -359,6 +371,10 @@ <groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> + <exclusion> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + </exclusion> </exclusions> </dependency> <dependency> @@ -400,17 +416,37 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> - <version>3.1.2</version> + <version>3.3.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> - <version>3.0.0-M1</version> + <version>3.0.0-M5</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>versions-maven-plugin</artifactId> + <version>2.10.0</version> + <configuration> + <rulesUri>file:${project.basedir}/versionRuleset.xml</rulesUri> + </configuration> </plugin> </plugins> </pluginManagement> <plugins> + <!-- Create SBOM --> + <plugin> + <groupId>org.cyclonedx</groupId> + <artifactId>cyclonedx-maven-plugin</artifactId> + <version>2.5.3</version> + <executions> + <execution> + <phase>package</phase> + <goals><goal>makeBom</goal></goals> + </execution> + </executions> + </plugin> <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> @@ -426,6 +462,12 @@ </dependencies> </plugin> + <plugin> + <groupId>com.h3xstream.findsecbugs</groupId> + <artifactId>findsecbugs-plugin</artifactId> + <version>${version.findsecbugs}</version> + </plugin> + <plugin> <groupId>net.sourceforge.maven-taglib</groupId> <artifactId>maven-taglib-plugin</artifactId> @@ -441,18 +483,18 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-clean-plugin</artifactId> - <version>3.1.0</version> + <version>3.2.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.8.1</version> + <version>3.10.1</version> <configuration> - <source>1.7</source> - <target>1.7</target> - <testSource>1.7</testSource> - <testTarget>1.7</testTarget> + <source>${project.java.target}</source> + <target>${project.java.target}</target> + <testSource>${project.java.target}</testSource> + <testTarget>${project.java.target}</testTarget> <debug>true</debug> <showWarnings>true</showWarnings> <showDeprecation>false</showDeprecation> @@ -481,7 +523,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>3.0.0-M1</version> + <version>3.0.0-M2</version> </plugin> <plugin> @@ -496,19 +538,19 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> - <version>3.0.0-M3</version> + <version>3.0.0</version> <dependencies> <dependency> <groupId>org.codehaus.mojo</groupId> <artifactId>extra-enforcer-rules</artifactId> - <version>1.3</version> + <version>1.5.1</version> </dependency> <dependency> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-enforcer-rule</artifactId> - <!-- Updating to 1.19 causes LOTS of errors in 'mvn site' and 1.18 requires Java 8 so leaving it at 1.17. --> - <version>1.17</version> + <version>1.21</version> </dependency> + </dependencies> <executions> <execution> @@ -519,8 +561,8 @@ <configuration> <rules> <requireMavenVersion> - <version>[3.2.5,)</version> - <message>Building ESAPI 2.x now requires Maven 3.2.5 or later.</message> + <version>[3.3.9,)</version> + <message>Building ESAPI 2.x now requires Maven 3.3.9 or later.</message> </requireMavenVersion> </rules> </configuration> @@ -533,24 +575,24 @@ <rules> <dependencyConvergence/> <requireJavaVersion> - <version>1.7</version> + <version>${project.java.target}</version> <message> - ESAPI 2.x now uses the JDK1.7 for its baseline. Please make sure that your - JAVA_HOME environment variable is pointed to a JDK1.7 or later distribution. + 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. </message> </requireJavaVersion> - <enforceBytecodeVersion> - <maxJdkVersion>1.7</maxJdkVersion> + <enforceBytecodeVersion> + <maxJdkVersion>${project.java.target}</maxJdkVersion> <ignoreOptionals>true</ignoreOptionals> - <ignoredScopes/> <!-- 'test' scopes not ignored so we can actually test on Java 7. --> - <message>Dependencies shouldn't require Java 8+</message> + <ignoredScopes/><!-- 'test' scopes not ignored so we can actually test on Java 8. --> + <message>Dependencies shouldn't require Java 9+</message> </enforceBytecodeVersion> </rules> <fail>true</fail> </configuration> </execution> <execution> - <id>check-java7API-signatures</id> + <id>check-java8API-signatures</id> <phase>compile</phase> <goals><goal>enforce</goal></goals> <configuration> @@ -558,8 +600,8 @@ <checkSignatureRule implementation="org.codehaus.mojo.animal_sniffer.enforcer.CheckSignatureRule"> <signature> <groupId>org.codehaus.mojo.signature</groupId> - <!-- Check against Java 7 API --> - <artifactId>java17</artifactId> + <!-- Check against Java 8 API --> + <artifactId>java18</artifactId> <version>1.0</version> </signature> </checkSignatureRule> @@ -572,7 +614,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> - <version>1.6</version> + <version>3.0.1</version> <executions> <execution> <id>sign-artifacts</id> @@ -591,7 +633,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> - <version>3.2.0</version> + <version>3.2.2</version> <configuration> <archive> <manifest> @@ -605,9 +647,9 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> - <version>3.2.0</version> + <version>3.4.0</version> <configuration> - <source>7</source> + <source>8</source> <doclint>none</doclint> </configuration> <!-- generate esapi-VERSION.javadoc.jar --> @@ -623,19 +665,19 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jxr-plugin</artifactId> - <version>3.0.0</version> + <version>3.2.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> - <version>3.14.0</version> + <version>3.16.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> - <version>3.1.1</version> + <version>3.2.2</version> </plugin> <plugin> @@ -647,7 +689,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> - <version>3.9.1</version> + <version>3.12.0</version> </plugin> <plugin> @@ -688,17 +730,10 @@ </plugin> <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>io.github.jiangxincode</groupId> <artifactId>jdepend-maven-plugin</artifactId> - <version>2.0</version> + <version>2.1</version> </plugin> - - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>versions-maven-plugin</artifactId> - <version>2.8.1</version> - </plugin> - <plugin> <groupId>org.eluder.coveralls</groupId> <artifactId>coveralls-maven-plugin</artifactId> @@ -708,16 +743,13 @@ <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> - <version>6.1.6</version> + <version>7.1.0</version> <configuration> <failBuildOnCVSS>1.0</failBuildOnCVSS> <suppressionFiles>./suppressions.xml</suppressionFiles> </configuration> <executions> <execution> - <!-- This version of Dep Ck requires Java 8. So only run it if using Java 8 or greater. - This property set in a profile below. --> - <phase>${PhaseIfJava8plus}</phase> <goals> <goal>check</goal> </goals> @@ -742,8 +774,9 @@ <issueLinkUrl>https://github.com/ESAPI/esapi-java-legacy/issues/%ISSUE%</issueLinkUrl> <type>date</type> <dates> - <!-- Should be date of previous official release --> - <date>2015-02-05 00:00:00</date> + <!-- TODO: Be sure to update. Should be date of previous official release --> + <!-- Exact date should be in previous release notes file under 'documentation/' directory. --> + <date>${date.prev_release}</date> </dates> </configuration> </plugin> @@ -753,7 +786,7 @@ <artifactId>maven-javadoc-plugin</artifactId> <configuration> <doclint>none</doclint> - <source>7</source> + <source>8</source> </configuration> </plugin> <plugin> @@ -766,7 +799,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <configuration> - <targetJdk>1.7</targetJdk> + <targetJdk>${project.java.target}</targetJdk> <sourceEncoding>utf-8</sourceEncoding> <!-- excludeFromFailureFile>exclude-pmd.properties</excludeFromFailureFile --> </configuration> @@ -777,6 +810,7 @@ <reportSets> <reportSet> <reports> + <report>index</report> <report>dependency-convergence</report> </reports> </reportSet> @@ -797,7 +831,7 @@ <plugin> <!-- Using this introduces these errors: Skipped "JDepend" report (jdepend-maven-plugin:2.0:generate), file "jdepend-report.html" already exists. but don't know how to eliminate them, without disabling this plugin. --> - <groupId>org.codehaus.mojo</groupId> + <groupId>io.github.jiangxincode</groupId> <artifactId>jdepend-maven-plugin</artifactId> </plugin> <!-- Check for available updates to dependencies and report on them. --> @@ -819,41 +853,7 @@ </reporting> <profiles> - <profile> - <id>Java8plus</id> - <activation> - <jdk>[1.8,)</jdk> - </activation> - <properties> - <PhaseIfJava8plus>site</PhaseIfJava8plus> - </properties> - - <reporting> - <plugins> - - <!-- Run SpotBugs with the FindSecBugs plugin and include results in site report. - Is put in this profile as SpotBugs requires Java 8+ --> - <plugin> - <groupId>com.github.spotbugs</groupId> - <artifactId>spotbugs-maven-plugin</artifactId> - <configuration> - <plugins> - <plugin> - <groupId>com.h3xstream.findsecbugs</groupId> - <artifactId>findsecbugs-plugin</artifactId> - <version>1.10.1</version> - </plugin> - </plugins> - <effort>Max</effort> - <relaxed>false</relaxed> - </configuration> - </plugin> - </plugins> - </reporting> - - </profile> - - <profile> + <profile> <!-- Activate to sign jars and build distributable download. --> <id>dist</id> @@ -873,7 +873,6 @@ <plugin> <artifactId>maven-jar-plugin</artifactId> - <!-- <executions> <execution> @@ -887,7 +886,7 @@ <configuration> <!-- <keystore>codesign.keystore</keystore> - <alias>owasp foundation, inc.'s godaddy.com, inc. id</alias> + <alias>OWASP Foundation, Inc.'s GoDaddy.com ID</alias> <verify>true</verify> --> <archive> @@ -930,7 +929,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> - <version>2.5.3</version> <configuration> <tagBase>https://github.com/ESAPI/esapi-java-legacy/tags</tagBase> </configuration> diff --git a/scripts/README.txt b/scripts/README.txt index 94aeeeffef38281d3d518b1e550b9dfe0f204ea8..61df78f209881d292f63bb15438e79605027acb5 100644 --- a/scripts/README.txt +++ b/scripts/README.txt @@ -6,3 +6,10 @@ README.txt -- This readme file. esapi-release.sh -- Obsolete script to create new ESAPI release. Will be replaced soon. Do not use for now. 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.2.3.0 -- File that is 'sourced' (as in "source ./filename") and used with newReleaseNotes.sh +vars.2.2.3.1 -- File that is 'sourced' (as in "source ./filename") and used with newReleaseNotes.sh +vars.2.3.0.0 -- File that is 'sourced' (as in "source ./filename") and used with newReleaseNotes.sh +vars.template -- Template to construct the release specific vars files diff --git a/scripts/esapi4java-core-TEMPLATE-release-notes.txt b/scripts/esapi4java-core-TEMPLATE-release-notes.txt index af4f130bc35f43067d3105c69d4ad2a625127561..5caad5c3776024fcbc5117a6dea5614c8b44c926 100644 --- a/scripts/esapi4java-core-TEMPLATE-release-notes.txt +++ b/scripts/esapi4java-core-TEMPLATE-release-notes.txt @@ -1,5 +1,5 @@ @@@@ IMPORTANT: Be sure to 1) save in DOS text format, and 2) Delete this line and others starting with @@@@ -@@@ Edit with :set tw=0 +@@@@ Edit this file in vim with :set tw=0 @@@@ Meant to be used with scripts/newReleaseNotes.sh and the 'vars.*' scripts there. Release notes for ESAPI ${VERSION} Release date: ${YYYY_MM_DD_RELEASE_DATE} @@ -30,12 +30,12 @@ ESAPI ${PREV_VERSION} release: #### Java source files #### JUnit tests in #### Java test files -ESAPI ${version} release: +ESAPI ${VERSION} release: @@@@ Count them and run 'mvn test' to get the # of tests. #### 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)'). +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 diff --git a/scripts/newReleaseNotes.sh b/scripts/newReleaseNotes.sh index 7e93da5d79ad16468d941839f1a19c5307cee91d..c9f3d351a5b9e34cad146eda489509b00a90da30 100755 --- a/scripts/newReleaseNotes.sh +++ b/scripts/newReleaseNotes.sh @@ -24,6 +24,7 @@ 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 diff --git a/scripts/vars.2.3.0.0 b/scripts/vars.2.3.0.0 new file mode 100644 index 0000000000000000000000000000000000000000..4695f1f6f2054600d34fd6c5997dd1156f4508c8 --- /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.template b/scripts/vars.template index adae0e835585a32c5df3ee67b730ba18f425f59e..7e127c7a3454447d221017ef8d4d2d998a7f196d 100644 --- a/scripts/vars.template +++ b/scripts/vars.template @@ -1,4 +1,5 @@ -# Do NOT edit this file directly. It will be created by the new newReleaseNotes.sh script. +# 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 diff --git a/src/main/java/org/owasp/esapi/SecurityConfiguration.java b/src/main/java/org/owasp/esapi/SecurityConfiguration.java index d21a7eba47281429a17dd01f8d7434912b786079..27c4af964fc3670abdf88750a355e8cc43590fa7 100644 --- a/src/main/java/org/owasp/esapi/SecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/SecurityConfiguration.java @@ -389,35 +389,22 @@ 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 <b>deprecated</b> 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. + * For this reason, 'fixed' has now been removed (it was considered <b>deprecated</b> + * since release 2.2.0.0). An <b>ESAPI.properties</b> 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" or "fixed" (dereprected). + * @return A string specifying the IV type. Should be "random". Anything + * else should fail with a {@Code ConfigurationException} being thrown. * * @see #getFixedIV() * @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 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 * <b>both</b> confidentiality and authenticity. These would be preferred diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java index 1047116fabf7dbe4d81c7f6e4b96512e5d4813f8..a23aaedc59cb400ccf287ea4ca81ac565fe17f23 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -320,11 +320,10 @@ public final class CipherText implements Serializable { * base64-encoding is performed. * <p> * If there is a need to store an encrypted value, say in a database, this - * is <i>not</i> the method you should use unless you are using a <i>fixed</i> - * IV or are planning on retrieving the IV and storing it somewhere separately - * (e.g., a different database column). If you are <i>not</i> using a fixed IV - * (which is <strong>highly</strong> discouraged), you should normally use - * {@link #getEncodedIVCipherText()} instead. + * is <i>not</i> 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. * </p> * @see #getEncodedIVCipherText() */ @@ -338,11 +337,6 @@ public final class CipherText implements Serializable { * base64-encoding. If an IV is not used, then this method returns the same * value as {@link #getBase64EncodedRawCipherText()}. * <p> - * Generally, this is the method that you should use unless you only - * are using a fixed IV and a storing that IV separately, in which case - * using {@link #getBase64EncodedRawCipherText()} can reduce the storage - * overhead. - * </p> * @return The base64-encoded ciphertext or base64-encoded IV + ciphertext. * @see #getBase64EncodedRawCipherText() */ @@ -591,8 +585,8 @@ public final class CipherText implements Serializable { // 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()); } diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java index 889d5f0bba8d2ec940fb02b920a45f3008a29c12..6e57c39e0e0603007b0003f00c0a3722d96e7813 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java @@ -24,7 +24,7 @@ public class JavaLogger implements org.owasp.esapi.Logger { /** Handler for translating events from ESAPI context for Java processing.*/ private final JavaLogBridge logBridge; /** Maximum log level that will be forwarded to Java from the ESAPI context.*/ - private int maxLogLevel; + private int loggingLevel; /** * Constructs a new instance. @@ -35,7 +35,7 @@ public class JavaLogger implements org.owasp.esapi.Logger { public JavaLogger(java.util.logging.Logger JavaLogger, JavaLogBridge bridge, int defaultEsapiLevel) { delegate = JavaLogger; this.logBridge = bridge; - maxLogLevel = defaultEsapiLevel; + loggingLevel = defaultEsapiLevel; } private void log(int esapiLevel, EventType type, String message) { @@ -52,8 +52,7 @@ public class JavaLogger implements org.owasp.esapi.Logger { private boolean isEnabled(int esapiLevel) { - //Are Logger.OFF and Logger.ALL reversed? This should be simply the less than or equal to check... - return (esapiLevel <= maxLogLevel && maxLogLevel != Logger.OFF) || maxLogLevel == Logger.ALL; + return esapiLevel >= loggingLevel; } @Override @@ -128,7 +127,7 @@ public class JavaLogger implements org.owasp.esapi.Logger { @Override public int getESAPILevel() { - return maxLogLevel; + return loggingLevel; } @Override @@ -162,7 +161,7 @@ public class JavaLogger implements org.owasp.esapi.Logger { @Override public void setLevel(int level) { - maxLogLevel = level; + loggingLevel = level; } } diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java index b24a7448c7587eb4e9845f95be36a5166ccbc7e9..cbbda2140c68e8a75a687b4d857c9c90743e6eff 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java @@ -25,7 +25,7 @@ public class Log4JLogger implements org.owasp.esapi.Logger { /** Handler for translating events from ESAPI context for SLF4J processing.*/ private final Log4JLogBridge logBridge; /** Maximum log level that will be forwarded to SLF4J from the ESAPI context.*/ - private int maxLogLevel; + private int loggingLevel; /** * Constructs a new instance. @@ -36,7 +36,7 @@ public class Log4JLogger implements org.owasp.esapi.Logger { public Log4JLogger(org.apache.log4j.Logger slf4JLogger, Log4JLogBridge bridge, int defaultEsapiLevel) { delegate = slf4JLogger; this.logBridge = bridge; - maxLogLevel = defaultEsapiLevel; + loggingLevel = defaultEsapiLevel; } private void log(int esapiLevel, EventType type, String message) { @@ -53,8 +53,7 @@ public class Log4JLogger implements org.owasp.esapi.Logger { private boolean isEnabled(int esapiLevel) { - //Are Logger.OFF and Logger.ALL reversed? This should be simply the less than or equal to check... - return (esapiLevel <= maxLogLevel && maxLogLevel != Logger.OFF) || maxLogLevel == Logger.ALL; + return esapiLevel >= loggingLevel; } @Override @@ -129,7 +128,7 @@ public class Log4JLogger implements org.owasp.esapi.Logger { @Override public int getESAPILevel() { - return maxLogLevel; + return loggingLevel; } @Override @@ -163,7 +162,7 @@ public class Log4JLogger implements org.owasp.esapi.Logger { @Override public void setLevel(int level) { - maxLogLevel = level; + loggingLevel = level; } } diff --git a/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogger.java b/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogger.java index 9a08ef3b43528d92f5514443133620878a4b0f65..62fead5d1d49a21c668bce396f85b2c0d0f5ba9a 100644 --- a/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogger.java +++ b/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogger.java @@ -24,7 +24,7 @@ public class Slf4JLogger implements org.owasp.esapi.Logger { /** Handler for translating events from ESAPI context for SLF4J processing.*/ private final Slf4JLogBridge logBridge; /** Maximum log level that will be forwarded to SLF4J from the ESAPI context.*/ - private int maxLogLevel; + private int loggingLevel; /** * Constructs a new instance. @@ -35,7 +35,7 @@ public class Slf4JLogger implements org.owasp.esapi.Logger { public Slf4JLogger(org.slf4j.Logger slf4JLogger, Slf4JLogBridge bridge, int defaultEsapiLevel) { delegate = slf4JLogger; this.logBridge = bridge; - maxLogLevel = defaultEsapiLevel; + loggingLevel = defaultEsapiLevel; } private void log(int esapiLevel, EventType type, String message) { @@ -52,8 +52,7 @@ public class Slf4JLogger implements org.owasp.esapi.Logger { private boolean isEnabled(int esapiLevel) { - //Are Logger.OFF and Logger.ALL reversed? This should be simply the less than or equal to check... - return (esapiLevel <= maxLogLevel && maxLogLevel != Logger.OFF) || maxLogLevel == Logger.ALL; + return esapiLevel >= loggingLevel; } @Override @@ -128,7 +127,7 @@ public class Slf4JLogger implements org.owasp.esapi.Logger { @Override public int getESAPILevel() { - return maxLogLevel; + return loggingLevel; } @Override @@ -162,7 +161,7 @@ public class Slf4JLogger implements org.owasp.esapi.Logger { @Override public void setLevel(int level) { - maxLogLevel = level; + loggingLevel = level; } } diff --git a/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java b/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java index d7de99f043b65020b140c6b54f9033d343353590..e6976c7ac76530ac4dfb14ddc841cfad2739af8a 100644 --- a/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java +++ b/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java @@ -114,7 +114,8 @@ public abstract class AbstractAuthenticator implements org.owasp.esapi.Authentic */ protected DefaultUser getUserFromRememberToken() { try { - String token = ESAPI.httpUtilities().getCookie(ESAPI.currentRequest(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME); + HTTPUtilities utils =ESAPI.httpUtilities(); + String token = utils.getCookie(ESAPI.currentRequest(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME); if (token == null) return null; // See Google Issue 144 regarding first URLDecode the token and THEN unsealing. diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index f690e26bdac23771d43ad19ae1e5d3ce9acf8384..a9212f2c6e1c14ca0a3239545b7c9863d95f1679 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -189,8 +189,9 @@ public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities { // validate the name and value ValidationErrorList errors = new ValidationErrorList(); - String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors); - String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", 5000, false, errors); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false, errors); + String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false, errors); // if there are no errors, then set the cookie either with a header or normally if (errors.size() == 0) { @@ -234,11 +235,12 @@ public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities { * {@inheritDoc} */ public void addHeader(HttpServletResponse response, String name, String value) { + SecurityConfiguration sc = ESAPI.securityConfiguration(); try { String strippedName = StringUtilities.replaceLinearWhiteSpace(name); String strippedValue = StringUtilities.replaceLinearWhiteSpace(value); - String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false); - String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", 500, false); + String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); + String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); response.addHeader(safeName, safeValue); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e); @@ -463,9 +465,10 @@ public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities { */ public String getCookie( HttpServletRequest request, String name ) throws ValidationException { Cookie c = getFirstCookie( request, name ); + SecurityConfiguration sc = ESAPI.securityConfiguration(); if ( c == null ) return null; String value = c.getValue(); - return ESAPI.validator().getValidInput("HTTP cookie value: " + value, value, "HTTPCookieValue", 1000, false); + return ESAPI.validator().getValidInput("HTTP cookie value: " + value, value, "HTTPCookieValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); } /** @@ -580,39 +583,40 @@ public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities { upload.setProgressListener(progressListener); List<FileItem> items = upload.parseRequest(request); - for (FileItem item : items) - { - if (!item.isFormField() && item.getName() != null && !(item.getName().equals(""))) - { + for (FileItem item : items) + { + if (!item.isFormField() && item.getName() != null && !(item.getName().equals(""))) + { String[] fparts = item.getName().split("[\\/\\\\]"); String filename = fparts[fparts.length - 1]; - if (!ESAPI.validator().isValidFileName("upload", filename, allowedExtensions, false)) - { + if (!ESAPI.validator().isValidFileName("upload", filename, allowedExtensions, false)) + { throw new ValidationUploadException("Upload only simple filenames with the following extensions " + allowedExtensions, "Upload failed isValidFileName check"); } logger.info(Logger.SECURITY_SUCCESS, "File upload requested: " + filename); File f = new File(finalDir, filename); - if (f.exists()) - { + if (f.exists()) + { String[] parts = filename.split("\\/."); String extension = ""; - if (parts.length > 1) - { + if (parts.length > 1) + { extension = parts[parts.length - 1]; } String filenm = filename.substring(0, filename.length() - extension.length()); f = File.createTempFile(filenm, "." + extension, finalDir); } + item.write(f); - newFiles.add(f); + newFiles.add(f); // delete temporary file item.delete(); logger.fatal(Logger.SECURITY_SUCCESS, "File successfully uploaded: " + f); - if (session != null) - { - session.setAttribute("progress", Long.toString(0)); + if (session != null) + { + session.setAttribute("progress", Long.toString(0)); } } } @@ -655,8 +659,9 @@ public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities { * {@inheritDoc} */ public String getHeader( HttpServletRequest request, String name ) throws ValidationException { + SecurityConfiguration sc = ESAPI.securityConfiguration(); String value = request.getHeader(name); - return ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, false); + return ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); } diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index 5a96c76d86a40da309ba114e0026576e74ff37dd..3e80055d3e84d8ea0b789838f30ab6b47fb487ec 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -121,10 +121,9 @@ public class DefaultSecurityConfiguration implements SecurityConfiguration { 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"; @Deprecated - public static final String FIXED_IV = "Encryptor.fixedIV"; + 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"; @@ -251,6 +250,13 @@ public class DefaultSecurityConfiguration implements SecurityConfiguration { */ public DefaultSecurityConfiguration(Properties properties) { resourceFile = DEFAULT_RESOURCE_FILE; + try { + this.esapiPropertyManager = new EsapiPropertyManager(); + // Do NOT call loadConfiguration() here! + } catch( IOException e ) { + logSpecial("Failed to load security configuration", e ); + throw new ConfigurationException("Failed to load security configuration", e); + } this.properties = properties; this.setCipherXProperties(); } @@ -265,7 +271,7 @@ public class DefaultSecurityConfiguration implements SecurityConfiguration { // TODO: FUTURE: Replace by future CryptoControls class??? // See SecurityConfiguration.setCipherTransformation() for // explanation of this. - // (Propose this in 2.1 via future email to ESAPI-DEV list.) + // (Propose this in a future 2.x release via future email to ESAPI-DEV list.) cipherXformFromESAPIProp = getESAPIProperty(CIPHER_TRANSFORMATION_IMPLEMENTATION, "AES/CBC/PKCS5Padding"); @@ -832,49 +838,26 @@ public class DefaultSecurityConfiguration implements SecurityConfiguration { /** * {@inheritDoc} */ + @Deprecated public String getIVType() { String value = getESAPIProperty(IV_TYPE, "random"); if ( value.equalsIgnoreCase("random") ) { return value; } else if ( value.equalsIgnoreCase("fixed") ) { - logSpecial("WARNING: Property '" + IV_TYPE + "=fixed' is DEPRECATED. It was intended to support legacy applications, but is inherently insecure, especially with any streaming mode. Support for this will be completed dropped next ESAPI minor release (probably 2.3"); - return value; + logSpecial("WARNING: Property '" + IV_TYPE + "=fixed' is no longer supported AT ALL!!! It had been deprecated since 2.2.0.0 and back then, was announced it would be removed in release 2.3.0.0. It was originally intended to support legacy applications, but is inherently insecure, especially with any streaming mode."); + throw new ConfigurationException("'" + IV_TYPE + "=fixed' is no longer supported AT ALL. It has been deprecated since release 2.2 and has been removed since 2.3."); } else if ( value.equalsIgnoreCase("specified") ) { - // This is planned for future implementation where setting - // Encryptor.ChooseIVMethod=specified will require setting some - // other TBD property that will specify an implementation class that - // will generate appropriate IVs. The intent of this would be to use - // such a class with various feedback modes where it is imperative - // that for a given key, any particular IV is *NEVER* reused. For - // now, we will assume that generating a random IV is usually going - // to be sufficient to prevent this. - throw new ConfigurationException("'" + IV_TYPE + "=specified' is not yet implemented. Use 'random' for now."); - } else { - // TODO: Once 'specified' is legal, adjust exception msg, below. - // DISCUSS: Could just log this and then silently return "random" instead. - throw new ConfigurationException(value + " is illegal value for " + IV_TYPE + - ". Use 'random'."); - } - } - - /** - * {@inheritDoc} - */ - @Deprecated - public String getFixedIV() { - if ( getIVType().equalsIgnoreCase("fixed") ) { - String ivAsHex = getESAPIProperty(FIXED_IV, ""); // No default - if ( ivAsHex == null || ivAsHex.trim().equals("") ) { - throw new ConfigurationException("Fixed IV requires property " + - FIXED_IV + " to be set, but it is not."); - } - return ivAsHex; // We do no further checks here as we have no context. + // Originally, this was planned for future implementation where setting + // Encryptor.ChooseIVMethod=specified + // would have allowed a dev to write their own static method to be + // invoked in a future TBD property, but that is a recipe for + // disaster. So, it's not going to happen. Ever. + throw new ConfigurationException("Contrary to previous internal comments, '" + IV_TYPE + "=specified' is not going to be supported -- ever."); } else { - // DISCUSS: Should we just log a warning here and return null instead? - // If so, may cause NullPointException somewhere later. - throw new ConfigurationException("IV type not 'fixed' [which is DEPRECATED!] (set to '" + - getIVType() + "'), so no fixed IV applicable."); + logSpecial("WARNING: '" + value + "' is illegal value for " + IV_TYPE + + ". Using 'random' for the IV type."); } + return "random"; } /** diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index 530e2efa8c3d36e1c8ed3a0c28bc70b047daba51..0699a5287031cf30370a0cd257bdfe570ed95813 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -466,7 +466,7 @@ public class DefaultValidator implements org.owasp.esapi.Validator { 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() ) ) { + if ( !dir.getCanonicalFile().toPath().startsWith( parent.getCanonicalFile().toPath() ) ) { // Fixes GHSL-2022-008 throw new ValidationException( context + ": Invalid directory name", "Invalid directory, not inside specified parent: context=" + context + ", input=" + input + ", parent=" + parent ); } diff --git a/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java index 64d3e756195364b721292ad2526f160f3a403b48..63022925e6c600f53b59a36509d1cfa047503ea3 100644 --- a/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java +++ b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java @@ -464,25 +464,10 @@ public final class JavaEncryptor implements Encryptor { IvParameterSpec ivSpec = null; if ( ivType.equalsIgnoreCase("random") ) { ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize()); - } else if ( ivType.equalsIgnoreCase("fixed") ) { - String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV(); - ivBytes = Hex.decode(fixedIVAsHex); - /* FUTURE } else if ( ivType.equalsIgnoreCase("specified")) { - // FUTURE - TODO - Create instance of specified class to use for IV generation and - // use it to create the ivBytes. (The intent is to make sure that - // 1) IVs are never repeated for cipher modes like OFB and CFB, and - // 2) to screen for weak IVs for the particular cipher algorithm. - // In meantime, use 'random' for block cipher in feedback mode. Unlikely they will - // be repeated unless you are salting SecureRandom with same value each time. Anything - // monotonically increasing should be suitable, like a counter, but need to remember - // it across JVM restarts. Was thinking of using System.currentTimeMillis(). While - // it's not perfect it probably is good enough. Could even all (advanced) developers - // to define their own class to create a unique IV to allow them some choice, but - // definitely need to provide a safe, default implementation. - */ } else { - // TODO: Update to add 'specified' once that is supported and added above. - throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'"); + // This really shouldn't happen here. Show catch it a few + // lines above. + throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random'."); } ivSpec = new IvParameterSpec(ivBytes); cipherSpec.setIV(ivBytes); diff --git a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java index 7a7e401d1b47e0b4aec98c1c3b06f4dd69c5b832..a1e725f00c38a0ff2dc98c7c543229b3e5f4d695 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java @@ -240,7 +240,7 @@ public class HTMLValidationRule extends StringValidationRule { try { AntiSamy as = new AntiSamy(); - CleanResults test = as.scan(canonical, antiSamyPolicy); + CleanResults test = as.scan(canonical, antiSamyPolicy); // Uses AntiSamy.DOM scanner. List<String> errors = test.getErrorMessages(); if ( !errors.isEmpty() ) { diff --git a/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java b/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java index 097b9d4a8bb4bb415d3040342904a7058aa250cc..d91f95f4a8d62b3caba3792c40475704047e3921 100644 --- a/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java +++ b/src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java @@ -24,6 +24,7 @@ import java.io.RandomAccessFile; import java.util.Enumeration; import java.util.Vector; +import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; @@ -171,6 +172,7 @@ public class InterceptingHTTPServletRequest extends HttpServletRequestWrapper { private class RAFInputStream extends ServletInputStream { RandomAccessFile raf; + boolean isDone = false; public RAFInputStream(RandomAccessFile raf) throws IOException { this.raf = raf; @@ -178,11 +180,29 @@ public class InterceptingHTTPServletRequest extends HttpServletRequestWrapper { } public int read() throws IOException { - return raf.read(); + int rval = raf.read(); + isDone = rval == -1; + return rval; } public synchronized void reset() throws IOException { raf.seek(0); + isDone=false; + } + + @Override + public boolean isFinished() { + return isDone; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + //NO-OP. Unused in this scope } } diff --git a/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java b/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java index 811b3b8bc9e9d7a6e2d5a845b2dc1afe809a67bf..8dcac00488dd99af2cb42c686135a94d0cca4b70 100644 --- a/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java +++ b/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; /** * This class was inspired by ModSecurity for Java by Ivan Ristic. We hook @@ -161,4 +162,14 @@ public class InterceptingServletOutputStream extends ServletOutputStream { } + @Override + public boolean isReady() { + return os.isReady(); + } + + @Override + public void setWriteListener(WriteListener writeListener) { + os.setWriteListener(writeListener); + } + } diff --git a/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java b/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java index 2c54a93f5af634b1ecab76590c4f40920487891a..a051d6f39dee45710a877f70780e70e8a09fdce8 100644 --- a/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java +++ b/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java @@ -291,14 +291,6 @@ public class SecurityConfigurationWrapper implements SecurityConfiguration return wrapped.getIVType(); } - /** - * {@inheritDoc} - */ - // @Override - public String getFixedIV() - { - return wrapped.getFixedIV(); - } /** * {@inheritDoc} diff --git a/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java b/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java index 8f808880f77020e604de55739cb2bcd5940f370b..81cde6a5263a5d55b9eb38e19f12324a14b1f773 100644 --- a/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java @@ -29,10 +29,7 @@ public class CipherSpecTest extends TestCase { private byte[] myIV = null; @Before public void setUp() throws Exception { - // This will throw ConfigurationException if IV type is not set to - // 'fixed', which it's not. (We have it set to 'random'.) - // myIV = Hex.decode( ESAPI.securityConfiguration().getFixedIV() ); - myIV = Hex.decode( "0x000102030405060708090a0b0c0d0e0f" ); + myIV = Hex.decode( "0x000102030405060708090a0b0c0d0e0f" ); // Any IV to test w/ will do. dfltAESCipher = Cipher.getInstance("AES"); dfltECBCipher = Cipher.getInstance("AES/ECB/NoPadding"); diff --git a/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java b/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java index 92fd73fe796f95bf74c6329565a058dbf95a828e..d085b7cb323cbd86c00dd8736f3d2cc7f187c663 100644 --- a/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java +++ b/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java @@ -46,6 +46,7 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; /** @@ -737,4 +738,19 @@ public class MockHttpServletRequest implements HttpServletRequest throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + @Override + public long getContentLengthLong() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String changeSessionId() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java b/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java index 23de2ce6e4686e225cf4046c4c1cbf5aaee9c2c2..fa341db2754ac10d80395d26287e63c3e9a64992 100644 --- a/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java +++ b/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Locale; import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; @@ -279,6 +280,16 @@ public class MockHttpServletResponse implements HttpServletResponse { public void write(int b) throws IOException { body.append((char)b); } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + //NO-OP + } }; } @@ -369,5 +380,10 @@ public class MockHttpServletResponse implements HttpServletResponse { public Collection<String> getHeaders(String string) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + + @Override + public void setContentLengthLong(long len) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/test/java/org/owasp/esapi/http/MockServletContext.java b/src/test/java/org/owasp/esapi/http/MockServletContext.java index 67cb4df597c02f9c7264452faea9226dd057cb70..cab828fc1284d2ed4092d08f19871a348dcdfcba 100644 --- a/src/test/java/org/owasp/esapi/http/MockServletContext.java +++ b/src/test/java/org/owasp/esapi/http/MockServletContext.java @@ -693,4 +693,9 @@ public class MockServletContext implements ServletContext { public void declareRoles(String... strings) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } + + @Override + public String getVirtualServerName() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } } \ No newline at end of file diff --git a/src/test/java/org/owasp/esapi/http/MockServletInputStream.java b/src/test/java/org/owasp/esapi/http/MockServletInputStream.java index 15c9c569d46672ad974000d6d3179d686d46589a..62ff01c0e36fba7e95cd95d7403fb8748024386e 100644 --- a/src/test/java/org/owasp/esapi/http/MockServletInputStream.java +++ b/src/test/java/org/owasp/esapi/http/MockServletInputStream.java @@ -15,6 +15,7 @@ */ package org.owasp.esapi.http; +import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import java.io.IOException; @@ -28,6 +29,7 @@ public class MockServletInputStream extends ServletInputStream { private int next; + private boolean isDone = false; /** * constructor * @param body @@ -45,7 +47,23 @@ public class MockServletInputStream extends ServletInputStream { if (next < body.length) { return body[next++]; } else { + isDone = true; return -1; } } + + @Override + public boolean isFinished() { + return isDone; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + //NO_OP + } } diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java index 2d9c243479b75dfc3d6f6cacaccabf5db39ec6a2..c7258a8ba8951d87a8195e2837be74f023754d8c 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java @@ -46,12 +46,12 @@ public class JavaLoggerTest { public void testLevelEnablement() { testLogger.setLevel(Logger.INFO); - Assert.assertFalse(testLogger.isFatalEnabled()); - Assert.assertFalse(testLogger.isErrorEnabled()); - Assert.assertFalse(testLogger.isWarningEnabled()); + Assert.assertTrue(testLogger.isFatalEnabled()); + Assert.assertTrue(testLogger.isErrorEnabled()); + Assert.assertTrue(testLogger.isWarningEnabled()); Assert.assertTrue(testLogger.isInfoEnabled()); - Assert.assertTrue(testLogger.isDebugEnabled()); - Assert.assertTrue(testLogger.isTraceEnabled()); + Assert.assertFalse(testLogger.isDebugEnabled()); + Assert.assertFalse(testLogger.isTraceEnabled()); Assert.assertEquals(Logger.INFO, testLogger.getESAPILevel()); } diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java index ebea57ac2ae6cf0f1bf7410b7c7cea24c61e1d64..16834f28871bc43b035a6bb5ecaad2bb28f6ca49 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java @@ -36,12 +36,12 @@ public class Log4JLoggerTest { public void testLevelEnablement() { testLogger.setLevel(Logger.INFO); - Assert.assertFalse(testLogger.isFatalEnabled()); - Assert.assertFalse(testLogger.isErrorEnabled()); - Assert.assertFalse(testLogger.isWarningEnabled()); + Assert.assertTrue(testLogger.isFatalEnabled()); + Assert.assertTrue(testLogger.isErrorEnabled()); + Assert.assertTrue(testLogger.isWarningEnabled()); Assert.assertTrue(testLogger.isInfoEnabled()); - Assert.assertTrue(testLogger.isDebugEnabled()); - Assert.assertTrue(testLogger.isTraceEnabled()); + Assert.assertFalse(testLogger.isDebugEnabled()); + Assert.assertFalse(testLogger.isTraceEnabled()); Assert.assertEquals(Logger.INFO, testLogger.getESAPILevel()); } diff --git a/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLoggerTest.java b/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLoggerTest.java index e2d7a5a2f6041e7702235ed5c2e21ae16aee6866..70687f4f209c138417d9d22dd96ce19aa7a98a97 100644 --- a/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLoggerTest.java +++ b/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLoggerTest.java @@ -34,12 +34,12 @@ public class Slf4JLoggerTest { public void testLevelEnablement() { testLogger.setLevel(Logger.INFO); - Assert.assertFalse(testLogger.isFatalEnabled()); - Assert.assertFalse(testLogger.isErrorEnabled()); - Assert.assertFalse(testLogger.isWarningEnabled()); + Assert.assertTrue(testLogger.isFatalEnabled()); + Assert.assertTrue(testLogger.isErrorEnabled()); + Assert.assertTrue(testLogger.isWarningEnabled()); Assert.assertTrue(testLogger.isInfoEnabled()); - Assert.assertTrue(testLogger.isDebugEnabled()); - Assert.assertTrue(testLogger.isTraceEnabled()); + Assert.assertFalse(testLogger.isDebugEnabled()); + Assert.assertFalse(testLogger.isTraceEnabled()); Assert.assertEquals(Logger.INFO, testLogger.getESAPILevel()); } diff --git a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java index 161b5ab6912e4a9143ed349c39fe3617ff672090..d0c3e6dc2ef15cd37dc0257c0b06a455c832adb1 100644 --- a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java +++ b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java @@ -8,8 +8,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.regex.Pattern; +import java.util.Properties; import org.junit.Test; + import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; import org.owasp.esapi.SecurityConfiguration; @@ -19,7 +21,7 @@ import org.owasp.esapi.reference.DefaultSecurityConfiguration.DefaultSearchPath; public class DefaultSecurityConfigurationTest { private DefaultSecurityConfiguration createWithProperty(String key, String val) { - java.util.Properties properties = new java.util.Properties(); + Properties properties = new Properties(); properties.setProperty(key, val); return new DefaultSecurityConfiguration(properties); } @@ -34,7 +36,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetLogImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_LOG_IMPLEMENTATION, secConf.getLogImplementation()); final String expected = "TestLogger"; @@ -45,7 +47,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testAuthenticationImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_AUTHENTICATION_IMPLEMENTATION, secConf.getAuthenticationImplementation()); final String expected = "TestAuthentication"; @@ -56,7 +58,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testEncoderImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCODER_IMPLEMENTATION, secConf.getEncoderImplementation()); final String expected = "TestEncoder"; @@ -67,7 +69,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testAccessControlImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_ACCESS_CONTROL_IMPLEMENTATION, secConf.getAccessControlImplementation()); final String expected = "TestAccessControl"; @@ -78,7 +80,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testEncryptionImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCRYPTION_IMPLEMENTATION, secConf.getEncryptionImplementation()); final String expected = "TestEncryption"; @@ -89,7 +91,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testIntrusionDetectionImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION, secConf.getIntrusionDetectionImplementation()); final String expected = "TestIntrusionDetection"; @@ -100,7 +102,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testRandomizerImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_RANDOMIZER_IMPLEMENTATION, secConf.getRandomizerImplementation()); final String expected = "TestRandomizer"; @@ -111,7 +113,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testExecutorImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_EXECUTOR_IMPLEMENTATION, secConf.getExecutorImplementation()); final String expected = "TestExecutor"; @@ -122,7 +124,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testHTTPUtilitiesImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_HTTP_UTILITIES_IMPLEMENTATION, secConf.getHTTPUtilitiesImplementation()); final String expected = "TestHTTPUtilities"; @@ -133,7 +135,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testValidationImplementation() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(DefaultSecurityConfiguration.DEFAULT_VALIDATOR_IMPLEMENTATION, secConf.getValidationImplementation()); final String expected = "TestValidation"; @@ -144,7 +146,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetEncryptionKeyLength() { // test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals(128, secConf.getEncryptionKeyLength()); final int expected = 256; @@ -155,7 +157,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetKDFPseudoRandomFunction() { // test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals("HmacSHA256", secConf.getKDFPseudoRandomFunction()); final String expected = "HmacSHA1"; @@ -166,7 +168,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetMasterSalt() { try { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); secConf.getMasterSalt(); fail("Expected Exception not thrown"); } @@ -176,7 +178,7 @@ public class DefaultSecurityConfigurationTest { final String salt = "53081"; final String property = ESAPI.encoder().encodeForBase64(salt.getBytes(), false); - java.util.Properties properties = new java.util.Properties(); + Properties properties = new Properties(); properties.setProperty(DefaultSecurityConfiguration.MASTER_SALT, property); DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(properties); assertEquals(salt, new String(secConf.getMasterSalt())); @@ -184,7 +186,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetAllowedExecutables() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); java.util.List<String> allowedExecutables = secConf.getAllowedExecutables(); //is this really what should be returned? what about an empty list? @@ -192,7 +194,7 @@ public class DefaultSecurityConfigurationTest { assertEquals("", allowedExecutables.get(0)); - java.util.Properties properties = new java.util.Properties(); + Properties properties = new Properties(); properties.setProperty(DefaultSecurityConfiguration.APPROVED_EXECUTABLES, String.valueOf("/bin/bzip2,/bin/diff, /bin/cvs")); secConf = new DefaultSecurityConfiguration(properties); allowedExecutables = secConf.getAllowedExecutables(); @@ -208,12 +210,12 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetAllowedFileExtensions() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); java.util.List<String> allowedFileExtensions = secConf.getAllowedFileExtensions(); assertFalse(allowedFileExtensions.isEmpty()); - java.util.Properties properties = new java.util.Properties(); + Properties properties = new Properties(); properties.setProperty(DefaultSecurityConfiguration.APPROVED_UPLOAD_EXTENSIONS, String.valueOf(".txt,.xml,.html,.png")); secConf = new DefaultSecurityConfiguration(properties); allowedFileExtensions = secConf.getAllowedFileExtensions(); @@ -223,7 +225,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetAllowedFileUploadSize() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); //assert that the default is of some reasonable size assertTrue(secConf.getAllowedFileUploadSize() > (1024 * 100)); @@ -235,11 +237,11 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetParameterNames() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals("password", secConf.getPasswordParameterName()); assertEquals("username", secConf.getUsernameParameterName()); - java.util.Properties properties = new java.util.Properties(); + Properties properties = new Properties(); properties.setProperty(DefaultSecurityConfiguration.PASSWORD_PARAMETER_NAME, "j_password"); properties.setProperty(DefaultSecurityConfiguration.USERNAME_PARAMETER_NAME, "j_username"); secConf = new DefaultSecurityConfiguration(properties); @@ -250,7 +252,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetEncryptionAlgorithm() { //test the default - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals("AES", secConf.getEncryptionAlgorithm()); secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCRYPTION_ALGORITHM, "3DES"); @@ -259,11 +261,11 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetCipherXProperties() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertEquals("AES/CBC/PKCS5Padding", secConf.getCipherTransformation()); //assertEquals("AES/CBC/PKCS5Padding", secConf.getC); - java.util.Properties properties = new java.util.Properties(); + Properties properties = new Properties(); properties.setProperty(DefaultSecurityConfiguration.CIPHER_TRANSFORMATION_IMPLEMENTATION, "Blowfish/CFB/ISO10126Padding"); secConf = new DefaultSecurityConfiguration(properties); assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation()); @@ -274,47 +276,35 @@ public class DefaultSecurityConfigurationTest { secConf.setCipherTransformation(null);//sets it back to default assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation()); } - + + // NOTE: When SecurityConfiguration.getIVType() is finally removed, this test can be as well. @Test public void testIV() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - assertEquals("random", secConf.getIVType()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); + assertEquals("random", secConf.getIVType()); // Ensure that 'random' is the default type for getIVType(). + + Properties props = new Properties(); + String ivType = null; + props.setProperty(DefaultSecurityConfiguration.IV_TYPE, "fixed"); // No longer supported. + + secConf = new DefaultSecurityConfiguration( props ); try { - secConf.getFixedIV(); - fail(); + ivType = secConf.getIVType(); // This should now throw a Configuration Exception. + fail("Expected ConfigurationException to be thrown for " + DefaultSecurityConfiguration.IV_TYPE + "=" + ivType); } catch (ConfigurationException ce) { assertNotNull(ce.getMessage()); } - java.util.Properties properties = new java.util.Properties(); - properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "fixed"); - properties.setProperty(DefaultSecurityConfiguration.FIXED_IV, "ivValue"); - secConf = new DefaultSecurityConfiguration(properties); - assertEquals("fixed", secConf.getIVType()); - assertEquals("ivValue", secConf.getFixedIV()); - - properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "illegal"); - secConf = new DefaultSecurityConfiguration(properties); - try { - secConf.getIVType(); - fail(); - } - catch (ConfigurationException ce) { - assertNotNull(ce.getMessage()); - } - try { - secConf.getFixedIV(); - fail(); - } - catch (ConfigurationException ce) { - assertNotNull(ce.getMessage()); - } + props.setProperty(DefaultSecurityConfiguration.IV_TYPE, "illegal"); // This will just result in a logSpecial message & "random" is returned. + secConf = new DefaultSecurityConfiguration(props); + ivType = secConf.getIVType(); + assertEquals(ivType, "random"); } @Test public void testGetAllowMultipleEncoding() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertFalse(secConf.getAllowMultipleEncoding()); secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "yes"); @@ -329,7 +319,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetDefaultCanonicalizationCodecs() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertFalse(secConf.getDefaultCanonicalizationCodecs().isEmpty()); String property = "org.owasp.esapi.codecs.TestCodec1,org.owasp.esapi.codecs.TestCodec2"; @@ -339,7 +329,7 @@ public class DefaultSecurityConfigurationTest { @Test public void testGetDisableIntrusionDetection() { - DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new Properties()); assertFalse(secConf.getDisableIntrusionDetection()); secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "TRUE"); diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 8f345e199a0a05d4092898854e29340dd4505a48..ed04b6d5beded740d2e1f22fa9c4c6f665f5ba02 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -212,6 +212,8 @@ public class EncoderTest extends TestCase { assertEquals( "<", instance.canonicalize("&lT;")); assertEquals( "<", instance.canonicalize("≪")); assertEquals( "<", instance.canonicalize("<")); + assertEquals( "&", instance.canonicalize("&")); + assertEquals( "〈", instance.canonicalize("&lang")); assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript%3Ealert%28%22hello%22%29%3B%3C%2Fscript%3E") ); assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript>alert%28%22hello"%29%3B%3C%2Fscript%3E", false) ); @@ -912,11 +914,28 @@ public class EncoderTest extends TestCase { public void testHtmlDecodeHexEntititesSurrogatePair() { - HTMLEntityCodec htmlCodec = new HTMLEntityCodec(); + HTMLEntityCodec htmlCodec = new HTMLEntityCodec(); String expected = new String (new int[]{0x2f804}, 0, 1); assertEquals( expected, htmlCodec.decode("你") ); assertEquals( expected, htmlCodec.decode("你") ); } + public void testUnicodeCanonicalize() { + Encoder e = ESAPI.encoder(); + String input = "测试"; + String expected = "测试"; + String output = e.canonicalize(input); + assertEquals(expected, output); + } + + public void testUnicodeCanonicalizePercentEncoding() { + //TODO: We need to find a way to specify the encoding type for percent encoding. + //I believe by default we're doing Latin-1 and we really should be doing UTF-8 + Encoder e = ESAPI.encoder(); + String input = "%E6%B5%8B%E8%AF%95"; + String expected = "测试"; + String output = e.canonicalize(input); + assertNotSame(expected, output); + } } diff --git a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java index ba671531353c85253d3becc1dadbe276e7a41d02..aac1686df11da3343909c5f836b459257e7b77ed 100644 --- a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java +++ b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java @@ -45,6 +45,7 @@ import org.owasp.esapi.http.MockHttpServletRequest; import org.owasp.esapi.http.MockHttpServletResponse; import org.owasp.esapi.http.MockHttpSession; import org.owasp.esapi.util.FileTestUtils; +import org.owasp.esapi.util.TestUtils; import junit.framework.Test; import junit.framework.TestCase; @@ -205,7 +206,7 @@ public class HTTPUtilitiesTest extends TestCase * Test of formatHttpRequestForLog method, of class org.owasp.esapi.HTTPUtilities. * @throws IOException */ - public void testGetFileUploads() throws IOException { + public void testGetFileUploads() throws Exception { File home = null; try @@ -226,37 +227,24 @@ public class HTTPUtilitiesTest extends TestCase MockHttpServletRequest request2 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); request2.setContentType( "multipart/form-data; boundary=ridiculous"); ESAPI.httpUtilities().setCurrentHTTP(request2, response); + List<File> response2 = new ArrayList<>(); try { - List<?> list = ESAPI.httpUtilities().getFileUploads(request2, home); - Iterator<?> i = list.iterator(); - while ( i.hasNext() ) { - File f = (File)i.next(); - System.out.println( " " + f.getAbsolutePath() ); - } - assertTrue( list.size() > 0 ); - } catch (ValidationException e) { - System.out.println("ERROR in testGetFileUploads() request2: " + e.toString()); - e.printStackTrace(); - fail(); + response2 = ESAPI.httpUtilities().getFileUploads(request2, home); + assertTrue( response2.size() > 0 ); + } finally { + response2.forEach(file -> file.delete()); } MockHttpServletRequest request4 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); request4.setContentType( "multipart/form-data; boundary=ridiculous"); ESAPI.httpUtilities().setCurrentHTTP(request4, response); System.err.println("UPLOAD DIRECTORY: " + ESAPI.securityConfiguration().getUploadDirectory()); + List<File> response4 = new ArrayList<>(); try { - List<?> list = ESAPI.httpUtilities().getFileUploads(request4, home); - Iterator<?> i = list.iterator(); - while ( i.hasNext() ) { - File f = (File)i.next(); - System.out.println( " " + f.getAbsolutePath() ); - } - assertTrue( list.size() > 0 ); - } catch (ValidationException e) { - // TODO: This test cases if failing when we upgrade to commons-fileupload;commons-fileupload:1.4 because of a duplicate file error. Need to figure out why. - System.out.println("ERROR in testGetFileUploads() request4: " + e.toString()); - e.printStackTrace(); - fail(); + response4 = ESAPI.httpUtilities().getFileUploads(request4, home); + assertTrue( response4.size() > 0 ); + } finally { + response4.forEach(file -> file.delete()); } MockHttpServletRequest request3 = new MockHttpServletRequest("/test", content.replaceAll("txt", "ridiculous").getBytes(response.getCharacterEncoding())); @@ -372,6 +360,27 @@ public class HTTPUtilitiesTest extends TestCase instance.addCookie( response, new Cookie( "test3", "tes<t3" ) ); assertTrue(response.getHeaderNames().size() == 2); } + + /** + * Test of setCookie method, of class org.owasp.esapi.HTTPUtilities. + * Validation failures should prevent cookies being added. + */ + public void testSetCookieExceedingMaxValueAndName() { + HTTPUtilities instance = ESAPI.httpUtilities(); + MockHttpServletResponse response = new MockHttpServletResponse(); + assertTrue(response.getHeaderNames().isEmpty()); + //request.addParameter(TestUtils.generateStringOfLength(32), "pass"); + instance.addCookie( response, new Cookie( TestUtils.generateStringOfLength(32), "pass" ) ); + assertTrue(response.getHeaderNames().size() == 1); + + instance.addCookie( response, new Cookie( "pass", TestUtils.generateStringOfLength(32) ) ); + assertTrue(response.getHeaderNames().size() == 2); + instance.addCookie( response, new Cookie( TestUtils.generateStringOfLength(5000), "fail" ) ); + assertTrue(response.getHeaderNames().size() == 2); + instance.addCookie( response, new Cookie( "fail", TestUtils.generateStringOfLength(5001) ) ); + assertTrue(response.getHeaderNames().size() == 2); + } + /** * diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index ef954107b78b40eecd36fa2ea7c7101c7a4653c5..1e5cfc78839d36fb6130c982f51553aa34bd5e3d 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -18,7 +18,6 @@ package org.owasp.esapi.reference; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -36,7 +35,9 @@ import java.util.Set; import javax.servlet.http.Cookie; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Encoder; import org.owasp.esapi.EncoderConstants; @@ -63,6 +64,9 @@ public class ValidatorTest { private static final String PREFERRED_ENCODING = "UTF-8"; + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + @Test public void testAddRule() { Validator validator = ESAPI.validator(); @@ -73,17 +77,17 @@ public class ValidatorTest { @Test public void testAssertValidFileUpload() { - // assertValidFileUpload(String, String, String, byte[], int, boolean, ValidationErrorList) + // TODO - assertValidFileUpload(String, String, String, byte[], int, boolean, ValidationErrorList) } @Test public void testGetPrintable1() { - // getValidPrintable(String, char[], int, boolean, ValidationErrorList) + // TODO - getValidPrintable(String, char[], int, boolean, ValidationErrorList) } @Test public void testGetPrintable2() { - // getValidPrintable(String, String, int, boolean, ValidationErrorList) + // TODO - getValidPrintable(String, String, int, boolean, ValidationErrorList) } @Test @@ -92,7 +96,7 @@ public class ValidatorTest { ValidationRule rule = new StringValidationRule("rule"); validator.addRule(rule); assertEquals(rule, validator.getRule("rule")); - assertFalse(rule == validator.getRule("ridiculous")); + assertFalse("Found unexpected validation rule named 'ridiculous'.", rule == validator.getRule("ridiculous")); } @Test @@ -116,13 +120,13 @@ public class ValidatorTest { assertEquals(2, errors.size()); assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false, errors)); - assertTrue(errors.size()==3); + assertEquals(3, errors.size()); assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); } @Test @@ -302,35 +306,35 @@ public class ValidatorTest { assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false)); assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); assertFalse(instance.isValidDirectoryPath("test2", "c:\\jeff", parent, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertFalse(instance.isValidDirectoryPath("test3", "c:\\temp\\..\\etc", parent, false, errors)); - assertTrue(errors.size()==3); + assertEquals(3, errors.size()); // Windows paths assertTrue(instance.isValidDirectoryPath("test4", "C:\\", parent, false, errors)); // Windows root directory - assertTrue(errors.size()==3); + assertEquals(3, errors.size()); assertTrue(instance.isValidDirectoryPath("test5", sysRoot, parent, false, errors)); // Windows always exist directory - assertTrue(errors.size()==3); + assertEquals(3, errors.size()); assertFalse(instance.isValidDirectoryPath("test6", sysRoot + "\\System32\\cmd.exe", parent, false, errors)); // Windows command shell - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); // Unix specific paths should not pass assertFalse(instance.isValidDirectoryPath("test7", "/tmp", parent, false, errors)); // Unix Temporary directory - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors)); // Unix Standard shell - assertTrue(errors.size()==6); + assertEquals(6, errors.size()); assertFalse(instance.isValidDirectoryPath("test9", "/etc/config", parent, false, errors)); - assertTrue(errors.size()==7); + assertEquals(7, errors.size()); // Unix specific paths that should not exist or work assertFalse(instance.isValidDirectoryPath("test10", "/etc/ridiculous", parent, false, errors)); - assertTrue(errors.size()==8); + assertEquals(8, errors.size()); assertFalse(instance.isValidDirectoryPath("test11", "/tmp/../etc", parent, false, errors)); - assertTrue(errors.size()==9); + assertEquals(9, errors.size()); - } else { + } else { // Non-Windows OS case... // Windows paths should fail assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false)); assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false)); @@ -357,40 +361,64 @@ public class ValidatorTest { // Windows paths should fail assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); assertFalse(instance.isValidDirectoryPath("test2", "c:\\temp\\..\\etc", parent, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); // Standard Windows locations should fail assertFalse(instance.isValidDirectoryPath("test3", "c:\\", parent, false, errors)); // Windows root directory - assertTrue(errors.size()==3); + assertEquals(3, errors.size()); assertFalse(instance.isValidDirectoryPath("test4", "c:\\Windows\\temp", parent, false, errors)); // Windows temporary directory - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); assertFalse(instance.isValidDirectoryPath("test5", "c:\\Windows\\System32\\cmd.exe", parent, false, errors)); // Windows command shell - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); // Unix specific paths should pass assertTrue(instance.isValidDirectoryPath("test6", "/", parent, false, errors)); // Root directory - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); // Note, we used to say that about '/bin', but Ubuntu 20.x // changed '/bin' to a sym link to 'usr/bin'. We can't use '/etc' // because under MacOS, that is a sym link to 'private/etc'. assertTrue(instance.isValidDirectoryPath("test7", "/dev", parent, false, errors)); // Always exist directory - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); // Unix specific paths that should not exist or work assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors)); // Standard shell, not dir - assertTrue(errors.size()==6); + assertEquals(6, errors.size()); assertFalse(instance.isValidDirectoryPath("test9", "/etc/ridiculous", parent, false, errors)); - assertTrue(errors.size()==7); + assertEquals(7, errors.size()); assertFalse(instance.isValidDirectoryPath("test10", "/tmp/../etc", parent, false, errors)); - assertTrue(errors.size()==8); + assertEquals(8, errors.size()); } } + // GitHub issue # xxxx - GHSL-2022-008 + @Test + public void testIsValidDirectoryPathGHSL_POC() throws IOException { + // Note this uses different 'parent' and 'input' directories that generally don't already + // exist, so we will may have to create them and then remove them. The above + // mkdir() method does this. + + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + + String invalidPath = tempFolder.newFolder("esapi-test2").getAbsolutePath(); + File parent = tempFolder.newFolder("sibling-of-esapi-test2"); + String validPath = tempFolder.newFolder("sibling-of-esapi-test2", "child").getAbsolutePath(); + + // Before the fix, this incorrectly would return 'true' even though + // 'esapi-test2' directory clearly was not within the 'esapi-test' + // directory. + // + assertFalse( instance.isValidDirectoryPath("GHSL-2022-008", invalidPath, parent, false, errors) ); + assertEquals( 1, errors.size() ); + assertTrue (instance.isValidDirectoryPath("GHSL-2022-008", validPath, parent, false, new ValidationErrorList())); + } + + @Test public void TestIsValidDirectoryPath() { - // isValidDirectoryPath(String, String, boolean) + // TODO - isValidDirectoryPath(String, File, boolean) - returns true if no exceptions thrown } @Test @@ -400,56 +428,56 @@ public class ValidatorTest { ValidationErrorList errors = new ValidationErrorList(); //testing negative range assertFalse(instance.isValidDouble("test1", "-4", 1, 10, false, errors)); - assertTrue(errors.size() == 1); + assertEquals(1, errors.size()); assertTrue(instance.isValidDouble("test2", "-4", -10, 10, false, errors)); - assertTrue(errors.size() == 1); + assertEquals(1, errors.size()); //testing null value assertTrue(instance.isValidDouble("test3", null, -10, 10, true, errors)); - assertTrue(errors.size() == 1); + assertEquals(1, errors.size()); assertFalse(instance.isValidDouble("test4", null, -10, 10, false, errors)); - assertTrue(errors.size() == 2); + assertEquals(2, errors.size()); //testing empty string assertTrue(instance.isValidDouble("test5", "", -10, 10, true, errors)); - assertTrue(errors.size() == 2); + assertEquals(2, errors.size()); assertFalse(instance.isValidDouble("test6", "", -10, 10, false, errors)); - assertTrue(errors.size() == 3); + assertEquals(3, errors.size()); //testing improper range assertFalse(instance.isValidDouble("test7", "50.0", 10, -10, false, errors)); - assertTrue(errors.size() == 4); + assertEquals(4, errors.size()); //testing non-integers assertTrue(instance.isValidDouble("test8", "4.3214", -10, 10, true, errors)); - assertTrue(errors.size() == 4); + assertEquals(4, errors.size()); assertTrue(instance.isValidDouble("test9", "-1.65", -10, 10, true, errors)); - assertTrue(errors.size() == 4); + assertEquals(4, errors.size()); //other testing assertTrue(instance.isValidDouble("test10", "4", 1, 10, false, errors)); - assertTrue(errors.size() == 4); + assertEquals(4, errors.size()); assertTrue(instance.isValidDouble("test11", "400", 1, 10000, false, errors)); - assertTrue(errors.size() == 4); + assertEquals(4, errors.size()); assertTrue(instance.isValidDouble("test12", "400000000", 1, 400000000, false, errors)); - assertTrue(errors.size() == 4); + assertEquals(4, errors.size()); assertFalse(instance.isValidDouble("test13", "4000000000000", 1, 10000, false, errors)); - assertTrue(errors.size() == 5); + assertEquals(5, errors.size()); assertFalse(instance.isValidDouble("test14", "alsdkf", 10, 10000, false, errors)); - assertTrue(errors.size() == 6); + assertEquals(6, errors.size()); assertFalse(instance.isValidDouble("test15", "--10", 10, 10000, false, errors)); - assertTrue(errors.size() == 7); + assertEquals(7, errors.size()); assertFalse(instance.isValidDouble("test16", "14.1414234x", 10, 10000, false, errors)); - assertTrue(errors.size() == 8); + assertEquals(8, errors.size()); assertFalse(instance.isValidDouble("test17", "Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 9); + assertEquals(9, errors.size()); assertFalse(instance.isValidDouble("test18", "-Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 10); + assertEquals(10, errors.size()); assertFalse(instance.isValidDouble("test19", "NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 11); + assertEquals(11, errors.size()); assertFalse(instance.isValidDouble("test20", "-NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 12); + assertEquals(12, errors.size()); assertFalse(instance.isValidDouble("test21", "+NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 13); + assertEquals(13, errors.size()); assertTrue(instance.isValidDouble("test22", "1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 13); + assertEquals(13, errors.size()); assertTrue(instance.isValidDouble("test23", "-1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 13); + assertEquals(13, errors.size()); } @Test @@ -478,7 +506,7 @@ public class ValidatorTest { assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.txt", false, errors)); assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.txt", false, errors)); assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.txt", false, errors)); - assertTrue(errors.size() == 0); + assertEquals(0, errors.size()); } @Test @@ -498,7 +526,7 @@ public class ValidatorTest { Validator instance = ESAPI.validator(); assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false)); assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors)); - assertTrue(errors.size() == 0); + assertEquals(0, errors.size()); filepath = "/ridiculous"; filename = "aspect.txt"; @@ -510,7 +538,7 @@ public class ValidatorTest { } assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false)); assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors)); - assertTrue(errors.size() == 1); + assertEquals(1, errors.size()); } @Test @@ -554,54 +582,54 @@ public class ValidatorTest { ValidationErrorList errors = new ValidationErrorList(); assertTrue(instance.isValidInput("test1", "jeff.williams@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==0); + assertEquals(0, errors.size()); assertFalse(instance.isValidInput("test2", "jeff.williams@@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); assertFalse(instance.isValidInput("test3", "jeff.williams@aspectsecurity", "Email", 100, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertTrue(instance.isValidInput("test4", "jeff.wil'liams@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertTrue(instance.isValidInput("test5", "jeff.wil''liams@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertTrue(instance.isValidInput("test6", "123.168.100.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertTrue(instance.isValidInput("test7", "192.168.1.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertFalse(instance.isValidInput("test8", "..168.1.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==3); + assertEquals(3, errors.size()); assertFalse(instance.isValidInput("test9", "10.x.1.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); assertTrue(instance.isValidInput("test10", "http://www.aspectsecurity.com", "URL", 100, false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); // This is getting flipped to true because it is no longer the validator regex's job to enforce URL structure. assertTrue(instance.isValidInput("test11", "http:///www.aspectsecurity.com", "URL", 100, false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); assertFalse(instance.isValidInput("test12", "http://www.aspect security.com", "URL", 100, false, errors)); - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); assertTrue(instance.isValidInput("test13", "078-05-1120", "SSN", 100, false, errors)); - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); assertTrue(instance.isValidInput("test14", "078 05 1120", "SSN", 100, false, errors)); - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); assertTrue(instance.isValidInput("test15", "078051120", "SSN", 100, false, errors)); - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); assertFalse(instance.isValidInput("test16", "987-65-4320", "SSN", 100, false, errors)); - assertTrue(errors.size()==6); + assertEquals(6, errors.size()); assertFalse(instance.isValidInput("test17", "000-00-0000", "SSN", 100, false, errors)); - assertTrue(errors.size()==7); + assertEquals(7, errors.size()); assertFalse(instance.isValidInput("test18", "(555) 555-5555", "SSN", 100, false, errors)); - assertTrue(errors.size()==8); + assertEquals(8, errors.size()); assertFalse(instance.isValidInput("test19", "test", "SSN", 100, false, errors)); - assertTrue(errors.size()==9); + assertEquals(9, errors.size()); assertTrue(instance.isValidInput("test20", "jeffWILLIAMS123", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==9); + assertEquals(9, errors.size()); assertTrue(instance.isValidInput("test21", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==9); + assertEquals(9, errors.size()); // Removed per Issue 116 - The '*' character is valid as a parameter character // assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false)); assertFalse(instance.isValidInput("test22", "jeff^WILLIAMS", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==10); + assertEquals(10, errors.size()); assertFalse(instance.isValidInput("test23", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==11); + assertEquals(11, errors.size()); assertTrue(instance.isValidInput("test", null, "Email", 100, true, errors)); assertFalse(instance.isValidInput("test", null, "Email", 100, false, errors)); @@ -644,56 +672,56 @@ public class ValidatorTest { ValidationErrorList errors = new ValidationErrorList(); //testing negative range assertFalse(instance.isValidInteger("test1", "-4", 1, 10, false, errors)); - assertTrue(errors.size() == 1); + assertEquals(1, errors.size()); assertTrue(instance.isValidInteger("test2", "-4", -10, 10, false, errors)); - assertTrue(errors.size() == 1); + assertEquals(1, errors.size()); //testing null value assertTrue(instance.isValidInteger("test3", null, -10, 10, true, errors)); - assertTrue(errors.size() == 1); + assertEquals(1, errors.size()); assertFalse(instance.isValidInteger("test4", null, -10, 10, false, errors)); - assertTrue(errors.size() == 2); + assertEquals(2, errors.size()); //testing empty string assertTrue(instance.isValidInteger("test5", "", -10, 10, true, errors)); - assertTrue(errors.size() == 2); + assertEquals(2, errors.size()); assertFalse(instance.isValidInteger("test6", "", -10, 10, false, errors)); - assertTrue(errors.size() == 3); + assertEquals(3, errors.size()); //testing improper range assertFalse(instance.isValidInteger("test7", "50", 10, -10, false, errors)); - assertTrue(errors.size() == 4); + assertEquals(4, errors.size()); //testing non-integers assertFalse(instance.isValidInteger("test8", "4.3214", -10, 10, true, errors)); - assertTrue(errors.size() == 5); + assertEquals(5, errors.size()); assertFalse(instance.isValidInteger("test9", "-1.65", -10, 10, true, errors)); - assertTrue(errors.size() == 6); + assertEquals(6, errors.size()); //other testing assertTrue(instance.isValidInteger("test10", "4", 1, 10, false, errors)); - assertTrue(errors.size() == 6); + assertEquals(6, errors.size()); assertTrue(instance.isValidInteger("test11", "400", 1, 10000, false, errors)); - assertTrue(errors.size() == 6); + assertEquals(6, errors.size()); assertTrue(instance.isValidInteger("test12", "400000000", 1, 400000000, false, errors)); - assertTrue(errors.size() == 6); + assertEquals(6, errors.size()); assertFalse(instance.isValidInteger("test13", "4000000000000", 1, 10000, false, errors)); - assertTrue(errors.size() == 7); + assertEquals(7, errors.size()); assertFalse(instance.isValidInteger("test14", "alsdkf", 10, 10000, false, errors)); - assertTrue(errors.size() == 8); + assertEquals(8, errors.size()); assertFalse(instance.isValidInteger("test15", "--10", 10, 10000, false, errors)); - assertTrue(errors.size() == 9); + assertEquals(9, errors.size()); assertFalse(instance.isValidInteger("test16", "14.1414234x", 10, 10000, false, errors)); - assertTrue(errors.size() == 10); + assertEquals(10, errors.size()); assertFalse(instance.isValidInteger("test17", "Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 11); + assertEquals(11, errors.size()); assertFalse(instance.isValidInteger("test18", "-Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 12); + assertEquals(12, errors.size()); assertFalse(instance.isValidInteger("test19", "NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 13); + assertEquals(13, errors.size()); assertFalse(instance.isValidInteger("test20", "-NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 14); + assertEquals(14, errors.size()); assertFalse(instance.isValidInteger("test21", "+NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 15); + assertEquals(15, errors.size()); assertFalse(instance.isValidInteger("test22", "1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 16); + assertEquals(16, errors.size()); assertFalse(instance.isValidInteger("test23", "-1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 17); + assertEquals(17, errors.size()); } @@ -709,9 +737,9 @@ public class ValidatorTest { ValidationErrorList errors = new ValidationErrorList(); assertTrue(instance.isValidListItem("test1", "one", list, errors)); - assertTrue(errors.size()==0); + assertEquals(0, errors.size()); assertFalse(instance.isValidListItem("test2", "three", list, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); } @Test @@ -751,56 +779,56 @@ public class ValidatorTest { ValidationErrorList errors = new ValidationErrorList(); //testing negative range assertFalse(instance.isValidNumber("test1", "-4", 1, 10, false, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); assertTrue(instance.isValidNumber("test2", "-4", -10, 10, false, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); //testing null value assertTrue(instance.isValidNumber("test3", null, -10, 10, true, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); assertFalse(instance.isValidNumber("test4", null, -10, 10, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); //testing empty string assertTrue(instance.isValidNumber("test5", "", -10, 10, true, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); assertFalse(instance.isValidNumber("test6", "", -10, 10, false, errors)); - assertTrue(errors.size()==3); + assertEquals(3, errors.size()); //testing improper range assertFalse(instance.isValidNumber("test7", "5", 10, -10, false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); //testing non-integers assertTrue(instance.isValidNumber("test8", "4.3214", -10, 10, true, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); assertTrue(instance.isValidNumber("test9", "-1.65", -10, 10, true, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); //other testing assertTrue(instance.isValidNumber("test10", "4", 1, 10, false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); assertTrue(instance.isValidNumber("test11", "400", 1, 10000, false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); assertTrue(instance.isValidNumber("test12", "400000000", 1, 400000000, false, errors)); - assertTrue(errors.size()==4); + assertEquals(4, errors.size()); assertFalse(instance.isValidNumber("test13", "4000000000000", 1, 10000, false, errors)); - assertTrue(errors.size()==5); + assertEquals(5, errors.size()); assertFalse(instance.isValidNumber("test14", "alsdkf", 10, 10000, false, errors)); - assertTrue(errors.size()==6); + assertEquals(6, errors.size()); assertFalse(instance.isValidNumber("test15", "--10", 10, 10000, false, errors)); - assertTrue(errors.size()==7); + assertEquals(7, errors.size()); assertFalse(instance.isValidNumber("test16", "14.1414234x", 10, 10000, false, errors)); - assertTrue(errors.size()==8); + assertEquals(8, errors.size()); assertFalse(instance.isValidNumber("test17", "Infinity", 10, 10000, false, errors)); - assertTrue(errors.size()==9); + assertEquals(9, errors.size()); assertFalse(instance.isValidNumber("test18", "-Infinity", 10, 10000, false, errors)); - assertTrue(errors.size()==10); + assertEquals(10, errors.size()); assertFalse(instance.isValidNumber("test19", "NaN", 10, 10000, false, errors)); - assertTrue(errors.size()==11); + assertEquals(11, errors.size()); assertFalse(instance.isValidNumber("test20", "-NaN", 10, 10000, false, errors)); - assertTrue(errors.size()==12); + assertEquals(12, errors.size()); assertFalse(instance.isValidNumber("test21", "+NaN", 10, 10000, false, errors)); - assertTrue(errors.size()==13); + assertEquals(13, errors.size()); assertTrue(instance.isValidNumber("test22", "1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size()==13); + assertEquals(13, errors.size()); assertTrue(instance.isValidNumber("test23", "-1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size()==13); + assertEquals(13, errors.size()); } @Test @@ -824,17 +852,17 @@ public class ValidatorTest { ValidationErrorList errors = new ValidationErrorList(); assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames,errors)); - assertTrue(errors.size()==0); + assertEquals(0, errors.size()); request.addParameter("p4", "value"); request.addParameter("p5", "value"); request.addParameter("p6", "value"); assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors)); - assertTrue(errors.size()==0); + assertEquals(0, errors.size()); request.removeParameter("p1"); assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors)); - assertTrue(errors.size() ==1); + assertEquals(1, errors.size()); } @Test @@ -849,19 +877,19 @@ public class ValidatorTest { ValidationErrorList errors = new ValidationErrorList(); assertTrue(instance.isValidPrintable("name1", "abcDEF", 100, false, errors)); - assertTrue(errors.size()==0); + assertEquals(0, errors.size()); assertTrue(instance.isValidPrintable("name2", "!@#R()*$;><()", 100, false, errors)); - assertTrue(errors.size()==0); + assertEquals(0, errors.size()); assertFalse(instance.isValidPrintable("name3", chars, 100, false, errors)); - assertTrue(errors.size()==1); + assertEquals(1, errors.size()); assertFalse(instance.isValidPrintable("name4", "%08", 100, false, errors)); - assertTrue(errors.size()==2); + assertEquals(2, errors.size()); } @Test public void testIsValidRedirectLocation() { - // isValidRedirectLocation(String, String, boolean) + // TODO - isValidRedirectLocation(String, String, boolean) } // Test split out and moved to HTMLValidationRuleLogsTest.java & HTMLValidationRuleThrowsTest.java @@ -1011,7 +1039,7 @@ public class ValidatorTest { Cookie[] cookies = safeRequest.getCookies(); assertEquals(cookies[0].getValue(), request.getCookies()[0].getValue()); assertEquals(cookies[1].getName(), request.getCookies()[2].getName()); - assertTrue(cookies.length == 2); + assertEquals(2, cookies.length); } @Test @@ -1131,4 +1159,3 @@ public class ValidatorTest { assertFalse(isValid); } } - diff --git a/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java index 2d489e793be90817c0f5bfde71c2df11fd3c87ba..b10c3d40b3bfc1f18dab4dc49254933746bcb672 100644 --- a/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java +++ b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java @@ -22,12 +22,14 @@ 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.IntrusionException; import org.owasp.esapi.errors.ValidationException; import org.owasp.esapi.filters.SecurityWrapperRequest; import org.owasp.esapi.reference.validation.HTMLValidationRule; import org.junit.Test; import org.junit.Before; +import org.junit.Ignore; import org.junit.After; import org.junit.Rule; import org.junit.rules.ExpectedException; @@ -125,6 +127,49 @@ public class HTMLValidationRuleCleanTest { // assertEquals("", result4); } + // FIXME: Change the method name to reflect the CVE once we have a number for this. + // Test to confirm that CVE-2022-xxxxx (TBD) is fixed. The cause of this was + // from a subtle botched regex for 'onsiteURL' in all the versions of + // antsamy-esapi.xml that had been there as far back as ESAPI 1.4! + // + // This TBD CVE should arguably get the same CVSSv3 store as the AntiSamy + // CVE-2021-35043 as the are very similar. + // + // Updated: Requested CVE from GitHub CNA on 4/23/2022. See + // https://github.com/ESAPI/esapi-java-legacy/security/advisories/GHSA-q77q-vx4q-xx6q + // (Which may not be published yet, but is remediated. Waiting on CVE ID to publish.) + @Test + public void testJavaScriptURL() throws Exception { + System.out.println("testJavaScriptURL"); + + String expectedSafeText = "This is safe from XSS. Trust us!"; + String badVoodoo = "<a href=\"javascript:alert(1)\">" + expectedSafeText + "</a>"; + Validator instance = ESAPI.validator(); + ValidationErrorList errorList = new ValidationErrorList(); + String result = instance.getValidSafeHTML("test", badVoodoo, 100, false, errorList); + assertEquals( expectedSafeText, result ); + } + + // To confirm fix for CVE-2021-35043 in AntiSamy 1.6.5 and later. Actually, + // it was never really "broken" in ESAPI's "default configuration" because it is + // triggers an Intrusion Detection when it is checking the canonicalization + // and the ':' trips it up, that that's pretty much irrelevant given + // the (TBD) CVE mented in the previous test case. + // + // Note: This test assumes a standard default ESAPI.properties file. In + // particular, the normal canonicalization has to be enabled. + public void testAntiSamyCVE_2021_35043Fixed() { + System.out.println("testAntiSamyCVE_2021_35043Fixed"); + + String expectedSafeText = "This is safe from XSS. Trust us!"; + + // Translates to '<a href="javascript:x=1,alert("boom")". + String badVoodoo = "<a href=\"javascript:alert(1)>" + expectedSafeText + "</a>"; + Validator instance = ESAPI.validator(); + // ValidationErrorList errorList = new ValidationErrorList(); + boolean result = instance.isValidSafeHTML("CVE-2021-35043", badVoodoo, 200, false); + assertTrue( result ); + } @Test public void testIsValidSafeHTML() { @@ -153,4 +198,62 @@ public class HTMLValidationRuleCleanTest { assertTrue(errors.size() == 0); } + + @Test + public void testAntiSamyRegressionCDATAWithJavascriptURL() throws Exception { + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + String input = "<style/>b<![cdata[</style><a href=javascript:alert(1)>test"; + assertTrue(instance.isValidSafeHTML("test8", input, 100, false, errors)); + String expected = "b</style><a href=javascript:alert(1)>test"; + String output = instance.getValidSafeHTML("javascript Link", input, 250, false); + assertEquals(expected, output); + assertTrue(errors.size() == 0); + } + + @Test + public void testScriptTagAfterStyleClosing() throws Exception { + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + String input = "<select<style/>W<xmp<script>alert(1)</script>"; + assertTrue(instance.isValidSafeHTML("test9", input, 100, false, errors)); + String expected = "W<script>alert(1)</script>"; + String output = instance.getValidSafeHTML("escaping style tag attack with script tag", input, 250, false); + assertEquals(expected, output); + assertTrue(errors.size() == 0); + } + + @Test + public void testOnfocusAfterStyleClosing() throws Exception { + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + String input = "<select<style/>k<input<</>input/onfocus=alert(1)>"; + assertTrue(instance.isValidSafeHTML("test10", input, 100, false, errors)); + String expected = "k<input/onfocus=alert(1)>"; // Suspicious? Doesn't agree w/ AntiSamy test. + String output = instance.getValidSafeHTML("escaping style tag attack with onfocus attribute", input, 250, false); + 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 = "<!--><?a/"; + assertTrue(instance.isValidSafeHTML("test11", input, 100, false, errors)); // Safe bc "" gets returned!!! + + String expectEmpty = ""; + String output = instance.getValidSafeHTML("escaping style tag attack", input, 250, false); + assertEquals(expectEmpty, output); // Because AntiSamy's CleanResults.getCleanHTML() should throw and is caught. + assertTrue(errors.size() == 0); + } } diff --git a/src/test/resources/antisamy-esapi-CP.xml b/src/test/resources/antisamy-esapi-CP.xml index 4eb23cdfe40850c83f5a9ca9738927a658cfc81e..ee1aa6b4de8a4c9684e449080510d3820bc91b6a 100644 --- a/src/test/resources/antisamy-esapi-CP.xml +++ b/src/test/resources/antisamy-esapi-CP.xml @@ -31,9 +31,9 @@ Slashdot allowed tags taken from "Reply" page: space characters. --> - <regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' --> - <regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/> - <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/> + <regexp name="htmlTitle" value="[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*"/> + <regexp name="onsiteURL" value="^(?!//)(?![\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*(&colon))[\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*"/> + <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[\p{L}\p{N}\p{Zs}\.\#@\$%\+&;:\-_~,\?=/!\(\)]*(\s)*"/> </common-regexps> diff --git a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties index 402ea806c9f646f6f5567777e14f33448019b194..5f10329c68fb98a32ddb5aad0389d95d3233a3f5 100644 --- a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties @@ -259,9 +259,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). # # IMPORTANT NOTE: In the official ESAPI.properties we do *NOT* include ECB # here as this is an extremely weak mode. However, we *must* @@ -284,29 +281,26 @@ Encryptor.EncryptionKeyLength=128 # Min key length - to support testing with 2TDEA Encryptor.MinEncryptionKeyLength=112 -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV). -# (All cipher modes except ECB require an IV.) There are two choices: we can either -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While -# the IV does not need to be hidden from adversaries, it is important that the -# adversary not be allowed to choose it. Also, random IVs are generally much more -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes -# such as CFB and OFB use a different IV for each encryption with a given key so -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and -# uncomment the Encryptor.fixedIV. -# -# Valid values: random|fixed|specified 'specified' not yet implemented; planned for 2.1 +# Because 2.x uses CBC mode by default, it requires an initialization vector (IV). +# (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.) -# -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 @@ -468,7 +462,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}$ Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ diff --git a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties index 322b0f5f4dc37812c6256d1c4b99c4ad14f0507d..74e645a20424941002851e3ce9e709bc28637002 100644 --- a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties @@ -259,9 +259,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). # # IMPORTANT NOTE: In the official ESAPI.properties we do *NOT* include ECB # here as this is an extremely weak mode. However, we *must* @@ -285,29 +282,26 @@ Encryptor.EncryptionKeyLength=128 # Min key length - to support testing with 2TDEA Encryptor.MinEncryptionKeyLength=112 -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV). -# (All cipher modes except ECB require an IV.) There are two choices: we can either -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While -# the IV does not need to be hidden from adversaries, it is important that the -# adversary not be allowed to choose it. Also, random IVs are generally much more -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes -# such as CFB and OFB use a different IV for each encryption with a given key so -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and -# uncomment the Encryptor.fixedIV. -# -# Valid values: random|fixed|specified 'specified' not yet implemented; planned for 2.1 +# Because 2.x uses CBC mode by default, it requires an initialization vector (IV). +# (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.) -# -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 @@ -469,7 +463,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}$ Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ diff --git a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties index 1a565c41ce9943603c90279b0711a7fc36e1bfd3..4b0a8a33d83d40ee461128292ee4aa68a21dfa16 100644 --- a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties @@ -258,9 +258,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). # # IMPORTANT NOTE: In the official ESAPI.properties we do *NOT* include ECB # here as this is an extremely weak mode. However, we *must* @@ -283,29 +280,26 @@ Encryptor.EncryptionKeyLength=128 # Min key length - to support testing with 2TDEA Encryptor.MinEncryptionKeyLength=112 -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV). -# (All cipher modes except ECB require an IV.) There are two choices: we can either -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While -# the IV does not need to be hidden from adversaries, it is important that the -# adversary not be allowed to choose it. Also, random IVs are generally much more -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes -# such as CFB and OFB use a different IV for each encryption with a given key so -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and -# uncomment the Encryptor.fixedIV. -# -# Valid values: random|fixed|specified 'specified' not yet implemented; planned for 2.1 +# Because 2.x uses CBC mode by default, it requires an initialization vector (IV). +# (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.) -# -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 @@ -467,7 +461,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}$ Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ diff --git a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties index bbf49c6d3e87eb9f9cfacaab838dfd3f86095591..462d04721547c16a0d35e71f48f9fa9db37e5edf 100644 --- a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties @@ -258,9 +258,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). # # IMPORTANT NOTE: In the official ESAPI.properties we do *NOT* include ECB # here as this is an extremely weak mode. However, we *must* @@ -283,29 +280,26 @@ Encryptor.EncryptionKeyLength=128 # Min key length - to support testing with 2TDEA Encryptor.MinEncryptionKeyLength=112 -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV). -# (All cipher modes except ECB require an IV.) There are two choices: we can either -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While -# the IV does not need to be hidden from adversaries, it is important that the -# adversary not be allowed to choose it. Also, random IVs are generally much more -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes -# such as CFB and OFB use a different IV for each encryption with a given key so -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and -# uncomment the Encryptor.fixedIV. -# -# Valid values: random|fixed|specified 'specified' not yet implemented; planned for 2.1 +# Because 2.x uses CBC mode by default, it requires an initialization vector (IV). +# (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.) -# -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 @@ -467,7 +461,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}$ Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index f3d7b46f13ed1da11187949fab237bcdf1156944..29bc7b3dd2450dbf81e19925f6a45f5a8fa7e6f4 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -239,9 +239,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). # # IMPORTANT NOTE: In the official ESAPI.properties we do *NOT* include ECB # here as this is an extremely weak mode. However, we *must* @@ -275,37 +272,26 @@ Encryptor.EncryptionKeyLength=128 Encryptor.MinEncryptionKeyLength=112 # 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 @@ -498,7 +484,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()\\-=\\*\\.\\?;,+\\/:&_ ]*$ diff --git a/src/test/resources/esapi/antisamy-esapi.xml b/src/test/resources/esapi/antisamy-esapi.xml index 4eb23cdfe40850c83f5a9ca9738927a658cfc81e..b6edfb3cd1c34d90611b92b25a723d16c5f08c04 100644 --- a/src/test/resources/esapi/antisamy-esapi.xml +++ b/src/test/resources/esapi/antisamy-esapi.xml @@ -31,9 +31,9 @@ Slashdot allowed tags taken from "Reply" page: space characters. --> - <regexp name="htmlTitle" value="[a-zA-Z0-9\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' --> - <regexp name="onsiteURL" value="([\w\\/\.\?=&;\#-~]+|\#(\w)+)"/> - <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@#$%&;:,\?=/\+!]*(\s)*"/> + <regexp name="htmlTitle" value="[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*"/> + <regexp name="onsiteURL" value="^(?!//)(?![\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*(&colon))[\p{L}\p{N}\\\.\#@\$%\+&;\-_~,\?=/!]*"/> + <regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[\p{L}\p{N}\p{Zs}\.\#@\$%\+&;:\-_~,\?=/!\(\)]*(\s)*"/> </common-regexps> @@ -168,6 +168,4 @@ Slashdot allowed tags taken from "Reply" page: <css-rules> </css-rules> - - </anti-samy-rules> diff --git a/suppressions.xml b/suppressions.xml index b999137881e78ff27a350047d959caf689ffa696..a51a81660ab8c2e7347fc95c44028a91dea8ebcf 100644 --- a/suppressions.xml +++ b/suppressions.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- OWASP Dependency Check suppression file for ESAPI. --> -<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.2.xsd"> +<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <suppress> <notes><![CDATA[ This suppresses CVE-2019-17571 for the log4j-1.2.17.jar dependency. ESAPI does @@ -37,4 +37,86 @@ <cpe>cpe:/a:apache:log4j</cpe> <cve>CVE-2020-9488</cve> </suppress> + <suppress> + <notes><![CDATA[ + This suppresses CVE-2021-4104 for the log4j-1.2.17.jar dependency. ESAPI's + default configuration uses ConsoleAppender rather than JMSAppender and + thus does not use Log4J 1 in a manner that makes it exploitable. ESAPI is unable to + eliminate the dependency completely because our our deprecation policy. + + For further details, please see: + https://nvd.nist.gov/vuln/detail/CVE-2021-4104 and + the ESAPI security advisory #6, "documentation/ESAPI-security-bulletin6.pdf", which + provides a detailed analysis of this issue in ESAPI. + ]]></notes> + <gav regex="true">^log4j:log4j:1\.2\.17$</gav> + <cpe>cpe:/a:apache:log4j</cpe> + <cve>CVE-2021-4104</cve> + </suppress> + <suppress> + <notes><![CDATA[ + This suppresses CVE-2022-23305 for the log4j-1.2.17.jar dependency. ESAPI's + default configuration uses ConsoleAppender rather than JDBCAppender and + thus does not use Log4J 1 in a manner that makes it exploitable. ESAPI is unable to + eliminate the dependency completely because our our deprecation policy. + + For further details, please see: + https://nvd.nist.gov/vuln/detail/CVE-2022-23305 and + the ESAPI security advisory #7, "documentation/ESAPI-security-bulletin7.pdf", which + provides a detailed analysis of this issue in ESAPI. + ]]></notes> + <gav regex="true">^log4j:log4j:1\.2\.17$</gav> + <cpe>cpe:/a:apache:log4j</cpe> + <cve>CVE-2022-23305</cve> + </suppress> +<!-- +java-8 Integration - content required for successful owasp dependency-check execution +MISSING Security Bulletin content! + + <suppress> + <notes><![CDATA[ + This suppresses CVE-2022-23307 for the log4j-1.2.17.jar dependency. ESAPI's + default configuration uses ConsoleAppender rather than Chainsaw and + thus does not use Log4J 1 in a manner that makes it exploitable. ESAPI is unable to + eliminate the dependency completely because our our deprecation policy. + + For further details, please see: + https://nvd.nist.gov/vuln/detail/CVE-2022-23307 and + +-> NEEDS BULLETIN REFERENCE + + ]]></notes> + <gav regex="true">^log4j:log4j:1\.2\.17$</gav> + <cpe>cpe:/a:apache:log4j</cpe> + <cve>CVE-2022-23307</cve> + </suppress> + <suppress> + <notes><![CDATA[ + This suppresses CVE-2022-23302 for the log4j-1.2.17.jar dependency. ESAPI's + default configuration uses ConsoleAppender rather than JMSAppender and + thus does not use Log4J 1 in a manner that makes it exploitable. ESAPI is unable to + eliminate the dependency completely because our our deprecation policy. + By virtue of not using a JMSAppender, the exploitable nature of the JMSSink implementation + referenced by this CVE is also mitigated. + + For further details, please see: + https://nvd.nist.gov/vuln/detail/CVE-2022-23302 +-> NEEDS BULLETIN REFERENCE + + ]]></notes> + <gav regex="true">^log4j:log4j:1\.2\.17$</gav> + <cpe>cpe:/a:apache:log4j</cpe> + <cve>CVE-2022-23302</cve> + </suppress> +--> + <suppress> + <notes><![CDATA[ + ESAPI does not use this jar directly. It is a transitive dependency of AntiSamy and (as per Dave Wichers on + the AntiSamy team), it does not impact AntiSamy, and therefore does not impact ESAPI. + + file name: batik-i18n-1.14.jar + ]]></notes> + <packageUrl regex="true">^pkg:maven/org\.apache\.xmlgraphics/batik\-i18n@.*$</packageUrl> + <cve>CVE-2020-7791</cve> + </suppress> </suppressions> diff --git a/versionRuleset.xml b/versionRuleset.xml new file mode 100644 index 0000000000000000000000000000000000000000..4edec3360d276b8c5a19e56674759dfdbdbe827e --- /dev/null +++ b/versionRuleset.xml @@ -0,0 +1,55 @@ +<ruleset comparisonMethod="maven" + xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0 https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd"> +<rules> + <rule groupId="*" artifactId="*" comparisonMethod="maven"> + <!-- + This regex is intended to restrict version updates to the last stable releases of dependencies and plugins. + + Any version that does not contain the stable maven-plugin version naming scheme (EG "-M7"), + but does contain letters a-zA-Z (and optional digits after) + --> + <ignoreVersions> + <ignoreVersion type="regex">^(?!.[\s\S]*-M\d*).[\s\S]*.*[a-zA-Z]\d.*$</ignoreVersion> + </ignoreVersions> + </rule> + <!-- + xml-apis has a screwy version structure where the 2.x versions are older than the 1.x versions (check the dates in maven!). + we are explicitly ignoring all of the 2.x version lineage of the project. + --> + <rule groupId="xml-apis" artifactId="xml-apis" comparisonMethod="maven"> + <ignoreVersions> + <ignoreVersion type="regex">2.*</ignoreVersion> + </ignoreVersions> + </rule> + <!-- + Mockito is bound to v.3.x until powermock gets updated + Prevent auto-update of mockito based on that expectation + + https://github.com/powermock/powermock/issues/1109 + --> + <rule groupId="org.mockito" artifactId="mockito-core" comparisonMethod="maven"> + <ignoreVersions> + <ignoreVersion type="regex">^0{0,1}[4-9].*</ignoreVersion> + </ignoreVersions> + </rule> + <rule groupId="javax.servlet" artifactId="javax.servlet-api" comparisonMethod="maven"> + <ignoreVersions> + <ignoreVersion type="regex">^0{0,1}[4-9].*</ignoreVersion> + </ignoreVersions> + </rule> + </rules> +</ruleset> +<!-- +########################### +RE-USABLE REGEX REFERENCES +########################### + Keep in mind that regexes are used as exclusions: + Anything that *DOES NOT MATCH IS VALID FOR UPDATE* + +##### REGEX ############## +^0{0,1}[4-9].* +########################### +Occassionally we need to couple to a major version of an artifact. + This REGEX is an example of how to tie to the latest artifact of major versions 0,1,2,or 3 + -->