This is an automated email from the ASF dual-hosted git repository. markap14 pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push: new 2ea4838157 NIFI-12764 Removed Commons Codec and Lang3 from security-utils (#8380) 2ea4838157 is described below commit 2ea4838157748de8b597357ac7b7af4ff504d61e Author: David Handermann <exceptionfact...@apache.org> AuthorDate: Fri Feb 9 15:00:06 2024 -0600 NIFI-12764 Removed Commons Codec and Lang3 from security-utils (#8380) --- nifi-commons/nifi-security-utils/pom.xml | 8 ------ .../apache/nifi/security/util/KeyStoreUtils.java | 27 ++++++++++-------- .../security/util/StandardTlsConfiguration.java | 17 ++++++----- .../nifi/security/util/crypto/HashAlgorithm.java | 13 ++------- .../nifi/security/util/crypto/HashService.java | 33 ++++++++++++++++------ .../nifi/security/util/KeyStoreUtilsTest.java | 4 +-- .../nifi/security/util/SslSocketFactoryTest.java | 7 +++-- .../nifi/security/util/crypto/HashServiceTest.java | 15 ++++------ .../nifi-web/nifi-web-security/pom.xml | 6 ++++ nifi-system-tests/nifi-system-test-suite/pom.xml | 4 +++ 10 files changed, 72 insertions(+), 62 deletions(-) diff --git a/nifi-commons/nifi-security-utils/pom.xml b/nifi-commons/nifi-security-utils/pom.xml index 69f93ddcb3..b9d1e66a85 100644 --- a/nifi-commons/nifi-security-utils/pom.xml +++ b/nifi-commons/nifi-security-utils/pom.xml @@ -44,14 +44,6 @@ <artifactId>nifi-security-cert-builder</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - </dependency> - <dependency> - <groupId>commons-codec</groupId> - <artifactId>commons-codec</artifactId> - </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java index 4b541c0ce6..34e8c5e057 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java @@ -41,6 +41,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HexFormat; import java.util.List; import java.util.Map; import java.util.Optional; @@ -48,8 +49,6 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; import javax.security.auth.x500.X500Principal; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.StringUtils; import org.apache.nifi.security.cert.builder.StandardCertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; @@ -171,11 +170,11 @@ public class KeyStoreUtils { public static TlsConfiguration createTlsConfigAndNewKeystoreTruststore(final TlsConfiguration tlsConfiguration, int certDurationDays, String[] dnsSubjectAlternativeNames) throws IOException, GeneralSecurityException { final Path keyStorePath; - final String keystorePassword = StringUtils.isNotBlank(tlsConfiguration.getKeystorePassword()) ? tlsConfiguration.getKeystorePassword() : generatePassword(); + final String keystorePassword = isNotBlank(tlsConfiguration.getKeystorePassword()) ? tlsConfiguration.getKeystorePassword() : generatePassword(); final KeystoreType keystoreType = tlsConfiguration.getKeystoreType() != null ? tlsConfiguration.getKeystoreType() : KeystoreType.PKCS12; - final String keyPassword = StringUtils.isNotBlank(tlsConfiguration.getKeyPassword()) ? tlsConfiguration.getKeyPassword() : keystorePassword; + final String keyPassword = isNotBlank(tlsConfiguration.getKeyPassword()) ? tlsConfiguration.getKeyPassword() : keystorePassword; final Path trustStorePath; - final String truststorePassword = StringUtils.isNotBlank(tlsConfiguration.getTruststorePassword()) ? tlsConfiguration.getTruststorePassword() : generatePassword(); + final String truststorePassword = isNotBlank(tlsConfiguration.getTruststorePassword()) ? tlsConfiguration.getTruststorePassword() : generatePassword(); final KeystoreType truststoreType = tlsConfiguration.getTruststoreType() != null ? tlsConfiguration.getTruststoreType() : KeystoreType.PKCS12; // Create temporary Keystore file @@ -259,11 +258,11 @@ public class KeyStoreUtils { * @throws TlsException if there is a problem initializing or reading from the keystore */ public static KeyManagerFactory loadKeyManagerFactory(String keystorePath, String keystorePassword, String keyPassword, String keystoreType) throws TlsException { - if (StringUtils.isEmpty(keystorePassword)) { + if (keystorePassword == null || keystorePassword.isEmpty()) { throw new IllegalArgumentException("The keystore password cannot be null or empty"); } final char[] keystorePasswordChars = keystorePassword.toCharArray(); - final char[] keyPasswordChars = (StringUtils.isNotEmpty(keyPassword)) ? keyPassword.toCharArray() : keystorePasswordChars; + final char[] keyPasswordChars = isNotBlank(keyPassword) ? keyPassword.toCharArray() : keystorePasswordChars; KeyStore keyStore = loadKeyStore(keystorePath, keystorePasswordChars, keystoreType); return getKeyManagerFactoryFromKeyStore(keyStore, keystorePasswordChars, keyPasswordChars); } @@ -331,12 +330,12 @@ public class KeyStoreUtils { */ public static TrustManagerFactory loadTrustManagerFactory(String truststorePath, String truststorePassword, String truststoreType) throws TlsException { // Bouncy Castle PKCS12 type requires a password - if (truststoreType.equalsIgnoreCase(KeystoreType.PKCS12.getType()) && StringUtils.isBlank(truststorePassword)) { + if (truststoreType.equalsIgnoreCase(KeystoreType.PKCS12.getType()) && (truststorePassword == null || truststorePassword.isBlank())) { throw new IllegalArgumentException("A PKCS12 Truststore Type requires a password"); } // Legacy truststore passwords can be empty - final char[] truststorePasswordChars = StringUtils.isNotBlank(truststorePassword) ? truststorePassword.toCharArray() : null; + final char[] truststorePasswordChars = isNotBlank(truststorePassword) ? truststorePassword.toCharArray() : null; KeyStore trustStore = loadTrustStore(truststorePath, truststorePasswordChars, truststoreType); return getTrustManagerFactoryFromTrustStore(trustStore); } @@ -445,8 +444,8 @@ public class KeyStoreUtils { KeystoreType keystoreType = KeystoreType.PKCS12; for (final Map.Entry<KeystoreType, String> keystoreTypeEntry : KEY_STORE_EXTENSIONS.entrySet()) { - final String extension = keystoreTypeEntry.getValue(); - if (StringUtils.endsWithIgnoreCase(keystorePath, extension)) { + final String extension = keystoreTypeEntry.getValue().toLowerCase(); + if (keystorePath.endsWith(extension)) { keystoreType = keystoreTypeEntry.getKey(); break; } @@ -574,7 +573,7 @@ public class KeyStoreUtils { private static String generatePassword() { final byte[] password = new byte[PASSWORD_LENGTH]; new SecureRandom().nextBytes(password); - return Hex.encodeHexString(password); + return HexFormat.of().formatHex(password); } private static KeystoreType getKeystoreType(final String keystoreTypeName) { @@ -584,4 +583,8 @@ public class KeyStoreUtils { .findFirst(); return foundKeystoreType.orElseThrow(() -> new IllegalArgumentException(String.format("Keystore Type [%s] not found", keystoreTypeFilter))); } + + private static boolean isNotBlank(final String string) { + return string != null && !string.isBlank(); + } } diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java index 88f0bc6c91..82aee96fa5 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/StandardTlsConfiguration.java @@ -16,7 +16,6 @@ */ package org.apache.nifi.security.util; -import org.apache.commons.lang3.StringUtils; import org.apache.nifi.util.NiFiProperties; import java.io.File; @@ -267,7 +266,7 @@ public class StandardTlsConfiguration implements TlsConfiguration { */ @Override public String getFunctionalKeyPassword() { - return StringUtils.isNotBlank(keyPassword) ? keyPassword : keystorePassword; + return isNotBlank(keyPassword) ? keyPassword : keystorePassword; } /** @@ -346,7 +345,7 @@ public class StandardTlsConfiguration implements TlsConfiguration { public boolean isKeystoreValid() { if (isStoreValid(keystorePath, keystorePassword, keystoreType, StoreType.KEY_STORE)) { return true; - } else if (StringUtils.isNotBlank(keyPassword) && !keyPassword.equals(keystorePassword)) { + } else if (isNotBlank(keyPassword) && !keyPassword.equals(keystorePassword)) { return isKeystorePopulated() && KeyStoreUtils.isKeyPasswordCorrect(getFileUrl(keystorePath), keystoreType, keystorePassword.toCharArray(), getFunctionalKeyPassword().toCharArray()); @@ -464,20 +463,20 @@ public class StandardTlsConfiguration implements TlsConfiguration { } private static String maskPasswordForLog(String password) { - return StringUtils.isNotBlank(password) ? MASKED_PASSWORD_LOG : NULL_LOG; + return isNotBlank(password) ? MASKED_PASSWORD_LOG : NULL_LOG; } private boolean isAnyPopulated(String path, String password, KeystoreType type) { - return StringUtils.isNotBlank(path) || StringUtils.isNotBlank(password) || type != null; + return isNotBlank(path) || isNotBlank(password) || type != null; } private boolean isStorePopulated(final String path, final String password, final KeystoreType type, final StoreType storeType) { boolean populated; // Legacy trust stores such as JKS can be populated without a password; only check the path and type - populated = StringUtils.isNotBlank(path) && type != null; + populated = isNotBlank(path) && type != null; if (StoreType.KEY_STORE == storeType) { - populated = populated && StringUtils.isNotBlank(password); + populated = populated && isNotBlank(password); } return populated; @@ -495,6 +494,10 @@ public class StandardTlsConfiguration implements TlsConfiguration { } } + private static boolean isNotBlank(final String string) { + return string != null && !string.isBlank(); + } + private enum StoreType { KEY_STORE, TRUST_STORE diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashAlgorithm.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashAlgorithm.java index 661a0a6eda..adb996a66e 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashAlgorithm.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashAlgorithm.java @@ -18,10 +18,6 @@ package org.apache.nifi.security.util.crypto; import java.util.Arrays; import java.util.List; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - /** * Enumeration capturing information about the cryptographic hash algorithms @@ -113,12 +109,7 @@ public enum HashAlgorithm { @Override public String toString() { - final ToStringBuilder builder = new ToStringBuilder(this); - ToStringBuilder.setDefaultStyle(ToStringStyle.SHORT_PREFIX_STYLE); - builder.append("Algorithm Name", name); - builder.append("Digest Length", digestBytesLength + " bytes"); - builder.append("Description", description); - return builder.toString(); + return "HashAlgorithm[Name=%s,Digest Length=%d bytes,Description=%s".formatted(name, digestBytesLength, description); } /** @@ -137,7 +128,7 @@ public enum HashAlgorithm { if (!isStrongAlgorithm()) { sb.append(" [WARNING -- Cryptographically broken]"); } - if (StringUtils.isNotBlank(description)) { + if (description != null && !description.isBlank()) { sb.append(" ").append(description); } return sb.toString(); diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashService.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashService.java index 208829055f..336d370ecb 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashService.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/crypto/HashService.java @@ -21,11 +21,11 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HexFormat; import java.util.List; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.nifi.components.AllowableValue; import org.bouncycastle.crypto.digests.Blake2bDigest; import org.slf4j.Logger; @@ -106,9 +106,9 @@ public class HashService { } // The Blake2 algorithms are instantiated differently and rely on BouncyCastle if (algorithm.isBlake2()) { - return Hex.encodeHexString(blake2HashStreaming(algorithm, value)); + return HexFormat.of().formatHex(blake2HashStreaming(algorithm, value)); } else { - return Hex.encodeHexString(traditionalHashStreaming(algorithm, value)); + return HexFormat.of().formatHex(traditionalHashStreaming(algorithm, value)); } } @@ -122,7 +122,7 @@ public class HashService { */ public static String hashValue(HashAlgorithm algorithm, String value, Charset charset) { byte[] rawHash = hashValueRaw(algorithm, value, charset); - return Hex.encodeHexString(rawHash); + return HexFormat.of().formatHex(rawHash); } /** @@ -189,13 +189,28 @@ public class HashService { } private static byte[] traditionalHash(HashAlgorithm algorithm, byte[] value) { - return DigestUtils.getDigest(algorithm.getName()).digest(value); + return getMessageDigest(algorithm).digest(value); } private static byte[] traditionalHashStreaming(HashAlgorithm algorithm, InputStream value) throws IOException { - MessageDigest digest = DigestUtils.getDigest(algorithm.getName()); - // DigestInputStream digestInputStream = new DigestInputStream(value, digest); - return DigestUtils.digest(digest, value); + final MessageDigest messageDigest = getMessageDigest(algorithm); + + final byte[] buffer = new byte[BUFFER_SIZE]; + int read = value.read(buffer); + while (read != -1) { + messageDigest.update(buffer, 0 , read); + read = value.read(buffer); + } + + return messageDigest.digest(); + } + + private static MessageDigest getMessageDigest(final HashAlgorithm algorithm) { + try { + return MessageDigest.getInstance(algorithm.getName()); + } catch (final NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Message Digest algorithm not found", e); + } } private static byte[] blake2Hash(HashAlgorithm algorithm, byte[] value) { diff --git a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java index 68717a38d6..20b0b26ac0 100644 --- a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java +++ b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java @@ -17,7 +17,6 @@ package org.apache.nifi.security.util; -import org.apache.commons.lang3.StringUtils; import org.apache.nifi.security.cert.builder.StandardCertificateBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -56,6 +55,7 @@ public class KeyStoreUtilsTest { private static final String SECRET_KEY_ALGORITHM = "AES"; private static final String KEY_PROTECTION_ALGORITHM = "PBEWithHmacSHA256AndAES_256"; private static final String HYPHEN_SEPARATOR = "-"; + private static final String EMPTY = ""; private static KeyPair keyPair; private static X509Certificate certificate; @@ -65,7 +65,7 @@ public class KeyStoreUtilsTest { public static void generateKeysAndCertificates() throws NoSuchAlgorithmException { keyPair = KeyPairGenerator.getInstance(KEY_ALGORITHM).generateKeyPair(); certificate = new StandardCertificateBuilder(keyPair, new X500Principal(SUBJECT_DN), Duration.ofDays(DURATION_DAYS)).build(); - final byte[] encodedKey = StringUtils.remove(UUID.randomUUID().toString(), HYPHEN_SEPARATOR).getBytes(StandardCharsets.UTF_8); + final byte[] encodedKey = UUID.randomUUID().toString().replaceAll(HYPHEN_SEPARATOR, EMPTY).getBytes(StandardCharsets.UTF_8); secretKey = new SecretKeySpec(encodedKey, SECRET_KEY_ALGORITHM); } diff --git a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/SslSocketFactoryTest.java b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/SslSocketFactoryTest.java index 9400a95e99..87b39696cb 100644 --- a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/SslSocketFactoryTest.java +++ b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/SslSocketFactoryTest.java @@ -16,7 +16,6 @@ */ package org.apache.nifi.security.util; -import org.apache.nifi.util.StringUtils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -29,6 +28,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; public class SslSocketFactoryTest { + private static final String EMPTY = ""; + private static TlsConfiguration tlsConfiguration; @BeforeAll @@ -53,7 +54,7 @@ public class SslSocketFactoryTest { final TlsConfiguration customTlsConfiguration = new StandardTlsConfiguration( tlsConfiguration.getKeystorePath(), tlsConfiguration.getKeystorePassword(), - StringUtils.EMPTY, + EMPTY, tlsConfiguration.getKeystoreType(), tlsConfiguration.getTruststorePath(), tlsConfiguration.getTruststorePassword(), @@ -68,7 +69,7 @@ public class SslSocketFactoryTest { @Test public void testCreateSslContextEmptyTrustStorePasswordJks() throws TlsException { final TlsConfiguration customTlsConfiguration = new TemporaryKeyStoreBuilder() - .trustStorePassword(StringUtils.EMPTY) + .trustStorePassword(EMPTY) .trustStoreType(KeystoreType.JKS.getType()) .build(); final SSLContext sslContext = SslContextFactory.createSslContext(customTlsConfiguration); diff --git a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/crypto/HashServiceTest.java b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/crypto/HashServiceTest.java index 79318248e9..e3dd48b3e9 100644 --- a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/crypto/HashServiceTest.java +++ b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/crypto/HashServiceTest.java @@ -92,18 +92,13 @@ public class HashServiceTest { /** * This test ensures that the service properly handles UTF-16 encoded data to return it without * the Big Endian Byte Order Mark (BOM). Java treats UTF-16 encoded data without a BOM as Big Endian by default on decoding, but when <em>encoding</em>, it inserts a BE BOM in the data. - * * Examples: - * * "apachenifi" - * * * UTF-8: 0x61 0x70 0x61 0x63 0x68 0x65 0x6E 0x69 0x66 0x69 * * UTF-16: 0xFE 0xFF 0x00 0x61 0x00 0x70 0x00 0x61 0x00 0x63 0x00 0x68 0x00 0x65 0x00 0x6E 0x00 0x69 0x00 0x66 0x00 0x69 * * UTF-16LE: 0x61 0x00 0x70 0x00 0x61 0x00 0x63 0x00 0x68 0x00 0x65 0x00 0x6E 0x00 0x69 0x00 0x66 0x00 0x69 0x00 * * UTF-16BE: 0x00 0x61 0x00 0x70 0x00 0x61 0x00 0x63 0x00 0x68 0x00 0x65 0x00 0x6E 0x00 0x69 0x00 0x66 0x00 0x69 - * * The result of "UTF-16" decoding should have the 0xFE 0xFF stripped on return by encoding in UTF-16BE directly, which will not insert a BOM. - * * See also: <a href="https://unicode.org/faq/utf_bom.html#bom10">https://unicode.org/faq/utf_bom.html#bom10</a> */ @Test @@ -191,7 +186,7 @@ public class HashServiceTest { } @Test - void testShouldHashConstantValue() throws Exception { + void testShouldHashConstantValue() { // Arrange final List<HashAlgorithm> algorithms = List.of(HashAlgorithm.values()); @@ -232,7 +227,7 @@ public class HashServiceTest { } @Test - void testShouldHashEmptyValue() throws Exception { + void testShouldHashEmptyValue() { // Arrange final List<HashAlgorithm> algorithms = List.of(HashAlgorithm.values()); final String EMPTY_VALUE = ""; @@ -274,7 +269,7 @@ public class HashServiceTest { } @Test - void testShouldBuildHashAlgorithmAllowableValues() throws Exception { + void testShouldBuildHashAlgorithmAllowableValues() { // Arrange final List<HashAlgorithm> EXPECTED_ALGORITHMS = List.of(HashAlgorithm.values()); @@ -298,7 +293,7 @@ public class HashServiceTest { } @Test - void testShouldBuildCharacterSetAllowableValues() throws Exception { + void testShouldBuildCharacterSetAllowableValues() { // Arrange final List<Charset> EXPECTED_CHARACTER_SETS = Arrays.asList( StandardCharsets.US_ASCII, @@ -336,7 +331,7 @@ public class HashServiceTest { } @Test - void testShouldHashValueFromStream() throws Exception { + void testShouldHashValueFromStream() { // Arrange // No command-line md2sum tool available diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml index a94050bdc4..b8b5021920 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml @@ -183,6 +183,12 @@ </exclusion> </exclusions> </dependency> + <!-- Commons Codec required for OpenSAML --> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + <version>${org.apache.commons.codec.version}</version> + </dependency> <dependency> <groupId>org.springframework.security.kerberos</groupId> <artifactId>spring-security-kerberos-core</artifactId> diff --git a/nifi-system-tests/nifi-system-test-suite/pom.xml b/nifi-system-tests/nifi-system-test-suite/pom.xml index 742ed14c9d..61a69646c1 100644 --- a/nifi-system-tests/nifi-system-test-suite/pom.xml +++ b/nifi-system-tests/nifi-system-test-suite/pom.xml @@ -189,6 +189,10 @@ <artifactId>logback-core</artifactId> <scope>compile</scope> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-api</artifactId>