From e84112fd7128e05a1a4a380d8242c672d1f539f9 Mon Sep 17 00:00:00 2001
From: xuxuepeng <xuxuepeng1@huawei.com>
Date: Mon, 13 Nov 2023 08:44:15 +0000
Subject: [PATCH 09/14] !2243 Refactor capbilities specs * Refactor capbilities
 specs

---
 src/cmd/isula/isula_host_spec.c               |   1 +
 src/daemon/modules/spec/specs.c               |  12 +-
 src/daemon/modules/spec/specs_security.c      | 140 ++++----
 src/utils/cutils/utils_cap.c                  | 119 +++++++
 src/utils/cutils/utils_cap.h                  |  39 +++
 src/utils/cutils/utils_verify.c               |  81 -----
 src/utils/cutils/utils_verify.h               |   6 -
 test/cutils/utils_verify/utils_verify_ut.cc   |  14 -
 .../image/oci/oci_config_merge/CMakeLists.txt |   1 +
 test/image/oci/storage/images/CMakeLists.txt  |   1 +
 test/specs/specs/CMakeLists.txt               |   1 +
 test/specs/specs/oci_runtime_spec.json        |  32 ++
 test/specs/specs/specs_ut.cc                  | 315 ++++++++++++++++++
 test/specs/specs_extend/CMakeLists.txt        |   1 +
 14 files changed, 573 insertions(+), 190 deletions(-)
 create mode 100644 src/utils/cutils/utils_cap.c
 create mode 100644 src/utils/cutils/utils_cap.h

diff --git a/src/cmd/isula/isula_host_spec.c b/src/cmd/isula/isula_host_spec.c
index 6f39588d..09dea271 100644
--- a/src/cmd/isula/isula_host_spec.c
+++ b/src/cmd/isula/isula_host_spec.c
@@ -36,6 +36,7 @@
 #include "utils_file.h"
 #include "utils_string.h"
 #include "utils_verify.h"
+#include "utils_cap.h"
 #include "opt_ulimit.h"
 
 static bool parse_restart_policy(const char *policy, host_config_restart_policy **rp)
diff --git a/src/daemon/modules/spec/specs.c b/src/daemon/modules/spec/specs.c
index 95346603..cc49d85f 100644
--- a/src/daemon/modules/spec/specs.c
+++ b/src/daemon/modules/spec/specs.c
@@ -49,6 +49,7 @@
 #include "utils_file.h"
 #include "utils_string.h"
 #include "utils_verify.h"
+#include "utils_cap.h"
 
 #ifndef CLONE_NEWUTS
 #define CLONE_NEWUTS 0x04000000
