]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
test/mgr: Add negative unit tests to MgrCap
authorszuraski898 <steven.zuraski@ibm.com>
Thu, 15 Jan 2026 02:08:13 +0000 (20:08 -0600)
committerszuraski898 <steven.zuraski@ibm.com>
Thu, 15 Jan 2026 02:08:13 +0000 (20:08 -0600)
Fixes: https://tracker.ceph.com/issues/72938
Signed-off-by: szuraski898 <steven.zuraski@ibm.com>
src/test/mgr/test_mgrcap.cc

index 93b26f4622b8e8797fb99024abdf8817d68348e8..dd148ca71ac2b76217af027990b00a04302175fe 100644 (file)
 
 #include <iostream>
 
+#include "gtest/gtest.h"
 #include "include/stringify.h"
 #include "mgr/MgrCap.h"
 
-#include "gtest/gtest.h"
-
 using namespace std;
 
-const char *parse_good[] = {
-
-  // MgrCapMatch
-  "allow *",
-  "allow r",
-  "allow rwx",
-  "allow  r",
-  " allow rwx",
-  "allow rwx ",
-  " allow rwx ",
-  " allow\t   rwx ",
-  "\tallow\nrwx\t",
-  "allow service=foo x",
-  "allow service=\"froo\" x",
-  "allow profile read-only",
-  "allow profile read-write",
-  "allow profile \"rbd-read-only\", allow *",
-  "allow command \"a b c\"",
-  "allow command abc",
-  "allow command abc with arg=foo",
-  "allow command abc with arg=foo arg2=bar",
-  "allow command abc with arg=foo arg2=bar",
-  "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz",
-  "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz",
-  "allow command abc with arg regex \"^[0-9a-z.]*$\"",
-  "allow command abc with arg regex \"\(invaluid regex\"",
-  "allow service foo x",
-  "allow service foo x; allow service bar x",
-  "allow service foo w ;allow service bar x",
-  "allow service foo  w , allow service bar x",
-  "allow service foo r , allow service bar x",
-  "allow service foo_foo r, allow service bar r",
-  "allow service foo-foo r, allow service bar r",
-  "allow service \" foo \" w, allow service bar r",
-  "allow module foo x",
-  "allow module=foo x",
-  "allow module foo_foo r",
-  "allow module \" foo \" w",
-  "allow module foo with arg1=value1 x",
-  "allow command abc with arg=foo arg2=bar, allow service foo r",
-  "allow command abc.def with arg=foo arg2=bar, allow service foo r",
-  "allow command \"foo bar\" with arg=\"baz\"",
-  "allow command \"foo bar\" with arg=\"baz.xx\"",
-  "allow command \"foo bar\" with arg = \"baz.xx\"",
-  "profile crash",
-  "profile osd",
-  "profile mds",
-  "profile rbd pool=ABC namespace=NS",
-  "profile \"rbd-read-only\", profile crash",
-  "allow * network 1.2.3.4/24",
-  "allow * network ::1/128",
-  "allow * network [aa:bb::1]/128",
-  "allow service=foo x network 1.2.3.4/16",
-  "allow command abc network 1.2.3.4/8",
-  "profile crash network 1.2.3.4/32",
-  "allow profile crash network 1.2.3.4/32",
-  0
+const std::vector<std::string> parse_good = {
+    // MgrCapMatch
+    "allow *"s,
+    "allow r"s,
+    "allow rwx"s,
+    "allow  r"s,
+    " allow rwx"s,
+    "allow rwx "s,
+    " allow rwx "s,
+    " allow\t   rwx "s,
+    "\tallow\nrwx\t"s,
+    "allow service=foo x"s,
+    "allow service=\"froo\" x"s,
+    "allow profile read-only"s,
+    "allow profile read-write"s,
+    "allow profile \"rbd-read-only\", allow *"s,
+    "allow command \"a b c\""s,
+    "allow command abc"s,
+    "allow command abc with arg=foo"s,
+    "allow command abc with arg=foo arg2=bar"s,
+    "allow command abc with arg=foo arg2=bar"s,
+    "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz"s,
+    "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz"s,
+    "allow command abc with arg regex \"^[0-9a-z.]*$\""s,
+    "allow command abc with arg regex \"\(invaluid regex\""s,
+    "allow service foo x"s,
+    "allow service foo x; allow service bar x"s,
+    "allow service foo w ;allow service bar x"s,
+    "allow service foo  w , allow service bar x"s,
+    "allow service foo r , allow service bar x"s,
+    "allow service foo_foo r, allow service bar r"s,
+    "allow service foo-foo r, allow service bar r"s,
+    "allow service \" foo \" w, allow service bar r"s,
+    "allow module foo x"s,
+    "allow module=foo x"s,
+    "allow module foo_foo r"s,
+    "allow module \" foo \" w"s,
+    "allow module foo with arg1=value1 x"s,
+    "allow command abc with arg=foo arg2=bar, allow service foo r"s,
+    "allow command abc.def with arg=foo arg2=bar, allow service foo r"s,
+    "allow command \"foo bar\" with arg=\"baz\""s,
+    "allow command \"foo bar\" with arg=\"baz.xx\""s,
+    "allow command \"foo bar\" with arg = \"baz.xx\""s,
+    "profile crash"s,
+    "profile osd"s,
+    "profile mds"s,
+    "profile rbd pool=ABC namespace=NS"s,
+    "profile \"rbd-read-only\", profile crash"s,
+    "allow * network 1.2.3.4/24"s,
+    "allow * network ::1/128"s,
+    "allow * network [aa:bb::1]/128"s,
+    "allow service=foo x network 1.2.3.4/16"s,
+    "allow command abc network 1.2.3.4/8"s,
+    "profile crash network 1.2.3.4/32"s,
+    "allow profile crash network 1.2.3.4/32"s,
 };
 
-TEST(MgrCap, ParseGood) {
-  for (int i=0; parse_good[i]; ++i) {
-    string str = parse_good[i];
+TEST(MgrCap, ParseGood)
+{
+  for (const auto& parseable : parse_good) {
     MgrCap cap;
-    std::cout << "Testing good input: '" << str << "'" << std::endl;
-    ASSERT_TRUE(cap.parse(str, &cout));
+    std::cout << "Testing good input: '" << parseable << "'" << std::endl;
+    ASSERT_TRUE(cap.parse(parseable, &cout));
     std::cout << "                                         -> " << cap
               << std::endl;
   }
 }
 
 // these should stringify to the input value
-const char *parse_identity[] = {
-  "allow *",
-  "allow r",
-  "allow rwx",
-  "allow service foo x",
-  "profile crash",
-  "profile rbd-read-only, allow *",
-  "profile rbd namespace=NS pool=ABC",
-  "allow command abc",
-  "allow command \"a b c\"",
-  "allow command abc with arg=foo",
-  "allow command abc with arg=foo arg2=bar",
-  "allow command abc with arg=foo arg2=bar",
-  "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz",
-  "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz",
-  "allow service foo x",
-  "allow service foo x, allow service bar x",
-  "allow service foo w, allow service bar x",
-  "allow service foo r, allow service bar x",
-  "allow service foo_foo r, allow service bar r",
-  "allow service foo-foo r, allow service bar r",
-  "allow service \" foo \" w, allow service bar r",
-  "allow module foo x",
-  "allow module \" foo_foo \" r",
-  "allow module foo with arg1=value1 x",
-  "allow command abc with arg=foo arg2=bar, allow service foo r",
-  0
+const std::vector<std::string> parse_identity = {
+    "allow *"s,
+    "allow r"s,
+    "allow rwx"s,
+    "allow service foo x"s,
+    "profile crash"s,
+    "profile rbd-read-only, allow *"s,
+    "profile rbd namespace=NS pool=ABC"s,
+    "allow command abc"s,
+    "allow command \"a b c\""s,
+    "allow command abc with arg=foo"s,
+    "allow command abc with arg=foo arg2=bar"s,
+    "allow command abc with arg=foo arg2=bar"s,
+    "allow command abc with arg=foo arg2 prefix bar arg3 prefix baz"s,
+    "allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz"s,
+    "allow service foo x"s,
+    "allow service foo x, allow service bar x"s,
+    "allow service foo w, allow service bar x"s,
+    "allow service foo r, allow service bar x"s,
+    "allow service foo_foo r, allow service bar r"s,
+    "allow service foo-foo r, allow service bar r"s,
+    "allow service \" foo \" w, allow service bar r"s,
+    "allow module foo x"s,
+    "allow module \" foo_foo \" r"s,
+    "allow module foo with arg1=value1 x"s,
+    "allow command abc with arg=foo arg2=bar, allow service foo r"s,
 };
 
 TEST(MgrCap, ParseIdentity)
 {
-  for (int i=0; parse_identity[i]; ++i) {
-    string str = parse_identity[i];
+  for (const auto& parseable : parse_identity) {
     MgrCap cap;
-    std::cout << "Testing good input: '" << str << "'" << std::endl;
-    ASSERT_TRUE(cap.parse(str, &cout));
+    std::cout << "Testing good input: '" << parseable << "'" << std::endl;
+    ASSERT_TRUE(cap.parse(parseable, &cout));
     string out = stringify(cap);
-    ASSERT_EQ(out, str);
+    ASSERT_EQ(out, parseable);
   }
 }
 
-const char *parse_bad[] = {
-  "allow r foo",
-  "allow*",
-  "foo allow *",
-  "profile foo rwx",
-  "profile",
-  "profile foo bar rwx",
-  "allow profile foo rwx",
-  "allow profile",
-  "allow profile foo bar rwx",
-  "allow service bar",
-  "allow command baz x",
-  "allow r w",
-  "ALLOW r",
-  "allow rwx,",
-  "allow rwx x",
-  "allow r pool foo r",
-  "allow wwx pool taco",
-  "allow wwx pool taco^funny&chars",
-  "allow rwx pool 'weird name''",
-  "allow rwx object_prefix \"beforepool\" pool weird",
-  "allow rwx auid 123 pool asdf",
-  "allow command foo a prefix b",
-  "allow command foo with a prefixb",
-  "allow command foo with a = prefix b",
-  "allow command foo with a prefix b c",
-  0
+const std::vector<std::string> parse_bad = {
+    "allow r foo"s,
+    "allow*"s,
+    "foo allow *"s,
+    "profile foo rwx"s,
+    "profile"s,
+    "profile foo bar rwx"s,
+    "allow profile foo rwx"s,
+    "allow profile"s,
+    "allow profile foo bar rwx"s,
+    "allow service bar"s,
+    "allow command baz x"s,
+    "allow r w"s,
+    "ALLOW r"s,
+    "allow rwx,"s,
+    "allow rwx x"s,
+    "allow r pool foo r"s,
+    "allow wwx pool taco"s,
+    "allow wwx pool taco^funny&chars"s,
+    "allow rwx pool 'weird name''"s,
+    "allow rwx object_prefix \"beforepool\" pool weird"s,
+    "allow rwx auid 123 pool asdf"s,
+    "allow command foo a prefix b"s,
+    "allow command foo with a prefixb"s,
+    "allow command foo with a = prefix b"s,
+    "allow command foo with a prefix b c"s,
+    // Empty and whitespace strings
+    ""s,
+    "   "s,
+    "\t\n"s,
+    // Empty names
+    "allow command \"\""s,
+    "allow service \"\" r"s,
+    "allow module \"\" r"s,
+    // Invalid profile parameters
+    "profile unknown"s,
+    "profile rbd invalid-key=value"s,
+    "profile rbd pool"s,
+    "profile rbd pool="s,
+    "profile rbd =value"s,
 };
 
-TEST(MgrCap, ParseBad) {
-  for (int i=0; parse_bad[i]; ++i) {
-    string str = parse_bad[i];
+TEST(MgrCap, ParseBad)
+{
+  for (const auto& unparseable : parse_bad) {
     MgrCap cap;
-    std::cout << "Testing bad input: '" << str << "'" << std::endl;
-    ASSERT_FALSE(cap.parse(str, &cout));
+    std::cout << "Testing bad input: '" << unparseable << "'" << std::endl;
+    ASSERT_FALSE(cap.parse(unparseable, &cout));
   }
 }
 
-TEST(MgrCap, AllowAll) {
+TEST(MgrCap, AllowAll)
+{
   MgrCap cap;
   ASSERT_FALSE(cap.is_allow_all());
 
@@ -206,8 +215,8 @@ TEST(MgrCap, AllowAll) {
 
   ASSERT_TRUE(cap.parse("allow *", nullptr));
   ASSERT_TRUE(cap.is_allow_all());
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
-                             true, {}));
+  ASSERT_TRUE(
+      cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true, true, {}));
 
   MgrCap cap2;
   ASSERT_FALSE(cap2.is_allow_all());
@@ -215,9 +224,11 @@ TEST(MgrCap, AllowAll) {
   ASSERT_TRUE(cap2.is_allow_all());
 }
 
-TEST(MgrCap, Network) {
+TEST(MgrCap, Network)
+{
   MgrCap cap;
-  bool r = cap.parse("allow * network 192.168.0.0/16, allow * network 10.0.0.0/8", nullptr);
+  bool r = cap.parse(
+      "allow * network 192.168.0.0/16, allow * network 10.0.0.0/8", nullptr);
   ASSERT_TRUE(r);
 
   entity_addr_t a, b, c;
@@ -225,48 +236,72 @@ TEST(MgrCap, Network) {
   b.parse("192.168.2.3");
   c.parse("192.167.2.3");
 
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
-                             true, a));
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
-                             true, b));
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true,
-                              true, c));
+  ASSERT_TRUE(
+      cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true, true, a));
+  ASSERT_TRUE(
+      cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true, true, b));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true, true, c));
 }
 
