You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2026/08/21 20:33:20 UTC

(nifi) branch main updated: NIFI-16236 Validate versioned Parameter names and allow removal of invalid Parameters (#11579)

This is an automated email from the ASF dual-hosted git repository.

exceptionfactory 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 60319e26f85 NIFI-16236 Validate versioned Parameter names and allow removal of invalid Parameters (#11579)
60319e26f85 is described below

commit 60319e26f8549a6965d29feaf7c8103294882732
Author: skeossei <32...@users.noreply.github.com>
AuthorDate: Fri Aug 21 13:33:13 2026 -0700

    NIFI-16236 Validate versioned Parameter names and allow removal of invalid Parameters (#11579)
    
    Signed-off-by: David Handermann <ex...@apache.org>
---
 .../nifi/parameter/ParameterNameValidator.java     | 33 ++++++++++++++++++
 .../nifi/parameter/ParameterNameValidatorTest.java | 40 ++++++++++++++++++++++
 .../serialization/VersionedFlowSynchronizer.java   |  2 ++
 .../VersionedFlowSynchronizerTest.java             | 32 +++++++++++++++++
 .../nifi/web/api/ParameterContextResource.java     | 17 ++++-----
 5 files changed, 116 insertions(+), 8 deletions(-)

diff --git a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterNameValidator.java b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterNameValidator.java
new file mode 100644
index 00000000000..21bc03b268a
--- /dev/null
+++ b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterNameValidator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.parameter;
+
+import java.util.regex.Pattern;
+
+public final class ParameterNameValidator {
+    private static final Pattern VALID_PARAMETER_NAME_PATTERN = Pattern.compile("[A-Za-z0-9 ._\\-]+");
+
+    private ParameterNameValidator() {
+    }
+
+    public static void validate(final String parameterName) {
+        if (parameterName == null || !VALID_PARAMETER_NAME_PATTERN.matcher(parameterName).matches()) {
+            throw new IllegalArgumentException("Request contains an illegal Parameter Name (" + parameterName
+                    + "). Parameter names may only include letters, numbers, spaces, and the special characters .-_");
+        }
+    }
+}
diff --git a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/test/java/org/apache/nifi/parameter/ParameterNameValidatorTest.java b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/test/java/org/apache/nifi/parameter/ParameterNameValidatorTest.java
new file mode 100644
index 00000000000..5e867c6d854
--- /dev/null
+++ b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/test/java/org/apache/nifi/parameter/ParameterNameValidatorTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.parameter;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ParameterNameValidatorTest {
+    @Test
+    void testValidParameterName() {
+        assertDoesNotThrow(() -> ParameterNameValidator.validate("Parameter Name-1.0"));
+    }
+
+    @Test
+    void testInvalidParameterName() {
+        final String parameterName = "PARAMETER_{{ ENVIRONMENT }}";
+
+        final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+                () -> ParameterNameValidator.validate(parameterName));
+
+        assertTrue(exception.getMessage().contains(parameterName));
+    }
+}
diff --git a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizer.java b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizer.java
index 8036b7b7c65..3fb116e4d47 100644
--- a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizer.java
+++ b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizer.java
@@ -87,6 +87,7 @@ import org.apache.nifi.parameter.ParameterContext;
 import org.apache.nifi.parameter.ParameterContextManager;
 import org.apache.nifi.parameter.ParameterDescriptor;
 import org.apache.nifi.parameter.ParameterGroup;
+import org.apache.nifi.parameter.ParameterNameValidator;
 import org.apache.nifi.parameter.ParameterProviderConfiguration;
 import org.apache.nifi.parameter.StandardParameterProviderConfiguration;
 import org.apache.nifi.persistence.FlowConfigurationArchiveManager;
@@ -953,6 +954,7 @@ public class VersionedFlowSynchronizer implements FlowSynchronizer {
 
         final Map<String, Parameter> parameters = new HashMap<>();
         for (final VersionedParameter versioned : versionedParameterContext.getParameters()) {
+            ParameterNameValidator.validate(versioned.getName());
             final boolean provided = providerBacked || versioned.isProvided();
             final String parameterValue;
             final String rawValue = versioned.getValue();
diff --git a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizerTest.java b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizerTest.java
index 536bb24810d..d541e7bfabe 100644
--- a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizerTest.java
+++ b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/VersionedFlowSynchronizerTest.java
@@ -82,6 +82,7 @@ import java.util.concurrent.CompletableFuture;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -368,6 +369,37 @@ class VersionedFlowSynchronizerTest {
                 "Parameter value must be re-sourced from the Parameter Provider, not the corrupted serialized value or null");
     }
 
+    @Test
+    void testSyncRejectsInvalidParameterName() {
+        setRootGroup();
+        setFlowController();
+
+        final String invalidParameterName = "PARAMETER_{{ ENVIRONMENT }}";
+
+        final StandardParameterContextManager contextManager = new StandardParameterContextManager();
+        when(flowManager.getParameterContextManager()).thenReturn(contextManager);
+        doAnswer(invocation -> {
+            invocation.getArgument(0, Runnable.class).run();
+            return null;
+        }).when(flowManager).withParameterContextResolution(any());
+
+        final VersionedParameter versionedParameter = new VersionedParameter();
+        versionedParameter.setName(invalidParameterName);
+        versionedParameter.setSensitive(true);
+        versionedParameter.setValue("parameter-value");
+
+        final VersionedParameterContext versionedParameterContext = new VersionedParameterContext();
+        versionedParameterContext.setInstanceIdentifier("parameter-context-id");
+        versionedParameterContext.setName("parameter-context");
+        versionedParameterContext.setParameters(Collections.singleton(versionedParameter));
+        when(versionedDataflow.getParameterContexts()).thenReturn(List.of(versionedParameterContext));
+
+        final FlowSynchronizationException exception = assertThrows(FlowSynchronizationException.class, () ->
+                versionedFlowSynchronizer.sync(flowController, dataFlow, flowService, BundleUpdateStrategy.USE_SPECIFIED_OR_GHOST));
+        final IllegalArgumentException cause = assertInstanceOf(IllegalArgumentException.class, exception.getCause());
+        assertTrue(cause.getMessage().contains(invalidParameterName));
+    }
+
     private void setRootGroup() {
         when(flowController.getFlowManager()).thenReturn(flowManager);
         when(flowManager.getRootGroup()).thenReturn(rootGroup);
diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterContextResource.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterContextResource.java
index 87e5ac45384..5409fbb8c53 100644
--- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterContextResource.java
+++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterContextResource.java
@@ -59,6 +59,7 @@ import org.apache.nifi.cluster.protocol.NodeIdentifier;
 import org.apache.nifi.controller.ComponentNode;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.parameter.ParameterContext;
+import org.apache.nifi.parameter.ParameterNameValidator;
 import org.apache.nifi.parameter.ParameterReferencedControllerServiceData;
 import org.apache.nifi.processor.DataUnit;
 import org.apache.nifi.stream.io.MaxLengthInputStream;
@@ -121,7 +122,6 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 @Controller
@@ -129,7 +129,6 @@ import java.util.stream.Collectors;
 @Tag(name = "ParameterContexts")
 public class ParameterContextResource extends AbstractParameterResource {
     private static final Logger logger = LoggerFactory.getLogger(ParameterContextResource.class);
-    private static final Pattern VALID_PARAMETER_NAME_PATTERN = Pattern.compile("[A-Za-z0-9 ._\\-]+");
     private static final String FILENAME_HEADER = "Filename";
     private static final String CONTENT_TYPE_HEADER = "Content-Type";
     private static final String UPLOAD_CONTENT_TYPE = "application/octet-stream";
@@ -875,17 +874,19 @@ public class ParameterContextResource extends AbstractParameterResource {
     private void validateParameterNames(final ParameterContextDTO parameterContextDto) {
         if (parameterContextDto.getParameters() != null) {
             for (final ParameterEntity entity : parameterContextDto.getParameters()) {
-                final String parameterName = entity.getParameter().getName();
-                if (!isLegalParameterName(parameterName)) {
-                    throw new IllegalArgumentException("Request contains an illegal Parameter Name (" + parameterName
-                            + "). Parameter names may only include letters, numbers, spaces, and the special characters .-_");
+                final ParameterDTO parameter = entity.getParameter();
+                if (!isParameterDeletion(parameter)) {
+                    ParameterNameValidator.validate(parameter.getName());
                 }
             }
         }
     }
 
-    private boolean isLegalParameterName(final String parameterName) {
-        return VALID_PARAMETER_NAME_PATTERN.matcher(parameterName).matches();
+    private boolean isParameterDeletion(final ParameterDTO parameter) {
+        return parameter.getDescription() == null
+                && parameter.getSensitive() == null
+                && parameter.getValue() == null
+                && parameter.getReferencedAssets() == null;
     }
 
     @GET