@@ -814,15 +815,16 @@ static int adapt_settings_for_privileged(oci_runtime_spec *oci_spec, bool privil
 {
     int ret = 0;
     size_t all_caps_len = 0;
+    const char **all_caps = NULL;
 
     if (!privileged) {
         return 0;
     }
 
-    all_caps_len = util_get_all_caps_len();
-    if (oci_spec == NULL) {
-        ret = -1;
-        goto out;
+    all_caps = util_get_all_caps(&all_caps_len);
+    if (all_caps == NULL) {
+        ERROR("Failed to get all capabilities");
+        return -1;
     }
 
     clean_correlated_items(oci_spec);
@@ -838,7 +840,7 @@ static int adapt_settings_for_privileged(oci_runtime_spec *oci_spec, bool privil
         goto out;
     }
 
-    ret = refill_oci_process_capabilities(&oci_spec->process->capabilities, g_all_caps, all_caps_len);
+    ret = refill_oci_process_capabilities(&oci_spec->process->capabilities, all_caps, all_caps_len);
     if (ret != 0) {
         ERROR("Failed to copy all capabilities");
         ret = -1;
diff --git a/src/daemon/modules/spec/specs_security.c b/src/daemon/modules/spec/specs_security.c
index e78cc744..b34aec7c 100644
--- a/src/daemon/modules/spec/specs_security.c
+++ b/src/daemon/modules/spec/specs_security.c
@@ -37,6 +37,7 @@
 #include "utils_array.h"
 #include "utils_string.h"
 #include "utils_verify.h"
+#include "utils_cap.h"
 
 #define MAX_CAP_LEN 32
 
@@ -104,41 +105,31 @@ static int tweak_drops_capabilities(char ***new_caps, size_t *new_caps_len, char
     size_t i = 0;
     int ret = 0;
 
-    if (util_strings_in_slice((const char **)drops, drops_len, "all")) {
-        goto out;
+    if (basic_caps == NULL || basic_caps_len == 0) {
+        *new_caps = NULL;
+        *new_caps_len = 0;
+        return 0;
     }
 
-    for (i = 0; (basic_caps != NULL && i < basic_caps_len); i++) {
-        // skip `all` already handled above
-        if (!basic_caps[i] || !strcasecmp(basic_caps[i], "all")) {
-            continue;
-        }
-
-        // if we don't drop `all`, add back all the non-dropped caps
+    for (i = 0; i < basic_caps_len; i++) {
         if (!util_strings_in_slice((const char **)drops, drops_len, basic_caps[i] + strlen("CAP_"))) {
             ret = append_capability(new_caps, new_caps_len, basic_caps[i]);
             if (ret != 0) {
                 ERROR("Failed to append capabilities");
-                ret = -1;
-                goto out;
+                return -1;
             }
         }
     }
 
-out:
-    return ret;
+    return 0;
 }
 
 static int tweak_adds_capabilities(char ***new_caps, size_t *new_caps_len, const char **adds, size_t adds_len)
 {
     size_t i = 0;
-    int ret = 0;
     int nret = 0;
-    size_t all_caps_len = 0;
     char tmpcap[MAX_CAP_LEN] = { 0 };
 
-    all_caps_len = util_get_all_caps_len();
-
     for (i = 0; i < adds_len; i++) {
         // skip `all` already handled above
         if (strcasecmp(adds[i], "all") == 0) {
@@ -148,111 +139,92 @@ static int tweak_adds_capabilities(char ***new_caps, size_t *new_caps_len, const
         nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", adds[i]);
         if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) {
             ERROR("Failed to print string");
-            ret = -1;
-            goto out;
-        }
-        if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) {
-            ERROR("Unknown capability to add: '%s'", tmpcap);
-            ret = -1;
-            goto out;
+            return -1;
         }
 
         // add cap if not already in the list
         if (!util_strings_in_slice((const char **)*new_caps, *new_caps_len, tmpcap)) {
-            ret = append_capability(new_caps, new_caps_len, tmpcap);
-            if (ret != 0) {
+            nret = append_capability(new_caps, new_caps_len, tmpcap);
+            if (nret != 0) {
                 ERROR("Failed to append capabilities");
-                ret = -1;
-                goto out;
+                return -1;
             }
         }
     }
 
-out:
-    return ret;
-}
-
-static bool valid_drops_cap(const char **drops, size_t drops_len)
-{
-    int nret = 0;
-    size_t i;
-    size_t all_caps_len = 0;
-    char tmpcap[MAX_CAP_LEN] = { 0 };
-
-    all_caps_len = util_get_all_caps_len();
-    // look for invalid cap in the drop list
-    for (i = 0; i < drops_len; i++) {
-        if (strcasecmp(drops[i], "all") == 0) {
-            continue;
-        }
-
-        nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", drops[i]);
-        if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) {
-            ERROR("Failed to print string");
-            return false;
-        }
-        if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) {
-            ERROR("Unknown capability to drop: '%s'", drops[i]);
-            return false;
-        }
-    }
-
-    return true;
+    return 0;
 }
 
 // tweak_capabilities can tweak capabilities by adding or dropping capabilities
-// based on the basic capabilities.
+// based on the basic capabilities. The following are the priorities of the tweaks:
+// 1. if adds contains "all", then the basic capabilities will be ignored, and all capabilities will be added.
+// 2. if drops contains "all", all capabilities will be dropped.
+// 3. add individual capabilities in adds
+// 4. drop individual capabilities in drops.
+// The reason why we handle "all" first is that we can avoid the case that the individual capabilities are
+// not included by "all".
 static int tweak_capabilities(char ***caps, size_t *caps_len, const char **adds, size_t adds_len, const char **drops,
                               size_t drops_len)
 {
-    size_t i;
-    size_t all_caps_len = 0;
     int ret = 0;
     char **new_caps = NULL;
     char **basic_caps = NULL;
+    const char **all_caps = NULL;
     size_t new_caps_len = 0;
     size_t basic_caps_len = 0;
+    size_t all_caps_len = 0;
+    bool add_all = false;
+    bool drop_all = false;
 
-    all_caps_len = util_get_all_caps_len();
-    if (!valid_drops_cap(drops, drops_len)) {
+    all_caps = util_get_all_caps(&all_caps_len);
+    if (all_caps == NULL) {
+        ERROR("Failed to get all capabilities");
         return -1;
     }
 
-    if (util_strings_in_slice((const char **)adds, adds_len, "all")) {
-        ret = copy_capabilities(&basic_caps, &basic_caps_len, g_all_caps, all_caps_len);
-    } else {
+    add_all = util_strings_in_slice((const char **)adds, adds_len, "all");
+    drop_all = util_strings_in_slice((const char **)drops, drops_len, "all");
+
+
+    if (!add_all && !drop_all) {
+        // if neither add_all nor drop_all, we start with the default capabilities
         ret = copy_capabilities(&basic_caps, &basic_caps_len, (const char **)*caps, *caps_len);
-    }
-    if (ret != 0) {
-        ERROR("Failed to copy capabilities");
-        ret = -1;
-        goto free_out;
+        if (ret != 0) {
+            ERROR("Failed to copy capabilities");
+            ret = -1;
+            goto free_out;
+        }
+    } else if (drop_all) {
+        // if drop_all, we start with an empty set
+        basic_caps = NULL;
+        basic_caps_len = 0;
+    } else {
+        // if not drop_all but add_all, we start with all capabilities
+        ret = copy_capabilities(&basic_caps, &basic_caps_len, all_caps, all_caps_len);
+        if (ret != 0) {
+            ERROR("Failed to copy all capabilities");
+            ret = -1;
+            goto free_out;
+        }
     }
 
-    ret = tweak_drops_capabilities(&new_caps, &new_caps_len, basic_caps, basic_caps_len, drops, drops_len);
+    // Add capabilities to the basic capabilities
+    ret = tweak_adds_capabilities(&basic_caps, &basic_caps_len, adds, adds_len);
     if (ret != 0) {
         ret = -1;
         goto free_out;
     }
 
-    ret = tweak_adds_capabilities(&new_caps, &new_caps_len, adds, adds_len);
+    // Drop capabilities from the basic capabilities
+    ret = tweak_drops_capabilities(&new_caps, &new_caps_len, basic_caps, basic_caps_len, drops, drops_len);
     if (ret != 0) {
         ret = -1;
         goto free_out;
     }
 
 free_out:
-    for (i = 0; i < basic_caps_len; i++) {
-        free(basic_caps[i]);
-    }
-    free(basic_caps);
-
-    // free old caps
-    for (i = 0; i < *caps_len; i++) {
-        free((*caps)[i]);
-        (*caps)[i] = NULL;
-    }
-    free(*caps);
+    util_free_array_by_len(basic_caps, basic_caps_len);
+    util_free_array_by_len(*caps, *caps_len);
 
     // set new caps
     *caps = new_caps;
diff --git a/src/utils/cutils/utils_cap.c b/src/utils/cutils/utils_cap.c
new file mode 100644
index 00000000..6473df45
--- /dev/null
+++ b/src/utils/cutils/utils_cap.c
@@ -0,0 +1,119 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2018-2022. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: xuxuepeng
+ * Create: 2023-11-08
+ * Description: provide capbilities utils functions
+ *******************************************************************************/
+
+#define _GNU_SOURCE
+
+#include "utils_cap.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <isula_libutils/log.h>
+
+#include "utils_string.h"
+
+const char *g_all_caps[] = {
+    "CAP_CHOWN",
+    "CAP_DAC_OVERRIDE",
+    "CAP_DAC_READ_SEARCH",
+    "CAP_FOWNER",
+    "CAP_FSETID",
+    "CAP_KILL",
+    "CAP_SETGID",
+    "CAP_SETUID",
+    "CAP_SETPCAP",
+    "CAP_LINUX_IMMUTABLE",
+    "CAP_NET_BIND_SERVICE",
+    "CAP_NET_BROADCAST",
+    "CAP_NET_ADMIN",
+    "CAP_NET_RAW",
+    "CAP_IPC_LOCK",
+    "CAP_IPC_OWNER",
+    "CAP_SYS_MODULE",
+    "CAP_SYS_RAWIO",
+    "CAP_SYS_CHROOT",
+    "CAP_SYS_PTRACE",
+    "CAP_SYS_PACCT",
+    "CAP_SYS_ADMIN",
+    "CAP_SYS_BOOT",
+    "CAP_SYS_NICE",
+    "CAP_SYS_RESOURCE",
+    "CAP_SYS_TIME",
+    "CAP_SYS_TTY_CONFIG",
+    "CAP_MKNOD",
+    "CAP_LEASE",
+#ifdef CAP_AUDIT_WRITE
+    "CAP_AUDIT_WRITE",
+#endif
+#ifdef CAP_AUDIT_CONTROL
+    "CAP_AUDIT_CONTROL",
+#endif
+    "CAP_SETFCAP",
+    "CAP_MAC_OVERRIDE",
+    "CAP_MAC_ADMIN",
+#ifdef CAP_SYSLOG
+    "CAP_SYSLOG",
+#endif
+#ifdef CAP_WAKE_ALARM
+    "CAP_WAKE_ALARM",
+#endif
+#ifdef CAP_BLOCK_SUSPEND
+    "CAP_BLOCK_SUSPEND",
+#endif
+#ifdef CAP_AUDIT_READ
+    "CAP_AUDIT_READ",
+#endif
+#ifdef CAP_PERFMON
+    "CAP_PERFMON",
+#endif
+#ifdef CAP_BPF
+    "CAP_BPF",
+#endif
+#ifdef CAP_CHECKPOINT_RESTORE
+    "CAP_CHECKPOINT_RESTORE",
+#endif
+};
+
+static inline size_t util_get_all_caps_len()
+{
+    return sizeof(g_all_caps) / sizeof(char *);
+}
+
+bool util_valid_cap(const char *cap)
+{
+    int nret = 0;
+    char tmpcap[32] = { 0 };
+    size_t all_caps_len = util_get_all_caps_len();
+
+    if (cap == NULL) {
+        return false;
+    }
+
+    nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", cap);
+    if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) {
+        ERROR("Failed to print string");
+        return false;
+    }
+    if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) {
+        return false;
+    }
+
+    return true;
+}
+
+const char **util_get_all_caps(size_t *cap_len)
+{
+    *cap_len = util_get_all_caps_len();
+    return g_all_caps;
+}
diff --git a/src/utils/cutils/utils_cap.h b/src/utils/cutils/utils_cap.h
new file mode 100644
index 00000000..de63d070
--- /dev/null
+++ b/src/utils/cutils/utils_cap.h
@@ -0,0 +1,39 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2018-2022. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: xuxuepeng
+ * Create: 2023-11-08
+ * Description: provide capbilities utils functions
+ *******************************************************************************/
+
+#ifndef UTILS_CUTILS_UTILS_CAP_H
+#define UTILS_CUTILS_UTILS_CAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <linux/capability.h>
+
+bool util_valid_cap(const char *cap);
+
+/**
+ * Get all supported capabilities for linux,
+ * note that the returned strings are unmutable
+ */
+const char **util_get_all_caps(size_t *cap_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UTILS_CUTILS_UTILS_CAP_H
diff --git a/src/utils/cutils/utils_verify.c b/src/utils/cutils/utils_verify.c
index 2f10f278..f4ce3199 100644
--- a/src/utils/cutils/utils_verify.c
+++ b/src/utils/cutils/utils_verify.c
@@ -22,7 +22,6 @@
 #include <sys/stat.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <linux/capability.h>
 #include <stdio.h>
 #include <strings.h>
 
@@ -32,59 +31,6 @@
 #include "utils_array.h"
 #include "utils_string.h"
 
-const char *g_all_caps[] = {
-    "CAP_CHOWN",
-    "CAP_DAC_OVERRIDE",
-    "CAP_DAC_READ_SEARCH",
-    "CAP_FOWNER",
-    "CAP_FSETID",
-    "CAP_KILL",
-    "CAP_SETGID",
-    "CAP_SETUID",
-    "CAP_SETPCAP",
-    "CAP_LINUX_IMMUTABLE",
-    "CAP_NET_BIND_SERVICE",
-    "CAP_NET_BROADCAST",
-    "CAP_NET_ADMIN",
-    "CAP_NET_RAW",
-    "CAP_IPC_LOCK",
-    "CAP_IPC_OWNER",
-    "CAP_SYS_MODULE",
-    "CAP_SYS_RAWIO",
-    "CAP_SYS_CHROOT",
-    "CAP_SYS_PTRACE",
-    "CAP_SYS_PACCT",
-    "CAP_SYS_ADMIN",
-    "CAP_SYS_BOOT",
-    "CAP_SYS_NICE",
-    "CAP_SYS_RESOURCE",
-    "CAP_SYS_TIME",
-    "CAP_SYS_TTY_CONFIG",
-    "CAP_MKNOD",
-    "CAP_LEASE",
-#ifdef CAP_AUDIT_WRITE
-    "CAP_AUDIT_WRITE",
-#endif
-#ifdef CAP_AUDIT_CONTROL
-    "CAP_AUDIT_CONTROL",
-#endif
-    "CAP_SETFCAP",
-    "CAP_MAC_OVERRIDE",
-    "CAP_MAC_ADMIN",
-#ifdef CAP_SYSLOG
-    "CAP_SYSLOG",
-#endif
-#ifdef CAP_WAKE_ALARM
-    "CAP_WAKE_ALARM",
-#endif
-#ifdef CAP_BLOCK_SUSPEND
-    "CAP_BLOCK_SUSPEND",
-#endif
-#ifdef CAP_AUDIT_READ
-    "CAP_AUDIT_READ",
-#endif
-};
-
 bool util_valid_cmd_arg(const char *arg)
 {
     return (arg != NULL) && (strchr(arg, '|') == NULL) && (strchr(arg, '`') == NULL) && (strchr(arg, '&')) == NULL &&
@@ -215,33 +161,6 @@ bool util_valid_str(const char *str)
     return (str != NULL && str[0] != '\0') ? true : false;
 }
 
-size_t util_get_all_caps_len()
-{
-    return sizeof(g_all_caps) / sizeof(char *);
-}
-
-bool util_valid_cap(const char *cap)
-{
-    int nret = 0;
-    char tmpcap[32] = { 0 };
-    size_t all_caps_len = util_get_all_caps_len();
-
-    if (cap == NULL) {
-        return false;
-    }
-
-    nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", cap);
-    if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) {
-        ERROR("Failed to print string");
-        return false;
-    }
-    if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) {
-        return false;
-    }
-
-    return true;
-}
-
 bool util_valid_container_id(const char *id)
 {
     char *patten = "^[a-f0-9]{1,64}$";
diff --git a/src/utils/cutils/utils_verify.h b/src/utils/cutils/utils_verify.h
index ad4466ef..54d1ce71 100644
--- a/src/utils/cutils/utils_verify.h
+++ b/src/utils/cutils/utils_verify.h
@@ -38,8 +38,6 @@ extern "C" {
 
 #define VALID_VOLUME_NAME "[a-zA-Z0-9][a-zA-Z0-9_.-]{1,63}"
 
-extern const char *g_all_caps[];
-
 bool util_valid_cmd_arg(const char *arg);
 
 bool util_valid_signal(int sig);
@@ -54,10 +52,6 @@ bool util_valid_device_mode(const char *mode);
 
 bool util_valid_str(const char *str);
 
-size_t util_get_all_caps_len();
-
-bool util_valid_cap(const char *cap);
-
 bool util_valid_time_tz(const char *time);
 
 bool util_valid_embedded_image_name(const char *name);
diff --git a/test/cutils/utils_verify/utils_verify_ut.cc b/test/cutils/utils_verify/utils_verify_ut.cc
index 99775d09..79670ec1 100644
--- a/test/cutils/utils_verify/utils_verify_ut.cc
+++ b/test/cutils/utils_verify/utils_verify_ut.cc
@@ -98,20 +98,6 @@ TEST(utils_verify, test_util_valid_str)
     ASSERT_EQ(util_valid_str(nullptr), false);
 }
 
-TEST(utils_verify, test_util_get_all_caps_len)
-{
-    ASSERT_NE(util_get_all_caps_len(), 0);
-}
-
-TEST(utils_verify, test_util_valid_cap)
-{
-    ASSERT_EQ(util_valid_cap("DAC_READ_SEARCH"), true);
-
-    ASSERT_EQ(util_valid_cap(nullptr), false);
-    ASSERT_EQ(util_valid_cap(""), false);
-    ASSERT_EQ(util_valid_cap("DA_READ_SEARCH"), false);
-}
-
 TEST(utils_verify, test_util_valid_time_tz)
 {
     ASSERT_EQ(util_valid_time_tz("2022-10-04T18:22:45.289257759Z"), true);
diff --git a/test/image/oci/oci_config_merge/CMakeLists.txt b/test/image/oci/oci_config_merge/CMakeLists.txt
index ce4df5ba..90809080 100644
--- a/test/image/oci/oci_config_merge/CMakeLists.txt
+++ b/test/image/oci/oci_config_merge/CMakeLists.txt
@@ -14,6 +14,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_file.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_timestamp.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_fs.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_cap.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/map/map.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/map/rb_tree.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/util_atomic.c
diff --git a/test/image/oci/storage/images/CMakeLists.txt b/test/image/oci/storage/images/CMakeLists.txt
index 8446ebba..28e0b505 100644
--- a/test/image/oci/storage/images/CMakeLists.txt
+++ b/test/image/oci/storage/images/CMakeLists.txt
@@ -11,6 +11,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_convert.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_file.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_base64.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_cap.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/util_atomic.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/sha256/sha256.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/path.c
diff --git a/test/specs/specs/CMakeLists.txt b/test/specs/specs/CMakeLists.txt
index 1d627e37..a9dbc52c 100644
--- a/test/specs/specs/CMakeLists.txt
+++ b/test/specs/specs/CMakeLists.txt
@@ -14,6 +14,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/util_atomic.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_mount_spec.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_fs.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_cap.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/sha256/sha256.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/path.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/map/map.c
diff --git a/test/specs/specs/oci_runtime_spec.json b/test/specs/specs/oci_runtime_spec.json
index 0223fd6f..efd5da35 100644
--- a/test/specs/specs/oci_runtime_spec.json
+++ b/test/specs/specs/oci_runtime_spec.json
@@ -154,6 +154,38 @@
                 "CAP_SYS_CHROOT",
                 "CAP_KILL",
                 "CAP_AUDIT_WRITE"
+            ],
+            "effective": [
+                "CAP_CHOWN",
+                "CAP_DAC_OVERRIDE",
+                "CAP_FSETID",
+                "CAP_FOWNER",
+                "CAP_MKNOD",
+                "CAP_NET_RAW",
+                "CAP_SETGID",
+                "CAP_SETUID",
+                "CAP_SETFCAP",
+                "CAP_SETPCAP",
+                "CAP_NET_BIND_SERVICE",
+                "CAP_SYS_CHROOT",
+                "CAP_KILL",
+                "CAP_AUDIT_WRITE"
+            ],
+            "permitted": [
+                "CAP_CHOWN",
+                "CAP_DAC_OVERRIDE",
+                "CAP_FSETID",
+                "CAP_FOWNER",
+                "CAP_MKNOD",
+                "CAP_NET_RAW",
+                "CAP_SETGID",
+                "CAP_SETUID",
+                "CAP_SETFCAP",
+                "CAP_SETPCAP",
+                "CAP_NET_BIND_SERVICE",
+                "CAP_SYS_CHROOT",
+                "CAP_KILL",
+                "CAP_AUDIT_WRITE"
             ]
         }
     },
diff --git a/test/specs/specs/specs_ut.cc b/test/specs/specs/specs_ut.cc
index 96aa1c63..ad903a3f 100644
--- a/test/specs/specs/specs_ut.cc
+++ b/test/specs/specs/specs_ut.cc
@@ -20,6 +20,7 @@
 #include "isula_libutils/oci_runtime_spec.h"
 #include "specs_api.h"
 #include "specs_namespace.h"
+#include "specs_security.h"
 #include "isula_libutils/host_config.h"
 #include "isula_libutils/container_config.h"
 #include "oci_ut_common.h"
@@ -27,6 +28,7 @@
 #include <gmock/gmock.h>
 #include "isulad_config_mock.h"
 #include "utils.h"
+#include "utils_cap.h"
 
 using ::testing::Args;
 using ::testing::ByRef;
@@ -344,3 +346,316 @@ TEST_F(SpecsUnitTest, test_merge_container_cgroups_path_5)
 
     testing::Mock::VerifyAndClearExpectations(&m_isulad_conf);
 }
+
+/********************************* UT for merge caps *******************************************/
+struct capabilities_lens {
+    size_t bounding_len;
+    size_t effective_len;
+    size_t inheritable_len;
+    size_t permitted_len;
+    size_t ambient_len;
+};
+
+void check_capabilities_len(defs_process_capabilities *cap, struct capabilities_lens *lens)
+{
+    lens->bounding_len = cap->bounding_len;
+    lens->effective_len = cap->effective_len;
+    lens->inheritable_len = cap->inheritable_len;
+    lens->permitted_len = cap->permitted_len;
+    lens->ambient_len = cap->ambient_len;
+}
+
+void validate_capabilities_len(defs_process_capabilities *cap, struct capabilities_lens *lens, ssize_t len_diff)
+{
+    ASSERT_EQ((ssize_t)cap->bounding_len, (ssize_t)lens->bounding_len + len_diff);
+    ASSERT_EQ((ssize_t)cap->effective_len, (ssize_t)lens->effective_len + len_diff);
+    ASSERT_EQ((ssize_t)cap->permitted_len, (ssize_t)lens->permitted_len + len_diff);
+    // Currently we don't support inheritable and ambient capabilities
+}
+
+TEST(merge_capability_ut, test_merge_caps_without_adds_drops)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, nullptr, 0, nullptr, 0);
+    ASSERT_EQ(ret, 0);
+
+    validate_capabilities_len(oci_spec->process->capabilities, &old_lens, 0);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_adds_without_drops)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    /* All of below capabilities are not in oci_config_file */
+    const char *adds[] = { "NET_ADMIN", "SYS_ADMIN", "SYS_TTY_CONFIG", "SYS_PTRACE" };
+    const char *drops[] = {};
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    /* All of capabilities in adds are added */
+    validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_adds_existing_without_drops)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    /* CHOWN already exits in oci_config_file */
+    const char *adds[] = { "CHOWN", "SYS_ADMIN", "SYS_TTY_CONFIG", "SYS_PTRACE" };
+    const char *drops[] = {};
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    /* CHOWN is not added, since it already exits in the default list */
+    validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len - 1);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_drops_without_adds)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    const char *adds[] = {};
+    /* Below capabilities are not in the oci_config_file */
+    const char *drops[] = { "SYS_TTY_CONFIG", "SYS_PTRACE" };
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    /* Nothing dropped */
+    validate_capabilities_len(oci_spec->process->capabilities, &old_lens, 0);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_drops_existing_without_adds)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    const char *adds[] = {};
+    /* Below capabilities are in the oci_config_file */
+    const char *drops[] = { "CHOWN", "MKNOD" };
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    /* All dropped */
+    validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len - drops_len);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_adds_drops)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    /* All of below capabilities are not in oci_config_file */
+    const char *adds[] = { "NET_ADMIN", "SYS_ADMIN", "SYS_TTY_CONFIG", "SYS_PTRACE" };
+    const char *drops[] = { "SYS_TTY_CONFIG", "SYS_PTRACE" };
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len - drops_len);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_adds_all_without_drops)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    /* NET_ADMIN is in all */
+    const char *adds[] = { "ALL", "NET_ADMIN" };
+    const char *drops[] = {};
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+    size_t all_caps_len = 0;
+    util_get_all_caps(&all_caps_len);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    ASSERT_EQ(oci_spec->process->capabilities->bounding_len, all_caps_len);
+    ASSERT_EQ(oci_spec->process->capabilities->effective_len, all_caps_len);
+    ASSERT_EQ(oci_spec->process->capabilities->permitted_len, all_caps_len);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_adds_all_and_extra_without_drops)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    /* ABC is not in all */
+    const char *adds[] = { "ALL", "ABC" };
+    const char *drops[] = {};
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+    size_t all_caps_len = 0;
+    util_get_all_caps(&all_caps_len);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    ASSERT_EQ(oci_spec->process->capabilities->bounding_len, all_caps_len + 1);
+    ASSERT_EQ(oci_spec->process->capabilities->effective_len, all_caps_len + 1);
+    ASSERT_EQ(oci_spec->process->capabilities->permitted_len, all_caps_len + 1);
+
+    free_oci_runtime_spec(oci_spec);
+}
+
+TEST(merge_capability_ut, test_merge_caps_adds_all_drops_all)
+{
+    oci_runtime_spec *oci_spec = nullptr;
+    int ret = 0;
+    char *err = nullptr;
+    struct capabilities_lens old_lens = { 0 };
+    char *oci_config_file = nullptr;
+    /* ABC, EFG is not in all */
+    const char *adds[] = { "ALL", "ABC", "EFG"};
+    const char *drops[] = { "ALL", "ABC" };
+    size_t adds_len = sizeof(adds) / sizeof(adds[0]);
+    size_t drops_len = sizeof(drops) / sizeof(drops[0]);
+    size_t all_caps_len = 0;
+    util_get_all_caps(&all_caps_len);
+
+    oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE);
+    ASSERT_TRUE(oci_config_file != nullptr);
+
+    oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err);
+    ASSERT_TRUE(oci_spec != nullptr);
+    free(err);
+
+    check_capabilities_len(oci_spec->process->capabilities, &old_lens);
+
+    ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len);
+    ASSERT_EQ(ret, 0);
+
+    ASSERT_EQ(oci_spec->process->capabilities->bounding_len, 1);
+    ASSERT_EQ(oci_spec->process->capabilities->effective_len, 1);
+    ASSERT_EQ(oci_spec->process->capabilities->permitted_len, 1);
+
+    free_oci_runtime_spec(oci_spec);
+}
diff --git a/test/specs/specs_extend/CMakeLists.txt b/test/specs/specs_extend/CMakeLists.txt
index 294690e8..bf4b378e 100644
--- a/test/specs/specs_extend/CMakeLists.txt
+++ b/test/specs/specs_extend/CMakeLists.txt
@@ -14,6 +14,7 @@ add_executable(${EXE}
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/util_atomic.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_mount_spec.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_fs.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_cap.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/sha256/sha256.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/path.c
     ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/map/map.c
-- 
2.42.0