-TEST(MgrCap, CommandRegEx) {
+TEST(MgrCap, NetworkIPv6)
+{
+  MgrCap cap;
+  bool r = cap.parse(
+      "allow * network 2001:db8::/32, allow * network fe80::/10", nullptr);
+  ASSERT_TRUE(r);
+
+  entity_addr_t a, b, c;
+  a.parse("[2001:db8::1]");
+  b.parse("[fe80::1]");
+  c.parse("[2001:db9::1]");
+
+  ASSERT_TRUE(
+      cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true, true, a));
+  ASSERT_TRUE(
+      cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true, true, b));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "foo", "", "asdf", {}, true, true, true, c));
+}
+
+TEST(MgrCap, CommandRegEx)
+{
   MgrCap cap;
   ASSERT_FALSE(cap.is_allow_all());
-  ASSERT_TRUE(cap.parse("allow command abc with arg regex \"^[0-9a-z.]*$\"",
-                        nullptr));
+  ASSERT_TRUE(
+      cap.parse("allow command abc with arg regex \"^[0-9a-z.]*$\"", nullptr));
 
   EntityName name;
   name.from_str("osd.123");
-  ASSERT_TRUE(cap.is_capable(nullptr, name, "", "", "abc",
-                             {{"arg", "12345abcde"}}, true, true, true, {}));
-  ASSERT_FALSE(cap.is_capable(nullptr, name, "", "", "abc", {{"arg", "~!@#$"}},
-                              true, true, true, {}));
+  ASSERT_TRUE(cap.is_capable(
+      nullptr, name, "", "", "abc", {{"arg", "12345abcde"}}, true, true, true,
+      {}));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, name, "", "", "abc", {{"arg", "~!@#$"}}, true, true, true, {}));
 
   ASSERT_TRUE(cap.parse("allow command abc with arg regex \"[*\"", nullptr));
