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

ferdei 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 2a5fe2e809 NIFI-13412 [MiNiFi] Handle absolute paths in Sync/Resource 
C2 command
2a5fe2e809 is described below

commit 2a5fe2e809a8021a55f4ef5c54962b84b8d227af
Author: Ferenc Kis <[email protected]>
AuthorDate: Tue Jun 18 11:56:07 2024 +0200

    NIFI-13412 [MiNiFi] Handle absolute paths in Sync/Resource C2 command
    
    Signed-off-by: Ferenc Erdei <[email protected]>
    This closes #8975.
---
 .../syncresource/DefaultSyncResourceStrategy.java  |  15 +--
 .../DefaultSyncResourceStrategyTest.java           | 107 ++++++++++++++++++++-
 2 files changed, 112 insertions(+), 10 deletions(-)

diff --git 
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/main/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategy.java
 
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/main/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategy.java
index a837436ebd..7c1f497192 100644
--- 
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/main/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategy.java
+++ 
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/main/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategy.java
@@ -24,6 +24,7 @@ import static java.util.Map.entry;
 import static java.util.Optional.empty;
 import static java.util.UUID.randomUUID;
 import static java.util.function.Predicate.not;
+import static java.util.regex.Pattern.compile;
 import static 
org.apache.nifi.c2.protocol.api.C2OperationState.OperationState.FULLY_APPLIED;
 import static 
org.apache.nifi.c2.protocol.api.C2OperationState.OperationState.NOT_APPLIED;
 import static 
org.apache.nifi.c2.protocol.api.C2OperationState.OperationState.NO_OPERATION;
@@ -39,6 +40,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.Function;
+import java.util.regex.Pattern;
 import org.apache.nifi.c2.client.service.operation.SyncResourceStrategy;
 import org.apache.nifi.c2.protocol.api.C2OperationState.OperationState;
 import org.apache.nifi.c2.protocol.api.ResourceItem;
