@@ -165,8 +165,13 @@ pub fn detect_capabilities(_platform: &Platform) -> PlatformCapabilities {
165165
166166/// Returns `true` when running inside Windows Subsystem for Linux (WSL1 or WSL2).
167167///
168- /// Three-tier detection: environment variable, WSLInterop file, /proc/version.
168+ /// Returns `false` immediately if a container is detected. Otherwise uses
169+ /// three-tier detection: environment variable, WSLInterop file, /proc/version.
169170fn is_wsl ( ) -> bool {
171+ if is_container ( ) {
172+ return false ;
173+ }
174+
170175 if std:: env:: var ( "WSL_DISTRO_NAME" ) . is_ok ( ) {
171176 return true ;
172177 }
@@ -182,12 +187,39 @@ fn is_wsl() -> bool {
182187
183188/// Returns `true` only when running inside WSL2 (not WSL1).
184189///
185- /// Detection: `/proc/sys/fs/binfmt_misc/WSLInterop` file exists in WSL2 but
186- /// is absent in WSL1.
190+ /// Returns `false` immediately if a container is detected. Otherwise checks
191+ /// whether `/proc/sys/fs/binfmt_misc/WSLInterop` exists (present in WSL2,
192+ /// absent in WSL1).
187193fn is_wsl2 ( ) -> bool {
194+ if is_container ( ) {
195+ return false ;
196+ }
197+
188198 std:: path:: Path :: new ( "/proc/sys/fs/binfmt_misc/WSLInterop" ) . exists ( )
189199}
190200
201+ /// Returns `true` when running inside a container (Docker, Podman, etc.).
202+ ///
203+ /// Checks four indicators in priority order: /.dockerenv file, DOCKER_CONTAINER
204+ /// env var, container env var (OCI standard), and /proc/1/cgroup contents.
205+ fn is_container ( ) -> bool {
206+ if std:: path:: Path :: new ( "/.dockerenv" ) . exists ( ) {
207+ return true ;
208+ }
209+
210+ if std:: env:: var ( "DOCKER_CONTAINER" ) . is_ok ( ) {
211+ return true ;
212+ }
213+
214+ if std:: env:: var ( "container" ) . is_ok ( ) {
215+ return true ;
216+ }
217+
218+ std:: fs:: read_to_string ( "/proc/1/cgroup" )
219+ . map ( |c| c. contains ( "docker" ) )
220+ . unwrap_or ( false )
221+ }
222+
191223/// Parse the `ID=` field from `/etc/os-release` into a `LinuxDistro`.
192224fn detect_linux_distro ( ) -> LinuxDistro {
193225 let content = match std:: fs:: read_to_string ( "/etc/os-release" ) {
@@ -299,8 +331,32 @@ trait OsProbe {
299331 fn command_output ( & self , cmd : & str , args : & [ & str ] ) -> Option < String > ;
300332}
301333
334+ #[ cfg( test) ]
335+ fn is_container_with_probe ( probe : & dyn OsProbe ) -> bool {
336+ if probe. path_exists ( "/.dockerenv" ) {
337+ return true ;
338+ }
339+
340+ if probe. env_var ( "DOCKER_CONTAINER" ) . is_some ( ) {
341+ return true ;
342+ }
343+
344+ if probe. env_var ( "container" ) . is_some ( ) {
345+ return true ;
346+ }
347+
348+ probe
349+ . read_file ( "/proc/1/cgroup" )
350+ . map ( |c| c. contains ( "docker" ) )
351+ . unwrap_or ( false )
352+ }
353+
302354#[ cfg( test) ]
303355fn is_wsl_with_probe ( probe : & dyn OsProbe ) -> bool {
356+ if is_container_with_probe ( probe) {
357+ return false ;
358+ }
359+
304360 if probe. env_var ( "WSL_DISTRO_NAME" ) . is_some ( ) {
305361 return true ;
306362 }
@@ -317,6 +373,10 @@ fn is_wsl_with_probe(probe: &dyn OsProbe) -> bool {
317373
318374#[ cfg( test) ]
319375fn is_wsl2_with_probe ( probe : & dyn OsProbe ) -> bool {
376+ if is_container_with_probe ( probe) {
377+ return false ;
378+ }
379+
320380 probe. path_exists ( "/proc/sys/fs/binfmt_misc/WSLInterop" )
321381}
322382
@@ -600,4 +660,165 @@ mod tests {
600660 let probe = MockProbe :: new ( ) ;
601661 assert_eq ! ( detect_shell_with_probe( & probe) , "unknown" ) ;
602662 }
663+
664+ // -----------------------------------------------------------------------
665+ // Container detection tests (tests 25-36)
666+ // -----------------------------------------------------------------------
667+
668+ #[ test]
669+ fn test_container_detected_from_dockerenv ( ) {
670+ let mut probe = MockProbe :: new ( ) ;
671+ probe. paths . insert ( "/.dockerenv" . into ( ) , true ) ;
672+ assert ! ( is_container_with_probe( & probe) ) ;
673+ }
674+
675+ #[ test]
676+ fn test_container_detected_from_docker_container_env ( ) {
677+ let mut probe = MockProbe :: new ( ) ;
678+ probe
679+ . env_vars
680+ . insert ( "DOCKER_CONTAINER" . into ( ) , "true" . into ( ) ) ;
681+ assert ! ( is_container_with_probe( & probe) ) ;
682+ }
683+
684+ #[ test]
685+ fn test_container_detected_from_container_env ( ) {
686+ let mut probe = MockProbe :: new ( ) ;
687+ probe
688+ . env_vars
689+ . insert ( "container" . into ( ) , "podman" . into ( ) ) ;
690+ assert ! ( is_container_with_probe( & probe) ) ;
691+ }
692+
693+ #[ test]
694+ fn test_container_detected_from_cgroup ( ) {
695+ let mut probe = MockProbe :: new ( ) ;
696+ probe. files . insert (
697+ "/proc/1/cgroup" . into ( ) ,
698+ "12:memory:/docker/abc123def456\n 11:cpuset:/docker/abc123def456\n " . into ( ) ,
699+ ) ;
700+ assert ! ( is_container_with_probe( & probe) ) ;
701+ }
702+
703+ #[ test]
704+ fn test_not_container_when_no_indicators ( ) {
705+ let probe = MockProbe :: new ( ) ;
706+ assert ! ( !is_container_with_probe( & probe) ) ;
707+ }
708+
709+ #[ test]
710+ fn test_wsl_false_when_dockerenv_present ( ) {
711+ let mut probe = MockProbe :: new ( ) ;
712+ // WSL indicators
713+ probe
714+ . paths
715+ . insert ( "/proc/sys/fs/binfmt_misc/WSLInterop" . into ( ) , true ) ;
716+ probe. files . insert (
717+ "/proc/version" . into ( ) ,
718+ "Linux version 6.6.87.2-microsoft-standard-WSL2" . into ( ) ,
719+ ) ;
720+ // Container indicator
721+ probe. paths . insert ( "/.dockerenv" . into ( ) , true ) ;
722+
723+ assert ! ( !is_wsl_with_probe( & probe) ) ;
724+ }
725+
726+ #[ test]
727+ fn test_wsl_false_when_container_env_set ( ) {
728+ let mut probe = MockProbe :: new ( ) ;
729+ // WSL indicators -- all three tiers
730+ probe
731+ . env_vars
732+ . insert ( "WSL_DISTRO_NAME" . into ( ) , "Ubuntu" . into ( ) ) ;
733+ probe
734+ . paths
735+ . insert ( "/proc/sys/fs/binfmt_misc/WSLInterop" . into ( ) , true ) ;
736+ probe. files . insert (
737+ "/proc/version" . into ( ) ,
738+ "Linux version 6.6.87.2-microsoft-standard-WSL2" . into ( ) ,
739+ ) ;
740+ // Container indicator
741+ probe
742+ . env_vars
743+ . insert ( "container" . into ( ) , "podman" . into ( ) ) ;
744+
745+ assert ! ( !is_wsl_with_probe( & probe) ) ;
746+ }
747+
748+ #[ test]
749+ fn test_wsl_false_when_docker_container_env_set ( ) {
750+ let mut probe = MockProbe :: new ( ) ;
751+ // WSL indicators
752+ probe
753+ . paths
754+ . insert ( "/proc/sys/fs/binfmt_misc/WSLInterop" . into ( ) , true ) ;
755+ probe. files . insert (
756+ "/proc/version" . into ( ) ,
757+ "Linux version 6.6.87.2-microsoft-standard-WSL2" . into ( ) ,
758+ ) ;
759+ // Container indicator
760+ probe
761+ . env_vars
762+ . insert ( "DOCKER_CONTAINER" . into ( ) , "1" . into ( ) ) ;
763+
764+ assert ! ( !is_wsl_with_probe( & probe) ) ;
765+ }
766+
767+ #[ test]
768+ fn test_wsl_true_genuine_wsl2_no_container ( ) {
769+ let mut probe = MockProbe :: new ( ) ;
770+ probe
771+ . env_vars
772+ . insert ( "WSL_DISTRO_NAME" . into ( ) , "Ubuntu" . into ( ) ) ;
773+ probe
774+ . paths
775+ . insert ( "/proc/sys/fs/binfmt_misc/WSLInterop" . into ( ) , true ) ;
776+ probe. files . insert (
777+ "/proc/version" . into ( ) ,
778+ "Linux version 6.6.87.2-microsoft-standard-WSL2" . into ( ) ,
779+ ) ;
780+
781+ assert ! ( is_wsl_with_probe( & probe) ) ;
782+ }
783+
784+ #[ test]
785+ fn test_wsl2_false_when_container_detected ( ) {
786+ let mut probe = MockProbe :: new ( ) ;
787+ probe
788+ . paths
789+ . insert ( "/proc/sys/fs/binfmt_misc/WSLInterop" . into ( ) , true ) ;
790+ probe. paths . insert ( "/.dockerenv" . into ( ) , true ) ;
791+
792+ assert ! ( !is_wsl2_with_probe( & probe) ) ;
793+ }
794+
795+ #[ test]
796+ fn test_wsl2_true_genuine_wsl2_no_container ( ) {
797+ let mut probe = MockProbe :: new ( ) ;
798+ probe
799+ . paths
800+ . insert ( "/proc/sys/fs/binfmt_misc/WSLInterop" . into ( ) , true ) ;
801+
802+ assert ! ( is_wsl2_with_probe( & probe) ) ;
803+ }
804+
805+ #[ test]
806+ fn test_wsl_false_when_cgroup_contains_docker ( ) {
807+ let mut probe = MockProbe :: new ( ) ;
808+ // WSL indicators
809+ probe
810+ . paths
811+ . insert ( "/proc/sys/fs/binfmt_misc/WSLInterop" . into ( ) , true ) ;
812+ probe. files . insert (
813+ "/proc/version" . into ( ) ,
814+ "Linux version 6.6.87.2-microsoft-standard-WSL2" . into ( ) ,
815+ ) ;
816+ // Only the cgroup indicator
817+ probe. files . insert (
818+ "/proc/1/cgroup" . into ( ) ,
819+ "0::/docker/abc123\n " . into ( ) ,
820+ ) ;
821+
822+ assert ! ( !is_wsl_with_probe( & probe) ) ;
823+ }
603824}
0 commit comments