-  ASSERT_FALSE(cap.is_capable(nullptr, name, "", "", "abc", {{"arg", ""}}, true,
-                              true, true, {}));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, name, "", "", "abc", {{"arg", ""}}, true, true, true, {}));
 }
 
-TEST(MgrCap, Module) {
+TEST(MgrCap, Module)
+{
   MgrCap cap;
   ASSERT_FALSE(cap.is_allow_all());
   ASSERT_TRUE(cap.parse("allow module abc r, allow module bcd w", nullptr));
 
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, true, false,
-                              {}));
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false, false,
-                             {}));
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "bcd", "", {}, true, true, false,
-                              {}));
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "bcd", "", {}, false, true, false,
-                             {}));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "abc", "", {}, true, true, false, {}));
+  ASSERT_TRUE(
+      cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false, false, {}));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "bcd", "", {}, true, true, false, {}));
+  ASSERT_TRUE(
+      cap.is_capable(nullptr, {}, "", "bcd", "", {}, false, true, false, {}));
 }
 
-TEST(MgrCap, Profile) {
+TEST(MgrCap, Profile)
+{
   MgrCap cap;
   ASSERT_FALSE(cap.is_allow_all());
 
@@ -274,28 +309,93 @@ TEST(MgrCap, Profile) {
   ASSERT_FALSE(cap.parse("profile rbd invalid-key=value"));
 
   ASSERT_TRUE(cap.parse("profile rbd", nullptr));
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false,
-                              false, {}));
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
-                             true, false, {}));
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
-                             false, false, {}));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false, false, {}));
+  ASSERT_TRUE(cap.is_capable(
+      nullptr, {}, "", "rbd_support", "", {}, true, true, false, {}));
+  ASSERT_TRUE(cap.is_capable(
+      nullptr, {}, "", "rbd_support", "", {}, true, false, false, {}));
 
   ASSERT_TRUE(cap.parse("profile rbd pool=abc namespace prefix def", nullptr));
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {},
-                              true, true, false, {}));
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "rbd_support", "",
-                              {{"pool", "abc"}},
-                              true, true, false, {}));
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "",
-                             {{"pool", "abc"}, {"namespace", "defghi"}},
-                             true, true, false, {}));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, {}, "", "rbd_support", "", {}, true, true, false, {}));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, {}, "", "rbd_support", "", {{"pool", "abc"}}, true, true, false,
+      {}));
+  ASSERT_TRUE(cap.is_capable(
+      nullptr, {}, "", "rbd_support", "",
+      {{"pool", "abc"}, {"namespace", "defghi"}}, true, true, false, {}));
 
   ASSERT_TRUE(cap.parse("profile rbd-read-only", nullptr));
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false,
-                              false, {}));
-  ASSERT_FALSE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
-                              true, false, {}));
-  ASSERT_TRUE(cap.is_capable(nullptr, {}, "", "rbd_support", "", {}, true,
-                             false, false, {}));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "abc", "", {}, true, false, false, {}));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, {}, "", "rbd_support", "", {}, true, true, false, {}));
+  ASSERT_TRUE(cap.is_capable(
+      nullptr, {}, "", "rbd_support", "", {}, true, false, false, {}));
+}
+
+/* Begin Negative Tests */
+
+TEST(MgrCap, IsCapableEmptyCommand)
+{
+  MgrCap cap;
+  ASSERT_TRUE(cap.parse("allow command abc", nullptr));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "", "", {}, true, false, false, {}));
 }
