@@ -70,7 +70,7 @@ use crate::core::compiler::rustdoc::RustdocExternMap;
70
70
use crate :: core:: shell:: Verbosity ;
71
71
use crate :: core:: { features, CliUnstable , Shell , SourceId , Workspace } ;
72
72
use crate :: ops;
73
- use crate :: util:: errors:: CargoResult ;
73
+ use crate :: util:: errors:: { ownership_error , CargoResult } ;
74
74
use crate :: util:: toml as cargo_toml;
75
75
use crate :: util:: validate_package_name;
76
76
use crate :: util:: { FileLock , Filesystem , IntoUrl , IntoUrlWithBase , Rustc } ;
@@ -186,6 +186,7 @@ pub struct Config {
186
186
doc_extern_map : LazyCell < RustdocExternMap > ,
187
187
progress_config : ProgressConfig ,
188
188
env_config : LazyCell < EnvConfig > ,
189
+ safe_directories : LazyCell < HashSet < PathBuf > > ,
189
190
/// This should be false if:
190
191
/// - this is an artifact of the rustc distribution process for "stable" or for "beta"
191
192
/// - this is an `#[test]` that does not opt in with `enable_nightly_features`
@@ -284,6 +285,7 @@ impl Config {
284
285
doc_extern_map : LazyCell :: new ( ) ,
285
286
progress_config : ProgressConfig :: default ( ) ,
286
287
env_config : LazyCell :: new ( ) ,
288
+ safe_directories : LazyCell :: new ( ) ,
287
289
nightly_features_allowed : matches ! ( & * features:: channel( ) , "nightly" | "dev" ) ,
288
290
}
289
291
}
@@ -1057,6 +1059,103 @@ impl Config {
1057
1059
}
1058
1060
}
1059
1061
1062
+ /// Checks if the given path is owned by a different user.
1063
+ fn check_safe_dir ( & self , path : & Path , safe_directories : & HashSet < PathBuf > ) -> CargoResult < ( ) > {
1064
+ if !self . safe_directories_enabled ( ) {
1065
+ return Ok ( ( ) ) ;
1066
+ }
1067
+ paths:: validate_ownership ( path, safe_directories) . map_err ( |e| {
1068
+ match e. downcast_ref :: < paths:: OwnershipError > ( ) {
1069
+ Some ( e) => {
1070
+ let mut to_add = e. path . parent ( ) . unwrap ( ) ;
1071
+ if to_add. file_name ( ) . and_then ( |f| f. to_str ( ) ) == Some ( ".cargo" ) {
1072
+ to_add = to_add. parent ( ) . unwrap ( ) ;
1073
+ }
1074
+ ownership_error ( e, "config files" , to_add, self )
1075
+ }
1076
+ None => e,
1077
+ }
1078
+ } )
1079
+ }
1080
+
1081
+ /// Returns whether or not the nightly-only safe.directories behavior is enabled.
1082
+ pub fn safe_directories_enabled ( & self ) -> bool {
1083
+ // Because the config is loaded before the CLI options are available
1084
+ // (primarily to handle aliases), this can't be gated on a `-Z` flag.
1085
+ // So for now, this is only enabled via an environment variable. This
1086
+ // has some downsides (such as not supporting the "allow" list), but
1087
+ // should be fine for a one-off exception.
1088
+ self . env
1089
+ . get ( "CARGO_UNSTABLE_SAFE_DIRECTORIES" )
1090
+ . map ( |s| s. as_str ( ) )
1091
+ == Some ( "true" )
1092
+ && self . nightly_features_allowed
1093
+ }
1094
+
1095
+ /// Returns the `safe.directories` config setting.
1096
+ pub fn safe_directories ( & self ) -> CargoResult < & HashSet < PathBuf > > {
1097
+ self . safe_directories . try_borrow_with ( || {
1098
+ if !self . safe_directories_enabled ( ) {
1099
+ return Ok ( HashSet :: new ( ) ) ;
1100
+ }
1101
+ let mut safe_directories: HashSet < PathBuf > = self
1102
+ . get_list ( "safe.directories" ) ?
1103
+ . map ( |dirs| {
1104
+ dirs. val
1105
+ . iter ( )
1106
+ . map ( |( s, def) | def. root ( self ) . join ( s) )
1107
+ . collect ( )
1108
+ } )
1109
+ . unwrap_or_default ( ) ;
1110
+ self . extend_safe_directories_env ( & mut safe_directories) ;
1111
+ Ok ( safe_directories)
1112
+ } )
1113
+ }
1114
+
1115
+ fn extend_safe_directories_env ( & self , safe_directories : & mut HashSet < PathBuf > ) {
1116
+ // Note: Unlike other Cargo environment variables, this does not
1117
+ // assume paths relative to the current directory (only absolute paths
1118
+ // are supported). This is intended as an extra layer of safety.
1119
+ if let Some ( dirs) = self . env . get ( "CARGO_SAFE_DIRECTORIES" ) {
1120
+ safe_directories. extend ( env:: split_paths ( dirs) ) ;
1121
+ }
1122
+ if let Some ( dirs) = self . env . get ( "RUSTUP_SAFE_DIRECTORIES" ) {
1123
+ safe_directories. extend ( env:: split_paths ( dirs) ) ;
1124
+ }
1125
+ }
1126
+
1127
+ /// Loads the `safe.directories` setting directly from a `ConfigValue`
1128
+ /// (which should be a Table of the root of the config file).
1129
+ fn safe_directories_from_cv (
1130
+ & self ,
1131
+ root : Option < & ConfigValue > ,
1132
+ ) -> CargoResult < HashSet < PathBuf > > {
1133
+ if !self . safe_directories_enabled ( ) {
1134
+ return Ok ( HashSet :: new ( ) ) ;
1135
+ }
1136
+ let mut safe_directories: HashSet < PathBuf > = root
1137
+ . map ( |root| root. get ( "safe.directories" ) )
1138
+ . transpose ( ) ?
1139
+ . flatten ( )
1140
+ . map ( |cv| cv. list ( "safe.directories" ) )
1141
+ . transpose ( ) ?
1142
+ . map ( |dirs| {
1143
+ dirs. iter ( )
1144
+ . map ( |( dir, def) | {
1145
+ if dir != "*" {
1146
+ def. root ( self ) . join ( dir)
1147
+ } else {
1148
+ PathBuf :: from ( dir)
1149
+ }
1150
+ } )
1151
+ . collect ( )
1152
+ } )
1153
+ . unwrap_or_default ( ) ;
1154
+
1155
+ self . extend_safe_directories_env ( & mut safe_directories) ;
1156
+ Ok ( safe_directories)
1157
+ }
1158
+
1060
1159
fn load_file ( & self , path : & Path , includes : bool ) -> CargoResult < ConfigValue > {
1061
1160
self . _load_file ( path, & mut HashSet :: new ( ) , includes)
1062
1161
}
@@ -1281,6 +1380,7 @@ impl Config {
1281
1380
. merge ( tmp_table, true )
1282
1381
. with_context ( || format ! ( "failed to merge --config argument `{arg}`" ) ) ?;
1283
1382
}
1383
+ reject_cli_unsupported ( & loaded_args) ?;
1284
1384
Ok ( loaded_args)
1285
1385
}
1286
1386
@@ -1370,6 +1470,7 @@ impl Config {
1370
1470
} else {
1371
1471
None
1372
1472
} ;
1473
+ let safe_directories = self . safe_directories_from_cv ( home_cv. as_ref ( ) ) ?;
1373
1474
1374
1475
let mut result = Vec :: new ( ) ;
1375
1476
@@ -1380,7 +1481,17 @@ impl Config {
1380
1481
continue ;
1381
1482
}
1382
1483
if let Some ( path) = self . get_file_path ( & dot_cargo, "config" , true ) ? {
1484
+ self . check_safe_dir ( & path, & safe_directories) ?;
1383
1485
let cv = self . _load_file ( & path, & mut seen, includes) ?;
1486
+ if let Some ( safe) = cv. get ( "safe.directories" ) ? {
1487
+ bail ! (
1488
+ "safe.directories may only be configured from Cargo's home directory\n \
1489
+ Found `safe.directories` in {}\n \
1490
+ Cargo's home directory is {}\n ",
1491
+ safe. definition( ) ,
1492
+ home. display( )
1493
+ ) ;
1494
+ }
1384
1495
result. push ( cv) ;
1385
1496
}
1386
1497
}
@@ -1982,6 +2093,28 @@ impl ConfigValue {
1982
2093
self . definition( )
1983
2094
)
1984
2095
}
2096
+
2097
+ /// Retrieve a `ConfigValue` using a dotted key notation.
2098
+ ///
2099
+ /// This is similar to `Config::get`, but can be used directly on a root
2100
+ /// `ConfigValue::Table`. This does *not* look at environment variables.
2101
+ fn get ( & self , key : & str ) -> CargoResult < Option < & ConfigValue > > {
2102
+ let mut key = ConfigKey :: from_str ( key) ;
2103
+ let last = key. pop ( ) ;
2104
+ let ( mut table, _def) = self . table ( "" ) ?;
2105
+ let mut key_so_far = ConfigKey :: new ( ) ;
2106
+ for part in key. parts ( ) {
2107
+ key_so_far. push ( part) ;
2108
+ match table. get ( part) {
2109
+ Some ( cv) => match cv {
2110
+ CV :: Table ( t, _def) => table = t,
2111
+ _ => cv. expected ( "table" , & key_so_far. to_string ( ) ) ?,
2112
+ } ,
2113
+ None => return Ok ( None ) ,
2114
+ }
2115
+ }
2116
+ Ok ( table. get ( & last) )
2117
+ }
1985
2118
}
1986
2119
1987
2120
pub fn homedir ( cwd : & Path ) -> Option < PathBuf > {
@@ -2473,3 +2606,17 @@ macro_rules! drop_eprint {
2473
2606
$crate:: __shell_print!( $config, err, false , $( $arg) * )
2474
2607
) ;
2475
2608
}
2609
+
2610
+ /// Rejects config entries set on the CLI that are not supported.
2611
+ fn reject_cli_unsupported ( root : & CV ) -> CargoResult < ( ) > {
2612
+ let ( root, _) = root. table ( "" ) ?;
2613
+ // safe.directories cannot be set on the CLI because the config is loaded
2614
+ // before the CLI is parsed (primarily to handle aliases).
2615
+ if let Some ( cv) = root. get ( "safe" ) {
2616
+ let ( safe, _) = cv. table ( "safe" ) ?;
2617
+ if safe. contains_key ( "directories" ) {
2618
+ bail ! ( "safe.directories cannot be set via the CLI" ) ;
2619
+ }
2620
+ }
2621
+ Ok ( ( ) )
2622
+ }
0 commit comments