Skip to content

Commit c136211

Browse files
authored
Merge pull request #815 from memorysafety/improve-diagnostic
Add path information to parse errors originating from included files.
2 parents 90e516b + a392c00 commit c136211

File tree

4 files changed

+54
-24
lines changed

4 files changed

+54
-24
lines changed

src/sudo/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ impl PolicyPlugin for SudoersPolicy {
4747
let (sudoers, syntax_errors) = crate::sudoers::Sudoers::open(sudoers_path)
4848
.map_err(|e| Error::Configuration(format!("{e}")))?;
4949

50-
for crate::sudoers::Error(pos, error) in syntax_errors {
51-
diagnostic::diagnostic!("{error}", sudoers_path @ pos);
50+
for crate::sudoers::Error {
51+
source,
52+
location,
53+
message,
54+
} in syntax_errors
55+
{
56+
let path = source.as_deref().unwrap_or(sudoers_path);
57+
diagnostic::diagnostic!("{message}", path @ location);
5258
}
5359

5460
Ok(sudoers)

src/sudoers/mod.rs

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ const INCLUDE_LIMIT: u8 = 128;
2525

2626
/// Export some necessary symbols from modules
2727
pub use ast::TextEnum;
28-
pub struct Error(pub Option<basic_parser::Position>, pub String);
28+
pub struct Error {
29+
pub source: Option<PathBuf>,
30+
pub location: Option<basic_parser::Position>,
31+
pub message: String,
32+
}
2933

3034
#[derive(Default)]
3135
pub struct Sudoers {
@@ -587,12 +591,19 @@ fn analyze(
587591
}
588592

589593
impl Sudoers {
590-
fn include(&mut self, path: &Path, diagnostics: &mut Vec<Error>, count: &mut u8) {
594+
fn include(
595+
&mut self,
596+
parent: &Path,
597+
path: &Path,
598+
diagnostics: &mut Vec<Error>,
599+
count: &mut u8,
600+
) {
591601
if *count >= INCLUDE_LIMIT {
592-
diagnostics.push(Error(
593-
None,
594-
format!("include file limit reached opening '{}'", path.display()),
595-
))
602+
diagnostics.push(Error {
603+
source: Some(parent.to_owned()),
604+
location: None,
605+
message: format!("include file limit reached opening '{}'", path.display()),
606+
})
596607
// FIXME: this will cause an error in `visudo` if we open a non-privileged sudoers file
597608
// that includes another non-privileged sudoer files.
598609
} else {
@@ -609,7 +620,11 @@ fn analyze(
609620
e.to_string()
610621
};
611622

612-
diagnostics.push(Error(None, message))
623+
diagnostics.push(Error {
624+
source: Some(parent.to_owned()),
625+
location: None,
626+
message,
627+
})
613628
}
614629
}
615630
}
@@ -641,25 +656,28 @@ fn analyze(
641656
}
642657

643658
Sudo::Include(path) => self.include(
659+
cur_path,
644660
&resolve_relative(cur_path, path),
645661
diagnostics,
646662
safety_count,
647663
),
648664

649665
Sudo::IncludeDir(path) => {
650666
if path.contains("%h") {
651-
diagnostics.push(Error(
652-
None,
653-
format!("cannot open sudoers file {path}: percent escape %h in includedir is unsupported")));
667+
diagnostics.push(Error{
668+
source: Some(cur_path.to_owned()),
669+
location: None,
670+
message: format!("cannot open sudoers file {path}: percent escape %h in includedir is unsupported")});
654671
continue;
655672
}
656673

657674
let path = resolve_relative(cur_path, path);
658675
let Ok(files) = std::fs::read_dir(&path) else {
659-
diagnostics.push(Error(
660-
None,
661-
format!("cannot open sudoers file {}", path.display()),
662-
));
676+
diagnostics.push(Error {
677+
source: Some(cur_path.to_owned()),
678+
location: None,
679+
message: format!("cannot open sudoers file {}", path.display()),
680+
});
663681
continue;
664682
};
665683
let mut safe_files = files
@@ -675,14 +693,16 @@ fn analyze(
675693
.collect::<Vec<_>>();
676694
safe_files.sort();
677695
for file in safe_files {
678-
self.include(file.as_ref(), diagnostics, safety_count)
696+
self.include(cur_path, file.as_ref(), diagnostics, safety_count)
679697
}
680698
}
681699
},
682700

683-
Err(basic_parser::Status::Fatal(pos, error)) => {
684-
diagnostics.push(Error(Some(pos), error))
685-
}
701+
Err(basic_parser::Status::Fatal(pos, message)) => diagnostics.push(Error {
702+
source: Some(cur_path.to_owned()),
703+
location: Some(pos),
704+
message,
705+
}),
686706
Err(_) => panic!("internal parser error"),
687707
}
688708
}
@@ -756,7 +776,11 @@ fn sanitize_alias_table<T>(table: &Vec<Def<T>>, diagnostics: &mut Vec<Error>) ->
756776

757777
impl<T> Visitor<'_, T> {
758778
fn complain(&mut self, text: String) {
759-
self.diagnostics.push(Error(None, text))
779+
self.diagnostics.push(Error {
780+
source: None,
781+
location: None,
782+
message: text,
783+
})
760784
}
761785

762786
fn visit(&mut self, pos: usize) {

src/sudoers/test/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ fn gh676_percent_h_escape_unsupported() {
382382
);
383383
assert_eq!(errs.len(), 1);
384384
assert_eq!(
385-
errs[0].1,
385+
errs[0].message,
386386
"cannot open sudoers file /etc/%h: percent escape %h in includedir is unsupported"
387387
)
388388
}

src/visudo/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ fn check(file_arg: Option<&str>, perms: bool, owner: bool) -> io::Result<()> {
107107
}
108108

109109
let mut stderr = io::stderr();
110-
for crate::sudoers::Error(_position, message) in errors {
110+
for crate::sudoers::Error { message, .. } in errors {
111111
writeln!(stderr, "syntax error: {message}")?;
112112
}
113113

@@ -245,7 +245,7 @@ fn edit_sudoers_file(
245245

246246
writeln!(stderr, "The provided sudoers file format is not recognized or contains syntax errors. Please review:\n")?;
247247

248-
for crate::sudoers::Error(_position, message) in errors {
248+
for crate::sudoers::Error { message, .. } in errors {
249249
writeln!(stderr, "syntax error: {message}")?;
250250
}
251251

0 commit comments

Comments
 (0)