@@ -19,6 +19,7 @@ use nom::IResult;
19
19
use thiserror:: Error ;
20
20
21
21
use crate :: filter:: parse:: Dec :: * ;
22
+ use crate :: filter:: Line ;
22
23
23
24
/// Errors that can occur in this module
24
25
#[ derive( Error , Debug ) ]
@@ -151,8 +152,8 @@ fn ignored_line(l: &str) -> bool {
151
152
l. trim_start ( ) . starts_with ( '#' ) || l. trim ( ) . is_empty ( )
152
153
}
153
154
154
- /// Parse a filter config from the conf lines
155
- pub fn parse ( lines : & [ & str ] ) -> Result < Decider , Error > {
155
+ /// Parse a Decider from the conf lines
156
+ pub fn decider ( lines : & [ & str ] ) -> Result < Decider , Error > {
156
157
use Error :: * ;
157
158
158
159
let mut decider = Decider :: default ( ) ;
@@ -205,6 +206,76 @@ pub fn parse(lines: &[&str]) -> Result<Decider, Error> {
205
206
Ok ( decider)
206
207
}
207
208
209
+ /// Parse Lines entries from the conf lines
210
+ pub fn lines ( input : Vec < String > ) -> Vec < Line > {
211
+ let mut lines = vec ! [ ] ;
212
+
213
+ let mut prev_i = None ;
214
+ let mut stack = vec ! [ ] ;
215
+
216
+ // process lines from the config, ignoring comments and empty lines
217
+ for ( _, line) in input. iter ( ) . enumerate ( ) {
218
+ if line. trim_start ( ) . starts_with ( '#' ) {
219
+ lines. push ( Line :: Comment ( line. to_string ( ) ) )
220
+ } else if line. is_empty ( ) {
221
+ lines. push ( Line :: BlankLine ) ;
222
+ } else {
223
+ match ( prev_i, parse_entry ( line) ) {
224
+ // ensure root level starts with /
225
+ ( _, Ok ( ( 0 , ( k, _) ) ) ) if !k. starts_with ( '/' ) => {
226
+ lines. push ( Line :: Invalid ( "NonAbsRootElement" . to_owned ( ) ) )
227
+ }
228
+ // at the root level, anywhere in the conf
229
+ ( prev, Ok ( ( 0 , ( k, _) ) ) ) => {
230
+ if prev. is_some ( ) {
231
+ stack. clear ( ) ;
232
+ }
233
+ lines. push ( Line :: Valid ( line. to_string ( ) ) ) ;
234
+ stack. push ( PathBuf :: from ( k) ) ;
235
+ prev_i = Some ( 0 ) ;
236
+ }
237
+ // fail if the first conf element is indented
238
+ ( None , Ok ( ( _, ( _, _) ) ) ) => {
239
+ lines. push ( Line :: Invalid ( "TooManyStartIndents" . to_owned ( ) ) )
240
+ }
241
+ // handle indentation
242
+ ( Some ( pi) , Ok ( ( i, ( k, _) ) ) ) if i > pi => {
243
+ let entry = if i - pi == 1 {
244
+ Line :: Valid ( line. to_string ( ) )
245
+ } else {
246
+ Line :: ValidWithWarning (
247
+ line. to_string ( ) ,
248
+ format ! ( "Excessive indent, {} spaces" , i - pi - 1 ) ,
249
+ )
250
+ } ;
251
+ let p = stack. last ( ) . map ( |l| l. join ( k) ) . unwrap ( ) ;
252
+ lines. push ( entry) ;
253
+ stack. push ( p) ;
254
+ prev_i = Some ( i) ;
255
+ }
256
+ // handle unindentation
257
+ ( Some ( pi) , Ok ( ( i, ( k, _) ) ) ) if i < pi => {
258
+ stack. truncate ( i) ;
259
+ let p = stack. last ( ) . map ( |l| l. join ( k) ) . unwrap ( ) ;
260
+ lines. push ( Line :: Valid ( line. to_string ( ) ) ) ;
261
+ stack. push ( p) ;
262
+ prev_i = Some ( i) ;
263
+ }
264
+ // remaining at previous level
265
+ ( Some ( _) , Ok ( ( i, ( k, _) ) ) ) => {
266
+ stack. truncate ( i) ;
267
+ let p = stack. last ( ) . map ( |l| l. join ( k) ) . unwrap ( ) ;
268
+ lines. push ( Line :: Valid ( line. to_string ( ) ) ) ;
269
+ stack. push ( p) ;
270
+ }
271
+ // propagate parse errors
272
+ ( _, Err ( _) ) => lines. push ( Line :: Malformed ( line. to_string ( ) ) ) ,
273
+ }
274
+ }
275
+ }
276
+ lines
277
+ }
278
+
208
279
fn parse_dec ( i : & str ) -> IResult < & str , Dec > {
209
280
alt ( ( map ( tag ( "+" ) , |_| Inc ( 0 ) ) , map ( tag ( "-" ) , |_| Exc ( 0 ) ) ) ) ( i)
210
281
}
@@ -220,12 +291,14 @@ fn parse_entry(i: &str) -> Result<(usize, (&str, Dec)), Error> {
220
291
221
292
#[ cfg( test) ]
222
293
mod tests {
294
+ use std:: default:: Default ;
295
+
223
296
use assert_matches:: assert_matches;
297
+
224
298
use fapolicy_util:: trimto:: TrimTo ;
225
- use std:: default:: Default ;
226
299
227
300
use crate :: filter:: parse:: Dec :: * ;
228
- use crate :: filter:: parse:: { parse , Decider , Error } ;
301
+ use crate :: filter:: parse:: { decider , Decider , Error } ;
229
302
230
303
// the first few tests are modeled after example config from the fapolicyd documentation
231
304
@@ -237,7 +310,7 @@ mod tests {
237
310
|+ /"#
238
311
. trim_to ( '|' ) ;
239
312
240
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
313
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
241
314
assert ! ( d. check( "/" ) ) ;
242
315
assert ! ( !d. check( "/usr/bin/some_binary1" ) ) ;
243
316
assert ! ( !d. check( "/usr/bin/some_binary2" ) ) ;
@@ -252,7 +325,7 @@ mod tests {
252
325
| - some_binary2"#
253
326
. trim_to ( '|' ) ;
254
327
255
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
328
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
256
329
assert ! ( d. check( "/" ) ) ;
257
330
assert ! ( !d. check( "/usr/bin/some_binary1" ) ) ;
258
331
assert ! ( !d. check( "/usr/bin/some_binary2" ) ) ;
@@ -306,7 +379,7 @@ mod tests {
306
379
|"#
307
380
. trim_to ( '|' ) ;
308
381
309
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
382
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
310
383
assert ! ( d. check( "/bin/foo" ) ) ;
311
384
assert ! ( !d. check( "/usr/share/x.txt" ) ) ;
312
385
assert ! ( !d. check( "/usr/include/x.h" ) ) ;
@@ -333,7 +406,7 @@ mod tests {
333
406
| - bar/baz"#
334
407
. trim_to ( '|' ) ;
335
408
336
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
409
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
337
410
assert_matches ! ( d. dec( "/" ) , Inc ( 3 ) ) ;
338
411
assert_matches ! ( d. dec( "/usr/bin/some_binary1" ) , Exc ( 1 ) ) ;
339
412
assert_matches ! ( d. dec( "/usr/bin/some_binary2" ) , Exc ( 2 ) ) ;
@@ -343,21 +416,21 @@ mod tests {
343
416
344
417
#[ test]
345
418
fn too_many_indents ( ) {
346
- assert_matches ! ( parse ( & [ " + foo" ] ) , Err ( Error :: TooManyStartIndents ) ) ;
419
+ assert_matches ! ( decider ( & [ " + foo" ] ) , Err ( Error :: TooManyStartIndents ) ) ;
347
420
}
348
421
349
422
#[ test]
350
423
fn indentation_0_starts_with_slash ( ) {
351
- assert_matches ! ( parse ( & [ "+ x" ] ) , Err ( Error :: NonAbsRootElement ) ) ;
424
+ assert_matches ! ( decider ( & [ "+ x" ] ) , Err ( Error :: NonAbsRootElement ) ) ;
352
425
assert_matches ! (
353
- parse ( & [ "+ /" , " - foo" , "+ bar" ] ) ,
426
+ decider ( & [ "+ /" , " - foo" , "+ bar" ] ) ,
354
427
Err ( Error :: NonAbsRootElement )
355
428
) ;
356
429
}
357
430
358
431
#[ test]
359
432
fn indentation_basic ( ) -> Result < ( ) , Error > {
360
- let d = parse ( & [ "+ /" , " - b" , " + baz" ] ) ?;
433
+ let d = decider ( & [ "+ /" , " - b" , " + baz" ] ) ?;
361
434
assert ! ( d. check( "/a" ) ) ;
362
435
assert ! ( !d. check( "/b" ) ) ;
363
436
assert ! ( d. check( "/b/baz" ) ) ;
@@ -367,7 +440,7 @@ mod tests {
367
440
368
441
#[ test]
369
442
fn indentation_mix ( ) -> Result < ( ) , Error > {
370
- let d = parse ( & [ "+ /" , " - foo/bar" , " + baz" ] ) ?;
443
+ let d = decider ( & [ "+ /" , " - foo/bar" , " + baz" ] ) ?;
371
444
assert ! ( d. check( "/" ) ) ;
372
445
assert ! ( d. check( "/foo" ) ) ;
373
446
assert ! ( !d. check( "/foo/bar" ) ) ;
@@ -378,7 +451,7 @@ mod tests {
378
451
379
452
#[ test]
380
453
fn indentation_nested ( ) -> Result < ( ) , Error > {
381
- let d = parse ( & [ "+ /" , "- /foo/bar" , "+ /foo/bar/baz" ] ) ?;
454
+ let d = decider ( & [ "+ /" , "- /foo/bar" , "+ /foo/bar/baz" ] ) ?;
382
455
assert ! ( d. check( "/" ) ) ;
383
456
assert ! ( d. check( "/foo" ) ) ;
384
457
assert ! ( !d. check( "/foo/bar" ) ) ;
@@ -389,15 +462,15 @@ mod tests {
389
462
390
463
#[ test]
391
464
fn basic ( ) -> Result < ( ) , Error > {
392
- let d = parse ( & [ "+ /" , "- /foo" ] ) ?;
465
+ let d = decider ( & [ "+ /" , "- /foo" ] ) ?;
393
466
assert ! ( d. check( "/" ) ) ;
394
467
assert ! ( !d. check( "/foo" ) ) ;
395
468
Ok ( ( ) )
396
469
}
397
470
398
471
#[ test]
399
472
fn wildcard_single ( ) -> Result < ( ) , Error > {
400
- let d = parse ( & [ "+ /" , " - b?" ] ) ?;
473
+ let d = decider ( & [ "+ /" , " - b?" ] ) ?;
401
474
assert ! ( d. check( "/a" ) ) ;
402
475
assert ! ( d. check( "/b" ) ) ;
403
476
assert ! ( !d. check( "/bb" ) ) ;
@@ -408,7 +481,7 @@ mod tests {
408
481
409
482
#[ test]
410
483
fn wildcard_glob ( ) -> Result < ( ) , Error > {
411
- let d = parse ( & [ "+ /" , " - b" , " - b*" ] ) ?;
484
+ let d = decider ( & [ "+ /" , " - b" , " - b*" ] ) ?;
412
485
assert ! ( d. check( "/a" ) ) ;
413
486
assert ! ( !d. check( "/b" ) ) ;
414
487
assert ! ( !d. check( "/bb" ) ) ;
@@ -419,11 +492,11 @@ mod tests {
419
492
420
493
#[ test]
421
494
fn parse_basic ( ) -> Result < ( ) , Error > {
422
- let d = parse ( & [ "+ /" ] ) ?;
495
+ let d = decider ( & [ "+ /" ] ) ?;
423
496
assert ! ( d. check( "/" ) ) ;
424
497
assert ! ( d. check( "/foo" ) ) ;
425
498
426
- let d = parse ( & [ "+ /foo" ] ) ?;
499
+ let d = decider ( & [ "+ /foo" ] ) ?;
427
500
assert ! ( !d. check( "/" ) ) ;
428
501
assert ! ( d. check( "/foo" ) ) ;
429
502
Ok ( ( ) )
@@ -440,7 +513,7 @@ mod tests {
440
513
| "#
441
514
. trim_to ( '|' ) ;
442
515
443
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
516
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
444
517
assert ! ( !d. check( "/usr/foo/x" ) ) ;
445
518
assert ! ( d. check( "/usr/foo/x.py" ) ) ;
446
519
assert ! ( !d. check( "/usr/bar/x" ) ) ;
@@ -462,7 +535,7 @@ mod tests {
462
535
|+ /"#
463
536
. trim_to ( '|' ) ;
464
537
465
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
538
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
466
539
assert ! ( d. check( "/" ) ) ;
467
540
assert ! ( !d. check( "/usr/bin/some_binary1" ) ) ;
468
541
assert ! ( !d. check( "/usr/bin/some_binary2" ) ) ;
@@ -475,7 +548,7 @@ mod tests {
475
548
| - usr/bin/some_binary*"#
476
549
. trim_to ( '|' ) ;
477
550
478
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
551
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
479
552
assert ! ( d. check( "/" ) ) ;
480
553
assert ! ( !d. check( "/usr/bin/some_binary1" ) ) ;
481
554
assert ! ( !d. check( "/usr/bin/some_binary2" ) ) ;
@@ -490,7 +563,7 @@ mod tests {
490
563
| + *.pl"#
491
564
. trim_to ( '|' ) ;
492
565
493
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
566
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
494
567
assert ! ( d. check( "/usr/bin/ls" ) ) ;
495
568
assert ! ( !d. check( "/usr/share/something" ) ) ;
496
569
assert ! ( d. check( "/usr/share/abcd.py" ) ) ;
@@ -504,7 +577,7 @@ mod tests {
504
577
| + *.py"#
505
578
. trim_to ( '|' ) ;
506
579
507
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
580
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
508
581
assert ! ( !d. check( "/usr/share/abc" ) ) ;
509
582
// assert!(!d.check("/usr/share/abc.py"));
510
583
}
@@ -517,7 +590,7 @@ mod tests {
517
590
| + *.py"#
518
591
. trim_to ( '|' ) ;
519
592
520
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
593
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
521
594
assert ! ( !d. check( "/usr/share/abc" ) ) ;
522
595
assert ! ( !d. check( "/usr/share/abc.pl" ) ) ;
523
596
assert ! ( d. check( "/usr/share/abc.py" ) ) ;
@@ -535,7 +608,7 @@ mod tests {
535
608
|- /z"#
536
609
. trim_to ( '|' ) ;
537
610
538
- let d = parse ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
611
+ let d = decider ( & al. split ( '\n' ) . collect :: < Vec < & str > > ( ) ) . unwrap ( ) ;
539
612
assert ! ( !d. check( "/usr/share/xyz" ) ) ;
540
613
assert ! ( d. check( "/usr/share/abc/def/foo.py" ) ) ;
541
614
assert ! ( !d. check( "/tmp/x" ) ) ;
0 commit comments