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 <[email protected]>
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 <[email protected]>
---
 .../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

Reply via email to