• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2018 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  *
5  * Test the minijail0 CLI using gtest.
6  *
7  * Note: We don't verify that the minijail struct was set correctly from these
8  * flags as only libminijail.c knows that definition.  If we wanted to improve
9  * this test, we'd have to pull that struct into a common (internal) header.
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 
15 #include <gtest/gtest.h>
16 
17 #include "config_parser.h"
18 #include "libminijail.h"
19 #include "minijail0_cli.h"
20 #include "test_util.h"
21 
22 namespace {
23 
24 constexpr char kValidUser[] = "nobody";
25 constexpr char kValidUid[] = "100";
26 constexpr char kValidGroup[] = "users";
27 constexpr char kValidGid[] = "100";
28 
29 class CliTest : public ::testing::Test {
30  protected:
SetUp()31   virtual void SetUp() {
32     // Most tests do not care about this logic.  For the few that do, make
33     // them opt into it so they can validate specifically.
34     elftype_ = ELFDYNAMIC;
35   }
TearDown()36   virtual void TearDown() {}
37 
38   // We use a vector of strings rather than const char * pointers because we
39   // need the backing memory to be writable.  The CLI might mutate the strings
40   // as it parses things (which is normally permissible with argv).
parse_args_(const std::vector<std::string> & argv,int * exit_immediately,ElfType * elftype)41   int parse_args_(const std::vector<std::string>& argv,
42                   int* exit_immediately,
43                   ElfType* elftype) {
44     // Make sure we reset the getopts state when scanning a new argv.  Setting
45     // this to 0 is a GNU extension, but AOSP/BSD also checks this (as an alias
46     // to their "optreset").
47     optind = 0;
48 
49     // We create & destroy this for every parse_args call because some API
50     // calls can dupe memory which confuses LSAN.  https://crbug.com/844615
51     struct minijail *j = minijail_new();
52 
53     std::vector<const char *> pargv;
54     pargv.push_back("minijail0");
55     for (const std::string& arg : argv)
56       pargv.push_back(arg.c_str());
57 
58     // We grab stdout from parse_args itself as it might dump things we don't
59     // usually care about like help output.
60     testing::internal::CaptureStdout();
61 
62     const char* preload_path = PRELOADPATH;
63     char **envp = NULL;
64     int ret =
65         parse_args(j, pargv.size(), const_cast<char* const*>(pargv.data()),
66                    NULL, exit_immediately, elftype, &preload_path, &envp);
67     testing::internal::GetCapturedStdout();
68 
69     minijail_destroy(j);
70 
71     return ret;
72   }
73 
parse_args_(const std::vector<std::string> & argv)74   int parse_args_(const std::vector<std::string>& argv) {
75     return parse_args_(argv, &exit_immediately_, &elftype_);
76   }
77 
78   ElfType elftype_;
79   int exit_immediately_;
80 };
81 
82 }  // namespace
83 
84 // Should exit non-zero when there's no arguments.
TEST_F(CliTest,no_args)85 TEST_F(CliTest, no_args) {
86   std::vector<std::string> argv = {};
87   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
88 }
89 
90 // Should exit zero when we asked for help.
TEST_F(CliTest,help)91 TEST_F(CliTest, help) {
92   std::vector<std::string> argv = {"-h"};
93   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
94 
95   argv = {"--help"};
96   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
97 
98   argv = {"-H"};
99   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
100 }
101 
102 // Just a simple program to run.
TEST_F(CliTest,valid_program)103 TEST_F(CliTest, valid_program) {
104   std::vector<std::string> argv = {"/bin/sh"};
105   ASSERT_TRUE(parse_args_(argv));
106 }
107 
108 // Valid calls to the change user option.
TEST_F(CliTest,valid_set_user)109 TEST_F(CliTest, valid_set_user) {
110   std::vector<std::string> argv = {"-u", "", "/bin/sh"};
111 
112   argv[1] = kValidUser;
113   ASSERT_TRUE(parse_args_(argv));
114 
115   argv[1] = kValidUid;
116   ASSERT_TRUE(parse_args_(argv));
117 }
118 
119 // Invalid calls to the change user option.
TEST_F(CliTest,invalid_set_user)120 TEST_F(CliTest, invalid_set_user) {
121   std::vector<std::string> argv = {"-u", "", "/bin/sh"};
122   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
123 
124   argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j";
125   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
126 
127   argv[1] = "1000x";
128   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
129 
130   // Supplying -u more than once is bad.
131   argv = {"-u", kValidUser, "-u", kValidUid, "/bin/sh"};
132   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1),
133               "-u provided multiple times");
134 }
135 
136 // Valid calls to the change group option.
TEST_F(CliTest,valid_set_group)137 TEST_F(CliTest, valid_set_group) {
138   std::vector<std::string> argv = {"-g", "", "/bin/sh"};
139 
140   argv[1] = kValidGroup;
141   ASSERT_TRUE(parse_args_(argv));
142 
143   argv[1] = kValidGid;
144   ASSERT_TRUE(parse_args_(argv));
145 }
146 
147 // Invalid calls to the change group option.
TEST_F(CliTest,invalid_set_group)148 TEST_F(CliTest, invalid_set_group) {
149   std::vector<std::string> argv = {"-g", "", "/bin/sh"};
150   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
151 
152   argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j";
153   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
154 
155   argv[1] = "1000x";
156   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
157 
158   // Supplying -g more than once is bad.
159   argv = {"-g", kValidGroup, "-g", kValidGid, "/bin/sh"};
160   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1),
161               "-g provided multiple times");
162 }
163 
164 // Valid calls to the add-suppl-group option.
TEST_F(CliTest,valid_add_supp_group)165 TEST_F(CliTest, valid_add_supp_group) {
166   std::vector<std::string> argv = {"--add-suppl-group", "", "/bin/sh"};
167 
168   argv[1] = kValidGroup;
169   ASSERT_TRUE(parse_args_(argv));
170 
171   argv[1] = kValidGid;
172   ASSERT_TRUE(parse_args_(argv));
173 
174   std::vector<std::string> argv2 = {"--add-suppl-group", "",
175                                     "--add-suppl-group", "", "/bin/sh"};
176   argv[1] = kValidGroup;
177   argv[2] = kValidGid;
178   ASSERT_TRUE(parse_args_(argv));
179 }
180 
181 // Invalid calls to the add-suppl-group option.
TEST_F(CliTest,invalid_add_supp_group)182 TEST_F(CliTest, invalid_add_supp_group) {
183   std::vector<std::string> argv = {"--add-suppl-group", "", "/bin/sh"};
184 
185   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
186 
187   argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j";
188   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
189 
190   argv[1] = "1000x";
191   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
192 }
193 
194 // Valid calls to the skip securebits option.
TEST_F(CliTest,valid_skip_securebits)195 TEST_F(CliTest, valid_skip_securebits) {
196   // An empty string is the same as 0.
197   std::vector<std::string> argv = {"-B", "", "/bin/sh"};
198   ASSERT_TRUE(parse_args_(argv));
199 
200   argv[1] = "0xAB";
201   ASSERT_TRUE(parse_args_(argv));
202 
203   argv[1] = "1234";
204   ASSERT_TRUE(parse_args_(argv));
205 }
206 
207 // Invalid calls to the skip securebits option.
TEST_F(CliTest,invalid_skip_securebits)208 TEST_F(CliTest, invalid_skip_securebits) {
209   std::vector<std::string> argv = {"-B", "", "/bin/sh"};
210 
211   argv[1] = "xja";
212   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
213 }
214 
215 // Valid calls to the caps option.
TEST_F(CliTest,valid_caps)216 TEST_F(CliTest, valid_caps) {
217   // An empty string is the same as 0.
218   std::vector<std::string> argv = {"-c", "", "/bin/sh"};
219   ASSERT_TRUE(parse_args_(argv));
220 
221   argv[1] = "0xAB";
222   ASSERT_TRUE(parse_args_(argv));
223 
224   argv[1] = "1234";
225   ASSERT_TRUE(parse_args_(argv));
226 }
227 
228 // Invalid calls to the caps option.
TEST_F(CliTest,invalid_caps)229 TEST_F(CliTest, invalid_caps) {
230   std::vector<std::string> argv = {"-c", "", "/bin/sh"};
231 
232   argv[1] = "xja";
233   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
234 }
235 
236 // Valid calls to the logging option.
TEST_F(CliTest,valid_logging)237 TEST_F(CliTest, valid_logging) {
238   std::vector<std::string> argv = {"--logging", "", "/bin/sh"};
239 
240   // This should list all valid logging targets.
241   const std::vector<std::string> profiles = {
242     "stderr",
243     "syslog",
244   };
245 
246   for (const auto& profile : profiles) {
247     argv[1] = profile;
248     ASSERT_TRUE(parse_args_(argv));
249   }
250 }
251 
252 // Invalid calls to the logging option.
TEST_F(CliTest,invalid_logging)253 TEST_F(CliTest, invalid_logging) {
254   std::vector<std::string> argv = {"--logging", "", "/bin/sh"};
255   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
256 
257   argv[1] = "stdout";
258   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
259 }
260 
261 // Valid calls to the rlimit option.
TEST_F(CliTest,valid_rlimit)262 TEST_F(CliTest, valid_rlimit) {
263   std::vector<std::string> argv = {"-R", "", "/bin/sh"};
264 
265   argv[1] = "0,1,2";
266   ASSERT_TRUE(parse_args_(argv));
267 
268   argv[1] = "0,0x100,4";
269   ASSERT_TRUE(parse_args_(argv));
270 
271   argv[1] = "1,1,unlimited";
272   ASSERT_TRUE(parse_args_(argv));
273 
274   argv[1] = "2,unlimited,2";
275   ASSERT_TRUE(parse_args_(argv));
276 
277   argv[1] = "RLIMIT_AS,unlimited,unlimited";
278   ASSERT_TRUE(parse_args_(argv));
279 }
280 
281 // Invalid calls to the rlimit option.
TEST_F(CliTest,invalid_rlimit)282 TEST_F(CliTest, invalid_rlimit) {
283   std::vector<std::string> argv = {"-R", "", "/bin/sh"};
284   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
285 
286   // Missing cur & max.
287   argv[1] = "0";
288   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
289 
290   // Missing max.
291   argv[1] = "0,0";
292   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
293 
294   // Too many options.
295   argv[1] = "0,0,0,0";
296   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
297 
298   // Non-numeric limits
299   argv[1] = "0,0,invalid-limit";
300   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
301 
302   // Invalid number.
303   argv[1] = "0,0,0j";
304   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
305 
306   // Invalid hex number.
307   argv[1] = "0,0x1jf,0";
308   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
309 
310   // Invalid rlimit constant.
311   argv[1] = "RLIMIT_GOGOOGOG,0,0";
312   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
313 }
314 
315 // Valid calls to the profile option.
TEST_F(CliTest,valid_profile)316 TEST_F(CliTest, valid_profile) {
317   std::vector<std::string> argv = {"--profile", "", "/bin/sh"};
318 
319   // This should list all valid profiles.
320   const std::vector<std::string> profiles = {
321     "minimalistic-mountns",
322     "minimalistic-mountns-nodev",
323   };
324 
325   for (const auto& profile : profiles) {
326     argv[1] = profile;
327     ASSERT_TRUE(parse_args_(argv));
328   }
329 }
330 
331 // Invalid calls to the profile option.
TEST_F(CliTest,invalid_profile)332 TEST_F(CliTest, invalid_profile) {
333   std::vector<std::string> argv = {"--profile", "", "/bin/sh"};
334   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
335 
336   argv[1] = "random-unknown-profile";
337   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
338 }
339 
340 // Valid calls to the chroot option.
TEST_F(CliTest,valid_chroot)341 TEST_F(CliTest, valid_chroot) {
342   std::vector<std::string> argv = {"-C", "/", "/bin/sh"};
343   ASSERT_TRUE(parse_args_(argv));
344 }
345 
346 // Valid calls to the pivot root option.
TEST_F(CliTest,valid_pivot_root)347 TEST_F(CliTest, valid_pivot_root) {
348   std::vector<std::string> argv = {"-P", "/", "/bin/sh"};
349   ASSERT_TRUE(parse_args_(argv));
350 }
351 
352 // We cannot handle multiple options with chroot/profile/pivot root.
TEST_F(CliTest,conflicting_roots)353 TEST_F(CliTest, conflicting_roots) {
354   std::vector<std::string> argv;
355 
356   // Chroot & pivot root.
357   argv = {"-C", "/", "-P", "/", "/bin/sh"};
358   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
359 
360   // Chroot & minimalistic-mountns profile.
361   argv = {"-C", "/", "--profile", "minimalistic-mountns", "/bin/sh"};
362   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
363 
364   // Pivot root & minimalistic-mountns profile.
365   argv = {"-P", "/", "--profile", "minimalistic-mountns", "/bin/sh"};
366   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
367 }
368 
369 // Valid calls to the uidmap option.
TEST_F(CliTest,valid_uidmap)370 TEST_F(CliTest, valid_uidmap) {
371   std::vector<std::string> argv = {"-m", "/bin/sh"};
372   // Use a default map (no option from user).
373   ASSERT_TRUE(parse_args_(argv));
374 
375   // Use a single map.
376   argv = {"-m0 0 1", "/bin/sh"};
377   ASSERT_TRUE(parse_args_(argv));
378 
379   // Multiple maps.
380   argv = {"-m0 0 1,100 100 1", "/bin/sh"};
381   ASSERT_TRUE(parse_args_(argv));
382 }
383 
384 // Valid calls to the gidmap option.
TEST_F(CliTest,valid_gidmap)385 TEST_F(CliTest, valid_gidmap) {
386   std::vector<std::string> argv = {"-M", "/bin/sh"};
387   // Use a default map (no option from user).
388   ASSERT_TRUE(parse_args_(argv));
389 
390   // Use a single map.
391   argv = {"-M0 0 1", "/bin/sh"};
392   ASSERT_TRUE(parse_args_(argv));
393 
394   // Multiple maps.
395   argv = {"-M0 0 1,100 100 1", "/bin/sh"};
396   ASSERT_TRUE(parse_args_(argv));
397 }
398 
399 // Invalid calls to the uidmap/gidmap options.
400 // Note: Can't really test these as all validation is delayed/left to the
401 // runtime kernel.  Minijail will simply write verbatim what the user gave
402 // it to the corresponding /proc/.../[ug]id_map.
403 
404 // Valid calls to the binding option.
TEST_F(CliTest,valid_binding)405 TEST_F(CliTest, valid_binding) {
406   std::vector<std::string> argv = {"-v", "-b", "", "/bin/sh"};
407 
408   // Dest & writable are optional.
409   argv[1] = "/";
410   ASSERT_TRUE(parse_args_(argv));
411 
412   // Writable is optional.
413   argv[1] = "/,/";
414   ASSERT_TRUE(parse_args_(argv));
415 
416   // Writable is an integer.
417   argv[1] = "/,/,0";
418   ASSERT_TRUE(parse_args_(argv));
419   argv[1] = "/,/,1";
420   ASSERT_TRUE(parse_args_(argv));
421 
422   // Dest is optional.
423   argv[1] = "/,,0";
424   ASSERT_TRUE(parse_args_(argv));
425 }
426 
427 // Invalid calls to the binding option.
TEST_F(CliTest,invalid_binding)428 TEST_F(CliTest, invalid_binding) {
429   std::vector<std::string> argv = {"-v", "-b", "", "/bin/sh"};
430 
431   // Missing source.
432   argv[2] = "";
433   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
434 
435   // Too many args.
436   argv[2] = "/,/,0,what";
437   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
438 
439   // Missing mount namespace/etc...
440   argv = {"-b", "/", "/bin/sh"};
441   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
442 
443   // Bad value for <writable>.
444   argv = {"-b", "/,,writable", "/bin/sh"};
445   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
446 }
447 
448 // Valid calls to the mount option.
TEST_F(CliTest,valid_mount)449 TEST_F(CliTest, valid_mount) {
450   std::vector<std::string> argv = {"-v", "-k", "", "/bin/sh"};
451 
452   // Flags & data are optional.
453   argv[2] = "none,/,none";
454   ASSERT_TRUE(parse_args_(argv));
455 
456   // Data is optional.
457   argv[2] = "none,/,none,0xe";
458   ASSERT_TRUE(parse_args_(argv));
459 
460   // Flags are optional.
461   argv[2] = "none,/,none,,mode=755";
462   ASSERT_TRUE(parse_args_(argv));
463 
464   // Multiple data options to the kernel.
465   argv[2] = "none,/,none,0xe,mode=755,uid=0,gid=10";
466   ASSERT_TRUE(parse_args_(argv));
467 
468   // Single MS constant.
469   argv[2] = "none,/,none,MS_NODEV,mode=755";
470   ASSERT_TRUE(parse_args_(argv));
471 
472   // Multiple MS constants.
473   argv[2] = "none,/,none,MS_NODEV|MS_NOEXEC,mode=755";
474   ASSERT_TRUE(parse_args_(argv));
475 
476   // Mixed constant & number.
477   argv[2] = "none,/,none,MS_NODEV|0xf,mode=755";
478   ASSERT_TRUE(parse_args_(argv));
479 }
480 
481 // Invalid calls to the mount option.
TEST_F(CliTest,invalid_mount)482 TEST_F(CliTest, invalid_mount) {
483   std::vector<std::string> argv = {"-v", "-k", "", "/bin/sh"};
484 
485   // Missing source.
486   argv[2] = "";
487   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
488 
489   // Missing dest.
490   argv[2] = "none";
491   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
492 
493   // Missing type.
494   argv[2] = "none,/";
495   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
496 
497   // Unknown MS constant.
498   argv[2] = "none,/,none,MS_WHOOPS";
499   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
500 }
501 
502 // Valid calls to the remount mode option.
TEST_F(CliTest,valid_remount_mode)503 TEST_F(CliTest, valid_remount_mode) {
504   std::vector<std::string> argv = {"-v", "", "/bin/sh"};
505 
506   // Mode is optional.
507   argv[1] = "-K";
508   ASSERT_TRUE(parse_args_(argv));
509 
510   // This should list all valid modes.
511   const std::vector<std::string> modes = {
512     "shared",
513     "private",
514     "slave",
515     "unbindable",
516   };
517 
518   for (const auto& mode : modes) {
519     argv[1] = "-K" + mode;
520     ASSERT_TRUE(parse_args_(argv));
521   }
522 }
523 
524 // Invalid calls to the remount mode option.
TEST_F(CliTest,invalid_remount_mode)525 TEST_F(CliTest, invalid_remount_mode) {
526   std::vector<std::string> argv = {"-v", "", "/bin/sh"};
527 
528   // Unknown mode.
529   argv[1] = "-Kfoo";
530   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
531 }
532 
TEST_F(CliTest,invalid_L_combo)533 TEST_F(CliTest, invalid_L_combo) {
534   std::vector<std::string> argv = {"", "", "", "/bin/sh"};
535 
536   // Cannot call minijail0 with -L and a pre-compiled seccomp policy.
537   argv[0] = "-L";
538   argv[1] = "--seccomp-bpf-binary";
539   argv[2] = "source";
540   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
541 
542   argv[0] = "--seccomp-bpf-binary";
543   argv[1] = "source";
544   argv[2] = "-L";
545   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
546 }
547 
548 // Valid calls to the clear env option.
TEST_F(CliTest,valid_clear_env)549 TEST_F(CliTest, valid_clear_env) {
550   std::vector<std::string> argv = {"--env-reset", "/bin/sh"};
551 
552   ASSERT_TRUE(parse_args_(argv));
553 }
554 
555 // Valid calls to the set env option.
TEST_F(CliTest,valid_set_env)556 TEST_F(CliTest, valid_set_env) {
557   std::vector<std::string> argv1 = {"--env-add", "NAME=value", "/bin/sh"};
558   ASSERT_TRUE(parse_args_(argv1));
559 
560   // multiple occurences are allowed.
561   std::vector<std::string> argv2 = {"--env-add", "A=b",
562                                     "--env-add", "b=C=D", "/bin/sh"};
563   ASSERT_TRUE(parse_args_(argv2));
564 
565   // --env-reset before any --env-add to not pass our own env.
566   std::vector<std::string> argv3 = {"--env-reset", "--env-add", "A=b", "/bin/sh"};
567   ASSERT_TRUE(parse_args_(argv3));
568 
569   // --env-add before an --env-reset doesn't have any effect, but is allowed.
570   std::vector<std::string> argv4 = {"--env-add", "A=b", "--env-reset", "/bin/sh"};
571   ASSERT_TRUE(parse_args_(argv4));
572 }
573 
574 // Invalid calls to the set env options.
TEST_F(CliTest,invalid_set_env)575 TEST_F(CliTest, invalid_set_env) {
576 
577   // invalid env=value arguments.
578   std::vector<std::string> argv2 = {"--env-add", "", "/bin/sh"};
579 
580   argv2[1] = "INVALID";
581   ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), "");
582 
583   argv2[1] = "=";
584   ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), "");
585 
586   argv2[1] = "=foo";
587   ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), "");
588 }
589 
590 // Android unit tests do not support data file yet.
591 #if !defined(__ANDROID__)
592 
TEST_F(CliTest,conf_parsing_invalid_key)593 TEST_F(CliTest, conf_parsing_invalid_key) {
594   std::vector<std::string> argv = {"--config", source_path("test/invalid.conf"),
595                                    "/bin/sh"};
596 
597   ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
598 }
599 
TEST_F(CliTest,conf_parsing)600 TEST_F(CliTest, conf_parsing) {
601   std::vector<std::string> argv = {"--config",
602                                    source_path("test/valid.conf"),
603                                    "/bin/sh"};
604 
605   ASSERT_TRUE(parse_args_(argv));
606 }
607 
TEST_F(CliTest,conf_parsing_with_dac_override)608 TEST_F(CliTest, conf_parsing_with_dac_override) {
609   std::vector<std::string> argv = {"-c 2", "--config",
610                                    source_path("test/valid.conf"),
611                                    "/bin/sh"};
612 
613   ASSERT_TRUE(parse_args_(argv));
614 }
615 
TEST_F(CliTest,conf_fs_path)616 TEST_F(CliTest, conf_fs_path) {
617   std::vector<std::string> argv = {"-c 2", "--config",
618                                    source_path("test/landlock.conf"),
619                                    "/bin/sh"};
620 
621   ASSERT_TRUE(parse_args_(argv));
622 }
623 
624 
625 #endif  // !__ANDROID__
626