@@ -76,6 +76,12 @@ pub(crate) enum WarningDisposition {
7676 FatalWarnings ,
7777}
7878
79+ #[ derive( Debug , Copy , Clone , Serialize , PartialEq , Eq ) ]
80+ pub ( crate ) enum RootType {
81+ Running ,
82+ Alternative ,
83+ }
84+
7985#[ derive( Debug , Serialize ) ]
8086#[ serde( rename_all = "kebab-case" ) ]
8187struct Lint {
@@ -85,6 +91,9 @@ struct Lint {
8591 #[ serde( skip) ]
8692 f : LintFn ,
8793 description : & ' static str ,
94+ // Set if this only applies to a specific root type.
95+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
96+ root_type : Option < RootType > ,
8897}
8998
9099impl Lint {
@@ -98,6 +107,7 @@ impl Lint {
98107 ty : LintType :: Fatal ,
99108 f : f,
100109 description : description,
110+ root_type : None ,
101111 }
102112 }
103113
@@ -111,6 +121,7 @@ impl Lint {
111121 ty : LintType :: Warning ,
112122 f : f,
113123 description : description,
124+ root_type : None ,
114125 }
115126 }
116127}
@@ -128,13 +139,22 @@ pub(crate) fn lint_list(output: impl std::io::Write) -> Result<()> {
128139pub ( crate ) fn lint (
129140 root : & Dir ,
130141 warning_disposition : WarningDisposition ,
142+ root_type : RootType ,
131143 mut output : impl std:: io:: Write ,
132144) -> Result < ( ) > {
133145 let mut fatal = 0usize ;
134146 let mut warnings = 0usize ;
135147 let mut passed = 0usize ;
148+ let mut skipped = 0usize ;
136149 for lint in LINTS {
137150 let name = lint. name ;
151+
152+ if let Some ( lint_root_type) = lint. root_type {
153+ if lint_root_type != root_type {
154+ skipped += 1 ;
155+ }
156+ }
157+
138158 let r = match ( lint. f ) ( & root) {
139159 Ok ( r) => r,
140160 Err ( e) => anyhow:: bail!( "Unexpected runtime error running lint {name}: {e}" ) ,
@@ -158,6 +178,9 @@ pub(crate) fn lint(
158178 }
159179 }
160180 writeln ! ( output, "Checks passed: {passed}" ) ?;
181+ if skipped > 0 {
182+ writeln ! ( output, "Checks skipped: {skipped}" ) ?;
183+ }
161184 let fatal = if matches ! ( warning_disposition, WarningDisposition :: FatalWarnings ) {
162185 fatal + warnings
163186 } else {
@@ -187,6 +210,30 @@ fn check_var_run(root: &Dir) -> LintResult {
187210 lint_ok ( )
188211}
189212
213+ #[ distributed_slice( LINTS ) ]
214+ static LINT_BUILDAH_INJECTED : Lint = Lint {
215+ name : "buildah-injected" ,
216+ description : indoc:: indoc! { "
217+ Check for an invalid /etc/hostname or /etc/resolv.conf that may have been injected by
218+ a container build system." } ,
219+ ty : LintType :: Warning ,
220+ f : check_buildah_injected,
221+ // This one doesn't make sense to run looking at the running root,
222+ // because we do expect /etc/hostname to be injected as
223+ root_type : Some ( RootType :: Alternative ) ,
224+ } ;
225+ fn check_buildah_injected ( root : & Dir ) -> LintResult {
226+ const RUNTIME_INJECTED : & [ & str ] = & [ "etc/hostname" , "etc/resolv.conf" ] ;
227+ for ent in RUNTIME_INJECTED {
228+ if let Some ( meta) = root. symlink_metadata_optional ( ent) ? {
229+ if meta. is_file ( ) && meta. size ( ) == 0 {
230+ return lint_err ( format ! ( "/{ent} is an empty file; this may have been synthesized by a container runtime." ) ) ;
231+ }
232+ }
233+ }
234+ lint_ok ( )
235+ }
236+
190237#[ distributed_slice( LINTS ) ]
191238static LINT_ETC_USRUSETC : Lint = Lint :: new_fatal (
192239 "etc-usretc" ,
@@ -462,10 +509,11 @@ mod tests {
462509 let root = & passing_fixture ( ) ?;
463510 let mut out = Vec :: new ( ) ;
464511 let warnings = WarningDisposition :: FatalWarnings ;
465- lint ( root, warnings, & mut out) . unwrap ( ) ;
512+ let root_type = RootType :: Running ;
513+ lint ( root, warnings, root_type, & mut out) . unwrap ( ) ;
466514 root. create_dir_all ( "var/run/foo" ) ?;
467515 let mut out = Vec :: new ( ) ;
468- assert ! ( lint( root, warnings, & mut out) . is_err( ) ) ;
516+ assert ! ( lint( root, warnings, root_type , & mut out) . is_err( ) ) ;
469517 Ok ( ( ) )
470518 }
471519
@@ -639,6 +687,18 @@ mod tests {
639687 Ok ( ( ) )
640688 }
641689
690+ #[ test]
691+ fn test_buildah_injected ( ) -> Result < ( ) > {
692+ let td = fixture ( ) ?;
693+ td. create_dir ( "etc" ) ?;
694+ assert ! ( check_buildah_injected( & td) . unwrap( ) . is_ok( ) ) ;
695+ td. write ( "etc/hostname" , b"" ) ?;
696+ assert ! ( check_buildah_injected( & td) . unwrap( ) . is_err( ) ) ;
697+ td. write ( "etc/hostname" , b"some static hostname" ) ?;
698+ assert ! ( check_buildah_injected( & td) . unwrap( ) . is_ok( ) ) ;
699+ Ok ( ( ) )
700+ }
701+
642702 #[ test]
643703 fn test_list ( ) {
644704 let mut r = Vec :: new ( ) ;
0 commit comments