@@ -76,6 +76,12 @@ pub(crate) enum WarningDisposition {
76
76
FatalWarnings ,
77
77
}
78
78
79
+ #[ derive( Debug , Copy , Clone , Serialize , PartialEq , Eq ) ]
80
+ pub ( crate ) enum RootType {
81
+ Running ,
82
+ Alternative ,
83
+ }
84
+
79
85
#[ derive( Debug , Serialize ) ]
80
86
#[ serde( rename_all = "kebab-case" ) ]
81
87
struct Lint {
@@ -85,6 +91,9 @@ struct Lint {
85
91
#[ serde( skip) ]
86
92
f : LintFn ,
87
93
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 > ,
88
97
}
89
98
90
99
impl Lint {
@@ -98,6 +107,7 @@ impl Lint {
98
107
ty : LintType :: Fatal ,
99
108
f : f,
100
109
description : description,
110
+ root_type : None ,
101
111
}
102
112
}
103
113
@@ -111,6 +121,7 @@ impl Lint {
111
121
ty : LintType :: Warning ,
112
122
f : f,
113
123
description : description,
124
+ root_type : None ,
114
125
}
115
126
}
116
127
}
@@ -128,13 +139,22 @@ pub(crate) fn lint_list(output: impl std::io::Write) -> Result<()> {
128
139
pub ( crate ) fn lint (
129
140
root : & Dir ,
130
141
warning_disposition : WarningDisposition ,
142
+ root_type : RootType ,
131
143
mut output : impl std:: io:: Write ,
132
144
) -> Result < ( ) > {
133
145
let mut fatal = 0usize ;
134
146
let mut warnings = 0usize ;
135
147
let mut passed = 0usize ;
148
+ let mut skipped = 0usize ;
136
149
for lint in LINTS {
137
150
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
+
138
158
let r = match ( lint. f ) ( & root) {
139
159
Ok ( r) => r,
140
160
Err ( e) => anyhow:: bail!( "Unexpected runtime error running lint {name}: {e}" ) ,
@@ -158,6 +178,9 @@ pub(crate) fn lint(
158
178
}
159
179
}
160
180
writeln ! ( output, "Checks passed: {passed}" ) ?;
181
+ if skipped > 0 {
182
+ writeln ! ( output, "Checks skipped: {skipped}" ) ?;
183
+ }
161
184
let fatal = if matches ! ( warning_disposition, WarningDisposition :: FatalWarnings ) {
162
185
fatal + warnings
163
186
} else {
@@ -187,6 +210,30 @@ fn check_var_run(root: &Dir) -> LintResult {
187
210
lint_ok ( )
188
211
}
189
212
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
+
190
237
#[ distributed_slice( LINTS ) ]
191
238
static LINT_ETC_USRUSETC : Lint = Lint :: new_fatal (
192
239
"etc-usretc" ,
@@ -462,10 +509,11 @@ mod tests {
462
509
let root = & passing_fixture ( ) ?;
463
510
let mut out = Vec :: new ( ) ;
464
511
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 ( ) ;
466
514
root. create_dir_all ( "var/run/foo" ) ?;
467
515
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( ) ) ;
469
517
Ok ( ( ) )
470
518
}
471
519
@@ -639,6 +687,18 @@ mod tests {
639
687
Ok ( ( ) )
640
688
}
641
689
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
+
642
702
#[ test]
643
703
fn test_list ( ) {
644
704
let mut r = Vec :: new ( ) ;
0 commit comments