@@ -49,6 +51,7 @@ import org.slf4j.LoggerFactory;
 public class DefaultSyncResourceStrategy implements SyncResourceStrategy {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(DefaultSyncResourceStrategy.class);
+    private static final Pattern ALLOWED_RESOURCE_PATH_PATTERN = 
compile("^(?:(?:.[/\\\\])?[^~<>:\\|\\\"\\?\\*\\./\\\\]+(?:(?!(\\.\\.))[^~<>:\\|\\\"\\?\\*])*)?$");
 
     private static final Set<Entry<OperationState, OperationState>> 
SUCCESS_RESULT_PAIRS = Set.of(
         entry(NO_OPERATION, NO_OPERATION),
@@ -61,8 +64,6 @@ public class DefaultSyncResourceStrategy implements 
SyncResourceStrategy {
         entry(NOT_APPLIED, NO_OPERATION),
         entry(NOT_APPLIED, NOT_APPLIED));
 
-    private static final String CHANGE_TO_PARENT_DIR_PATH_SEGMENT = "..";
-
     private final ResourceRepository resourceRepository;
 
     public DefaultSyncResourceStrategy(ResourceRepository resourceRepository) {
@@ -115,11 +116,11 @@ public class DefaultSyncResourceStrategy implements 
SyncResourceStrategy {
     }
 
     private boolean validate(ResourceItem resourceItem) {
-        if (resourceItem.getResourcePath() != null
-            && resourceItem.getResourceType() == ASSET
-            && 
resourceItem.getResourcePath().contains(CHANGE_TO_PARENT_DIR_PATH_SEGMENT)) {
-            LOG.error("Resource path should not contain '..' path segment in 
{}", resourceItem);
-            return false;
+        if (resourceItem.getResourcePath() != null && 
resourceItem.getResourceType() == ASSET) {
+            if 
(!ALLOWED_RESOURCE_PATH_PATTERN.matcher(resourceItem.getResourcePath()).matches())
 {
+                LOG.error("Invalid resource path {}", 
resourceItem.getResourcePath());
+                return false;
+            }
         }
         return true;
     }
diff --git 
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/test/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategyTest.java
 
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/test/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategyTest.java
index c54c828c54..1ba84d747d 100644
--- 
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/test/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategyTest.java
+++ 
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/src/test/java/org/apache/nifi/minifi/c2/command/syncresource/DefaultSyncResourceStrategyTest.java
@@ -44,6 +44,7 @@ import java.util.List;
 import java.util.Optional;
 import java.util.function.BiFunction;
 import java.util.function.Function;
+import java.util.stream.Stream;
 import org.apache.nifi.c2.protocol.api.C2OperationState.OperationState;
 import org.apache.nifi.c2.protocol.api.ResourceItem;
 import org.apache.nifi.c2.protocol.api.ResourceType;
@@ -51,6 +52,9 @@ import org.apache.nifi.c2.protocol.api.ResourcesGlobalHash;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
 import org.mockito.MockedStatic;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -124,10 +128,37 @@ public class DefaultSyncResourceStrategyTest {
         }
     }
 
-    @Test
-    public void 
testAddingNewItemFailureWhenTypeIsAssetAndPathContainsDoubleDots() {
+    @ParameterizedTest
+    @MethodSource("validResourcePaths")
+    public void testAddingNewItemsSuccessWithValidResourcePath(String 
validResourcePath) {
         List<ResourceItem> c2Items = List.of(
-            resourceItem("resource1", "valid_url", "../path", ASSET)
+            resourceItem("resource1", "url1", validResourcePath, ASSET)
+        );
+        
when(mockResourceRepository.findAllResourceItems()).thenReturn(List.of());
+        
when(mockResourceRepository.saveResourcesGlobalHash(C2_GLOBAL_HASH)).thenReturn(Optional.of(C2_GLOBAL_HASH));
+        c2Items.forEach(resourceItem -> {
+            try {
+                when(mockResourceRepository.addResourceItem(eq(resourceItem), 
any())).thenReturn(Optional.of(resourceItem));
+            } catch (Exception e) {
+            }
+        });
+
+        OperationState resultState =
+            
testSyncResourceStrategy.synchronizeResourceRepository(C2_GLOBAL_HASH, c2Items, 
URL_TO_CONTENT_DOWNLOAD_FUNCTION, PREFIXING_ENRICH_FUNCTION);
+
+        assertEquals(FULLY_APPLIED, resultState);
+        try {
+            verify(mockResourceRepository, never()).deleteResourceItem(any());
+        } catch (Exception e) {
+        }
+    }
+
+
+    @ParameterizedTest
+    @MethodSource("invalidResourcePaths")
+    public void testAddingNewItemFailureWhenTypeIsAssetAndPathIsInvalid(String 
invalidResourcePath) {
+        List<ResourceItem> c2Items = List.of(
+            resourceItem("resource1", "valid_url", invalidResourcePath, ASSET)
         );
         
when(mockResourceRepository.findAllResourceItems()).thenReturn(List.of());
 
@@ -295,6 +326,76 @@ public class DefaultSyncResourceStrategyTest {
         }
     }
 
+    private static Stream<Arguments> validResourcePaths() {
+        return Stream.of(
+                null,
+                "",
+                "sub-folder",
+                "sub-folder/",
+                "sub-folder\\",
+                "./sub-folder",
+                "./sub-folder/",
+                ".\\sub-folder",
+                ".\\sub-folder\\",
+                "./sub-folder/sub-sub-folder",
+                "./sub-folder/sub-sub-folder/",
+                ".\\sub-folder\\sub-sub-folder",
+                ".\\sub-folder\\sub-sub-folder\\",
+                "./sub-folder/sub-sub-folder/sub-sub-sub-folder",
+                "./sub-folder/sub-sub-folder/sub-sub-sub-folder/",
+                ".\\sub-folder\\sub-sub-folder\\sub-sub-sub-folder",
+                ".\\sub-folder\\sub-sub-folder\\sub-sub-sub-folder\\"
+            )
+            .map(Arguments::of);
+    }
+
+    private static Stream<Arguments> invalidResourcePaths() {
+        return Stream.of(
+                "~",
+                "~/",
+                "~\\",
+                "../sub-folder",
+                "sub-folder/../..",
+                "/relative-path/../..",
+                "sub-folder/../sub-sub-folder",
+                "/relative-path/../sub-sub-folder",
+                "./sub-folder/../sub-sub-folder",
+                "..\\sub-folder",
+                "sub-folder\\..\\..",
+                "\\relative-path\\..\\..",
+                "sub-folder\\..\\sub-sub-folder",
+                "\\relative-path\\..\\sub-sub-folder",
+                ".\\sub-folder\\..\\sub-sub-folder",
+                "sub-folder/..",
+                "./sub-folder/..",
+                "sub-folder\\..",
+                ".\\sub-folder\\..",
+                "sub-folder/../sub-sub-folder",
+                "./sub-folder/../sub-sub-folder",
+                "sub-folder\\..\\sub-sub-folder",
+                ".\\sub-folder\\..\\sub-sub-folder",
+                "invalid-char-in-path-<",
+                "invalid-char-in-path->",
+                "invalid-char-in-path-:",
+                "invalid-char-in-path-|",
+                "invalid-char-in-path-?",
+                "invalid-char-in-path-*",
+                "invalid-char-in-path-~",
+                "sub-folder/invalid-char-in-path-~",
+                "/absolute-path",
+                "/absolute-path/..",
+                "/absolute-path/invalid-char-in-path-~",
+                "\\absolute-path",
+                "\\absolute-path\\..",
+                "\\absolute-path\\invalid-char-in-path-~",
+                "C:\\",
+                "C:\\path",
+                "C:/",
+                "C:/path"
+            )
+            .map(Arguments::of);
+    }
+
     private static ResourcesGlobalHash resourcesGlobalHash(String digest) {
         ResourcesGlobalHash resourcesGlobalHash = new ResourcesGlobalHash();
         resourcesGlobalHash.setDigest(digest);

Reply via email to