+
+TEST(MgrCap, IsCapableEmptyModule)
+{
+  MgrCap cap;
+  ASSERT_TRUE(cap.parse("allow module xyz r", nullptr));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "", "", {}, true, false, false, {}));
+}
+
+TEST(MgrCap, IsCapableEmptyService)
+{
+  MgrCap cap;
+  ASSERT_TRUE(cap.parse("allow service foo r", nullptr));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "", "", {}, true, false, false, {}));
+}
+
+TEST(MgrCap, CommandEmptyArguments)
+{
+  MgrCap cap;
+  ASSERT_TRUE(cap.parse("allow command abc with arg=foo", nullptr));
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "", "abc", {}, true, false, false, {}));
+}
+
+TEST(MgrCap, CommandMismatchedArguments)
+{
+  MgrCap cap;
+  ASSERT_TRUE(cap.parse("allow command abc with arg=foo arg2=bar", nullptr));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, {}, "", "", "abc", {{"arg", "foo"}}, true, false, false, {}));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, {}, "", "", "abc", {{"arg", "wrong"}, {"arg2", "bar"}}, true,
+      false, false, {}));
+}
+
+TEST(MgrCap, RegexWithInvalidPattern)
+{
+  MgrCap cap;
+  // Invalid regex should be accepted during parse but fail during matching
+  ASSERT_TRUE(cap.parse("allow command abc with arg regex \"[*\"", nullptr));
+  ASSERT_FALSE(cap.is_capable(
+      nullptr, {}, "", "", "abc", {{"arg", "test"}}, true, false, false, {}));
+}
+
+TEST(MgrCap, PermissionsWithNoCapability)
+{
+  MgrCap cap;
+  ASSERT_TRUE(cap.parse("allow r", nullptr));
+  // Asking for write when only read is granted
+  ASSERT_FALSE(
+      cap.is_capable(nullptr, {}, "", "", "", {}, false, true, false, {}));
+}
+
+/* End Negative Tests */