diff options
Diffstat (limited to 'sanitizers/src/test/java/com')
14 files changed, 859 insertions, 4 deletions
diff --git a/sanitizers/src/test/java/com/example/BUILD.bazel b/sanitizers/src/test/java/com/example/BUILD.bazel index d148545a..5d2e1ca5 100644 --- a/sanitizers/src/test/java/com/example/BUILD.bazel +++ b/sanitizers/src/test/java/com/example/BUILD.bazel @@ -1,10 +1,12 @@ load("//bazel:fuzz_target.bzl", "java_fuzz_target_test") +load("//bazel:compat.bzl", "SKIP_ON_MACOS") java_fuzz_target_test( name = "ObjectInputStreamDeserialization", srcs = [ "ObjectInputStreamDeserialization.java", ], + expected_findings = ["java.lang.ExceptionInInitializerError"], target_class = "com.example.ObjectInputStreamDeserialization", ) @@ -13,10 +15,22 @@ java_fuzz_target_test( srcs = [ "ReflectiveCall.java", ], + expected_findings = ["java.lang.ExceptionInInitializerError"], target_class = "com.example.ReflectiveCall", ) java_fuzz_target_test( + name = "LibraryLoad", + srcs = [ + "LibraryLoad.java", + ], + target_class = "com.example.LibraryLoad", + # loading of native libraries is very slow on macos, + # especially using Java 17 + target_compatible_with = SKIP_ON_MACOS, +) + +java_fuzz_target_test( name = "ExpressionLanguageInjection", srcs = [ "ExpressionLanguageInjection.java", @@ -27,6 +41,100 @@ java_fuzz_target_test( "@maven//:javax_el_javax_el_api", "@maven//:javax_validation_validation_api", "@maven//:javax_xml_bind_jaxb_api", + "@maven//:org_glassfish_javax_el", "@maven//:org_hibernate_hibernate_validator", ], ) + +java_fuzz_target_test( + name = "OsCommandInjectionProcessBuilder", + srcs = [ + "OsCommandInjectionProcessBuilder.java", + ], + target_class = "com.example.OsCommandInjectionProcessBuilder", +) + +java_fuzz_target_test( + name = "OsCommandInjectionRuntimeExec", + srcs = [ + "OsCommandInjectionRuntimeExec.java", + ], + target_class = "com.example.OsCommandInjectionRuntimeExec", +) + +java_fuzz_target_test( + name = "LdapSearchInjection", + srcs = [ + "LdapSearchInjection.java", + "ldap/MockInitialContextFactory.java", + "ldap/MockLdapContext.java", + ], + expected_findings = ["javax.naming.directory.InvalidSearchFilterException"], + target_class = "com.example.LdapSearchInjection", + deps = [ + "@maven//:com_unboundid_unboundid_ldapsdk", + ], +) + +java_fuzz_target_test( + name = "LdapDnInjection", + srcs = [ + "LdapDnInjection.java", + "ldap/MockInitialContextFactory.java", + "ldap/MockLdapContext.java", + ], + expected_findings = ["javax.naming.NamingException"], + target_class = "com.example.LdapDnInjection", + deps = [ + "@maven//:com_unboundid_unboundid_ldapsdk", + ], +) + +java_fuzz_target_test( + name = "RegexInsecureQuoteInjection", + srcs = ["RegexInsecureQuoteInjection.java"], + target_class = "com.example.RegexInsecureQuoteInjection", +) + +java_fuzz_target_test( + name = "RegexCanonEqInjection", + srcs = [ + "RegexCanonEqInjection.java", + ], + target_class = "com.example.RegexCanonEqInjection", +) + +java_fuzz_target_test( + name = "ClassLoaderLoadClass", + srcs = [ + "ClassLoaderLoadClass.java", + ], + expected_findings = ["java.lang.ExceptionInInitializerError"], + target_class = "com.example.ClassLoaderLoadClass", +) + +java_fuzz_target_test( + name = "RegexRoadblocks", + srcs = ["RegexRoadblocks.java"], + fuzzer_args = [ + # Limit the number of runs to verify that the regex roadblocks are + # cleared quickly. + "-runs=22000", + ], + target_class = "com.example.RegexRoadblocks", +) + +java_fuzz_target_test( + name = "SqlInjection", + srcs = [ + "SqlInjection.java", + ], + expected_findings = [ + "com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh", + "org.h2.jdbc.JdbcSQLSyntaxErrorException", + ], + target_class = "com.example.SqlInjection", + deps = [ + "@maven//:com_h2database_h2", + ], +) diff --git a/sanitizers/src/test/java/com/example/ClassLoaderLoadClass.java b/sanitizers/src/test/java/com/example/ClassLoaderLoadClass.java new file mode 100644 index 00000000..c3fa47ac --- /dev/null +++ b/sanitizers/src/test/java/com/example/ClassLoaderLoadClass.java @@ -0,0 +1,30 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.lang.reflect.InvocationTargetException; + +public class ClassLoaderLoadClass { + public static void fuzzerTestOneInput(FuzzedDataProvider data) throws InterruptedException { + String input = data.consumeRemainingAsAsciiString(); + try { + // create an instance to trigger class initialization + ClassLoaderLoadClass.class.getClassLoader().loadClass(input).getConstructor().newInstance(); + } catch (ClassNotFoundException | InvocationTargetException | InstantiationException + | IllegalAccessException | NoSuchMethodException ignored) { + } + } +} diff --git a/sanitizers/src/test/java/com/example/LdapDnInjection.java b/sanitizers/src/test/java/com/example/LdapDnInjection.java new file mode 100644 index 00000000..911db1dc --- /dev/null +++ b/sanitizers/src/test/java/com/example/LdapDnInjection.java @@ -0,0 +1,39 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.util.Hashtable; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; + +public class LdapDnInjection { + private static InitialDirContext ctx; + + public static void fuzzerInitialize() throws NamingException { + Hashtable<String, String> env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.example.ldap.MockInitialContextFactory"); + ctx = new InitialDirContext(env); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider fuzzedDataProvider) throws Exception { + // Externally provided DN input needs to be escaped properly + String ou = fuzzedDataProvider.consumeRemainingAsString(); + String base = "ou=" + ou + ",dc=example,dc=com"; + ctx.search(base, "(&(uid=foo)(cn=bar))", new SearchControls()); + } +} diff --git a/sanitizers/src/test/java/com/example/LdapSearchInjection.java b/sanitizers/src/test/java/com/example/LdapSearchInjection.java new file mode 100644 index 00000000..b3dfee74 --- /dev/null +++ b/sanitizers/src/test/java/com/example/LdapSearchInjection.java @@ -0,0 +1,39 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.util.Hashtable; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.SearchControls; +import javax.naming.ldap.InitialLdapContext; + +public class LdapSearchInjection { + private static InitialLdapContext ctx; + + public static void fuzzerInitialize() throws NamingException { + Hashtable<String, String> env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.example.ldap.MockInitialContextFactory"); + ctx = new InitialLdapContext(env, null); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider fuzzedDataProvider) throws Exception { + // Externally provided LDAP query input needs to be escaped properly + String username = fuzzedDataProvider.consumeRemainingAsAsciiString(); + String filter = "(&(uid=" + username + ")(ou=security))"; + ctx.search("dc=example,dc=com", filter, new SearchControls()); + } +} diff --git a/sanitizers/src/test/java/com/example/LibraryLoad.java b/sanitizers/src/test/java/com/example/LibraryLoad.java new file mode 100644 index 00000000..81411767 --- /dev/null +++ b/sanitizers/src/test/java/com/example/LibraryLoad.java @@ -0,0 +1,29 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +public class LibraryLoad { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsAsciiString(); + + try { + System.loadLibrary(input); + } catch (SecurityException | UnsatisfiedLinkError | NullPointerException + | IllegalArgumentException ignored) { + } + } +} diff --git a/sanitizers/src/test/java/com/example/OsCommandInjectionProcessBuilder.java b/sanitizers/src/test/java/com/example/OsCommandInjectionProcessBuilder.java new file mode 100644 index 00000000..f5d52782 --- /dev/null +++ b/sanitizers/src/test/java/com/example/OsCommandInjectionProcessBuilder.java @@ -0,0 +1,35 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.util.concurrent.TimeUnit; + +public class OsCommandInjectionProcessBuilder { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsAsciiString(); + try { + ProcessBuilder processBuilder = new ProcessBuilder(input); + processBuilder.environment().clear(); + Process process = processBuilder.start(); + // This should be way faster, but we have to wait until the call is done + if (!process.waitFor(10, TimeUnit.MILLISECONDS)) { + process.destroyForcibly(); + } + } catch (Exception ignored) { + // Ignore execution and setup exceptions + } + } +} diff --git a/sanitizers/src/test/java/com/example/OsCommandInjectionRuntimeExec.java b/sanitizers/src/test/java/com/example/OsCommandInjectionRuntimeExec.java new file mode 100644 index 00000000..c620a751 --- /dev/null +++ b/sanitizers/src/test/java/com/example/OsCommandInjectionRuntimeExec.java @@ -0,0 +1,35 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import static java.lang.Runtime.getRuntime; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.util.concurrent.TimeUnit; + +public class OsCommandInjectionRuntimeExec { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsAsciiString(); + try { + Process process = getRuntime().exec(input, new String[] {}); + // This should be way faster, but we have to wait until the call is done + if (!process.waitFor(10, TimeUnit.MILLISECONDS)) { + process.destroyForcibly(); + } + } catch (Exception ignored) { + // Ignore execution and setup exceptions + } + } +} diff --git a/sanitizers/src/test/java/com/example/ReflectiveCall.java b/sanitizers/src/test/java/com/example/ReflectiveCall.java index 7f85e486..e6b62b45 100644 --- a/sanitizers/src/test/java/com/example/ReflectiveCall.java +++ b/sanitizers/src/test/java/com/example/ReflectiveCall.java @@ -15,7 +15,6 @@ package com.example; import com.code_intelligence.jazzer.api.FuzzedDataProvider; -import java.lang.reflect.InvocationTargetException; public class ReflectiveCall { public static void fuzzerTestOneInput(FuzzedDataProvider data) { @@ -23,9 +22,8 @@ public class ReflectiveCall { if (input.startsWith("@")) { String className = input.substring(1); try { - Class.forName(className).getConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException - | NoSuchMethodException | ClassNotFoundException ignored) { + Class.forName(className); + } catch (ClassNotFoundException ignored) { } } } diff --git a/sanitizers/src/test/java/com/example/RegexCanonEqInjection.java b/sanitizers/src/test/java/com/example/RegexCanonEqInjection.java new file mode 100644 index 00000000..e2d0b722 --- /dev/null +++ b/sanitizers/src/test/java/com/example/RegexCanonEqInjection.java @@ -0,0 +1,41 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class RegexCanonEqInjection { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + try { + Pattern.compile(Pattern.quote(input), Pattern.CANON_EQ); + } catch (PatternSyntaxException ignored) { + } catch (IllegalArgumentException ignored) { + // "[媼" generates an IllegalArgumentException but only on Windows using + // Java 8. We ignore this for now. + // + // java.lang.IllegalArgumentException + // at java.lang.AbstractStringBuilder.appendCodePoint(AbstractStringBuilder.java:800) + // at java.lang.StringBuilder.appendCodePoint(StringBuilder.java:240) + // at java.util.regex.Pattern.normalizeCharClass(Pattern.java:1430) + // at java.util.regex.Pattern.normalize(Pattern.java:1396) + // at java.util.regex.Pattern.compile(Pattern.java:1665) + // at java.util.regex.Pattern.<init>(Pattern.java:1352) + // at java.util.regex.Pattern.compile(Pattern.java:1054) + } + } +} diff --git a/sanitizers/src/test/java/com/example/RegexInsecureQuoteInjection.java b/sanitizers/src/test/java/com/example/RegexInsecureQuoteInjection.java new file mode 100644 index 00000000..a548cfb2 --- /dev/null +++ b/sanitizers/src/test/java/com/example/RegexInsecureQuoteInjection.java @@ -0,0 +1,29 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class RegexInsecureQuoteInjection { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + String input = data.consumeRemainingAsString(); + try { + Pattern.matches("\\Q" + input + "\\E", "foobar"); + } catch (PatternSyntaxException ignored) { + } + } +} diff --git a/sanitizers/src/test/java/com/example/RegexRoadblocks.java b/sanitizers/src/test/java/com/example/RegexRoadblocks.java new file mode 100644 index 00000000..21986e3d --- /dev/null +++ b/sanitizers/src/test/java/com/example/RegexRoadblocks.java @@ -0,0 +1,89 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow; +import java.util.regex.Pattern; + +public class RegexRoadblocks { + // We accept arbitrary suffixes but not prefixes for the following reasons: + // 1. The fuzzer will take much longer to match the exact length of the input than to satisfy the + // compare checks, which is what we really want to test. + // 2. Accepting arbitrary prefixes could lead to tests passing purely due to ToC entries being + // emitted in arbitrary positions, but we want to ensure that compares are correctly reported + // including position hints. + private static final Pattern LITERAL = Pattern.compile("foobarbaz.*"); + private static final Pattern QUOTED_LITERAL = Pattern.compile(Pattern.quote("jazzer_is_cool.*")); + private static final Pattern CASE_INSENSITIVE_LITERAL = + Pattern.compile("JaZzER!.*", Pattern.CASE_INSENSITIVE); + private static final Pattern GROUP = Pattern.compile("(always).*"); + private static final Pattern ALTERNATIVE = Pattern.compile("(to_be|not_to_be).*"); + private static final Pattern SINGLE_LATIN1_CHAR_PROPERTY = Pattern.compile("[€].*"); + private static final Pattern MULTIPLE_LATIN1_CHAR_PROPERTY = Pattern.compile("[ẞÄ].*"); + private static final Pattern RANGE_LATIN1_CHAR_PROPERTY = Pattern.compile("[¢-¥].*"); + + private static int run = 0; + + private static boolean matchedLiteral = false; + private static boolean matchedQuotedLiteral = false; + private static boolean matchedCaseInsensitiveLiteral = false; + private static boolean matchedGroup = false; + private static boolean matchedAlternative = false; + private static boolean matchedSingleLatin1CharProperty = false; + private static boolean matchedMultipleLatin1CharProperty = false; + private static boolean matchedRangeLatin1CharProperty = false; + + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + run++; + String input = data.consumeRemainingAsString(); + + if (!matchedLiteral && LITERAL.matcher(input).matches()) { + System.out.println("Cleared LITERAL"); + matchedLiteral = true; + } else if (!matchedQuotedLiteral && QUOTED_LITERAL.matcher(input).matches()) { + System.out.println("Cleared QUOTED_LITERAL"); + matchedQuotedLiteral = true; + } else if (!matchedCaseInsensitiveLiteral + && CASE_INSENSITIVE_LITERAL.matcher(input).matches()) { + System.out.println("Cleared CASE_INSENSITIVE_LITERAL"); + matchedCaseInsensitiveLiteral = true; + } else if (!matchedGroup && GROUP.matcher(input).matches()) { + System.out.println("Cleared GROUP"); + matchedGroup = true; + } else if (!matchedAlternative && ALTERNATIVE.matcher(input).matches()) { + System.out.println("Cleared ALTERNATIVE"); + matchedAlternative = true; + } else if (!matchedSingleLatin1CharProperty + && SINGLE_LATIN1_CHAR_PROPERTY.matcher(input).matches()) { + System.out.println("Cleared SINGLE_LATIN1_CHAR_PROPERTY"); + matchedSingleLatin1CharProperty = true; + } else if (!matchedMultipleLatin1CharProperty + && MULTIPLE_LATIN1_CHAR_PROPERTY.matcher(input).matches()) { + System.out.println("Cleared MULTIPLE_LATIN1_CHAR_PROPERTY"); + matchedMultipleLatin1CharProperty = true; + } else if (!matchedRangeLatin1CharProperty + && RANGE_LATIN1_CHAR_PROPERTY.matcher(input).matches()) { + System.out.println("Cleared RANGE_LATIN1_CHAR_PROPERTY"); + matchedRangeLatin1CharProperty = true; + } + + if (matchedLiteral && matchedQuotedLiteral && matchedCaseInsensitiveLiteral && matchedGroup + && matchedAlternative && matchedSingleLatin1CharProperty + && matchedMultipleLatin1CharProperty && matchedRangeLatin1CharProperty) { + throw new FuzzerSecurityIssueLow("Fuzzer matched all regexes in " + run + " runs"); + } + } +} diff --git a/sanitizers/src/test/java/com/example/SqlInjection.java b/sanitizers/src/test/java/com/example/SqlInjection.java new file mode 100644 index 00000000..8a16b5c8 --- /dev/null +++ b/sanitizers/src/test/java/com/example/SqlInjection.java @@ -0,0 +1,41 @@ +// Copyright 2022 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import java.sql.Connection; +import java.sql.SQLException; +import org.h2.jdbcx.JdbcDataSource; + +public class SqlInjection { + static Connection conn = null; + + public static void fuzzerInitialize() throws Exception { + JdbcDataSource ds = new JdbcDataSource(); + ds.setURL("jdbc:h2:./test.db"); + conn = ds.getConnection(); + conn.createStatement().execute( + "CREATE TABLE IF NOT EXISTS pet (id IDENTITY PRIMARY KEY, name VARCHAR(50))"); + } + + static void insecureInsertUser(String userName) throws SQLException { + // Never use String.format instead of java.sql.Connection.prepareStatement ... + conn.createStatement().execute(String.format("INSERT INTO pet (name) VALUES ('%s')", userName)); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider data) throws Exception { + insecureInsertUser(data.consumeRemainingAsString()); + } +} diff --git a/sanitizers/src/test/java/com/example/ldap/MockInitialContextFactory.java b/sanitizers/src/test/java/com/example/ldap/MockInitialContextFactory.java new file mode 100644 index 00000000..b674f5c5 --- /dev/null +++ b/sanitizers/src/test/java/com/example/ldap/MockInitialContextFactory.java @@ -0,0 +1,26 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example.ldap; + +import java.util.Hashtable; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.spi.InitialContextFactory; + +public class MockInitialContextFactory implements InitialContextFactory { + public Context getInitialContext(Hashtable environment) { + return new MockLdapContext(); + } +} diff --git a/sanitizers/src/test/java/com/example/ldap/MockLdapContext.java b/sanitizers/src/test/java/com/example/ldap/MockLdapContext.java new file mode 100644 index 00000000..a51fadcd --- /dev/null +++ b/sanitizers/src/test/java/com/example/ldap/MockLdapContext.java @@ -0,0 +1,316 @@ +// Copyright 2021 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.example.ldap; + +import com.unboundid.ldap.sdk.DN; +import com.unboundid.ldap.sdk.Filter; +import com.unboundid.ldap.sdk.LDAPException; +import java.util.Hashtable; +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +/** + * Mock LdapContex implementation to test LdapInjection hook configuration. + * + * Only {@code com.example.ldap.MockLdapContext#search(java.lang.String, java.lang.String, + * javax.naming.directory.SearchControls)} is implemented to validate DN and filer query. + */ +public class MockLdapContext implements LdapContext { + @Override + public ExtendedResponse extendedOperation(ExtendedRequest request) throws NamingException { + return null; + } + + @Override + public LdapContext newInstance(Control[] requestControls) throws NamingException { + return this; + } + + @Override + public void reconnect(Control[] connCtls) throws NamingException {} + + @Override + public Control[] getConnectControls() throws NamingException { + return new Control[0]; + } + + @Override + public void setRequestControls(Control[] requestControls) throws NamingException {} + + @Override + public Control[] getRequestControls() throws NamingException { + return new Control[0]; + } + + @Override + public Control[] getResponseControls() throws NamingException { + return new Control[0]; + } + + @Override + public Attributes getAttributes(Name name) throws NamingException { + return null; + } + + @Override + public Attributes getAttributes(String name) throws NamingException { + return null; + } + + @Override + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + return null; + } + + @Override + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + return null; + } + + @Override + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException {} + + @Override + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException {} + + @Override + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException {} + + @Override + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException {} + + @Override + public void bind(Name name, Object obj, Attributes attrs) throws NamingException {} + + @Override + public void bind(String name, Object obj, Attributes attrs) throws NamingException {} + + @Override + public void rebind(Name name, Object obj, Attributes attrs) throws NamingException {} + + @Override + public void rebind(String name, Object obj, Attributes attrs) throws NamingException {} + + @Override + public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException { + return this; + } + + @Override + public DirContext createSubcontext(String name, Attributes attrs) throws NamingException { + return this; + } + + @Override + public DirContext getSchema(Name name) throws NamingException { + return this; + } + + @Override + public DirContext getSchema(String name) throws NamingException { + return this; + } + + @Override + public DirContext getSchemaClassDefinition(Name name) throws NamingException { + return this; + } + + @Override + public DirContext getSchemaClassDefinition(String name) throws NamingException { + return this; + } + + @Override + public NamingEnumeration<SearchResult> search(Name name, Attributes matchingAttributes, + String[] attributesToReturn) throws NamingException { + return null; + } + + @Override + public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes, + String[] attributesToReturn) throws NamingException { + return null; + } + + @Override + public NamingEnumeration<SearchResult> search(Name name, Attributes matchingAttributes) + throws NamingException { + return null; + } + + @Override + public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes) + throws NamingException { + return null; + } + + @Override + public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) + throws NamingException { + return null; + } + + @Override + public NamingEnumeration<SearchResult> search(String name, String filter, SearchControls cons) + throws NamingException { + // Use UnboundID LDAP to validate DN and filter + if (!DN.isValidDN(name)) { + throw new NamingException("Invalid DN " + name); + } + try { + Filter.create(filter); + } catch (LDAPException e) { + throw new InvalidSearchFilterException("Invalid search filter " + filter); + } + return null; + } + + @Override + public NamingEnumeration<SearchResult> search(Name name, String filterExpr, Object[] filterArgs, + SearchControls cons) throws NamingException { + return null; + } + + @Override + public NamingEnumeration<SearchResult> search(String name, String filterExpr, Object[] filterArgs, + SearchControls cons) throws NamingException { + return null; + } + + @Override + public Object lookup(Name name) throws NamingException { + return this; + } + + @Override + public Object lookup(String name) throws NamingException { + return this; + } + + @Override + public void bind(Name name, Object obj) throws NamingException {} + + @Override + public void bind(String name, Object obj) throws NamingException {} + + @Override + public void rebind(Name name, Object obj) throws NamingException {} + + @Override + public void rebind(String name, Object obj) throws NamingException {} + + @Override + public void unbind(Name name) throws NamingException {} + + @Override + public void unbind(String name) throws NamingException {} + + @Override + public void rename(Name oldName, Name newName) throws NamingException {} + + @Override + public void rename(String oldName, String newName) throws NamingException {} + + @Override + public NamingEnumeration<NameClassPair> list(Name name) throws NamingException { + return null; + } + + @Override + public NamingEnumeration<NameClassPair> list(String name) throws NamingException { + return null; + } + + @Override + public NamingEnumeration<Binding> listBindings(Name name) throws NamingException { + return null; + } + + @Override + public NamingEnumeration<Binding> listBindings(String name) throws NamingException { + return null; + } + + @Override + public void destroySubcontext(Name name) throws NamingException {} + + @Override + public void destroySubcontext(String name) throws NamingException {} + + @Override + public Context createSubcontext(Name name) throws NamingException { + return this; + } + + @Override + public Context createSubcontext(String name) throws NamingException { + return this; + } + + @Override + public Object lookupLink(Name name) throws NamingException { + return this; + } + + @Override + public Object lookupLink(String name) throws NamingException { + return this; + } + + @Override + public NameParser getNameParser(Name name) throws NamingException { + return null; + } + + @Override + public NameParser getNameParser(String name) throws NamingException { + return null; + } + + @Override + public Name composeName(Name name, Name prefix) throws NamingException { + return null; + } + + @Override + public String composeName(String name, String prefix) throws NamingException { + return null; + } + + @Override + public Object addToEnvironment(String propName, Object propVal) throws NamingException { + return null; + } + + @Override + public Object removeFromEnvironment(String propName) throws NamingException { + return null; + } + + @Override + public Hashtable<?, ?> getEnvironment() throws NamingException { + return null; + } + + @Override + public void close() throws NamingException {} + + @Override + public String getNameInNamespace() throws NamingException { + return null; + } +} |