Unified · Split
| @@ -103,6 +103,7 @@ git-testtools = { path = "../tests/tools" } · 102 unmodified lines | ||
| 103 | 103 | is_ci = "1.1.1" |
| 104 | 104 | anyhow = "1" |
| 105 | 105 | tempfile = "3.2.0" |
| 106 | +serial_test = "0.8.0" | |
| 106 | 107 | |
| 107 | 108 | [package.metadata.docs.rs] |
| 108 | 109 | features = ["document-features", "max-performance", "one-stop-shop", "unstable", "blocking-network-client"] |
| @@ -1,9 +1,10 @@ | ||
| 1 | 1 | use std::{convert::TryFrom, path::PathBuf}; |
| 2 | 2 | |
| 3 | +use crate::repository; | |
| 3 | 4 | use git_config::{Boolean, Integer}; |
| 4 | 5 | |
| 5 | 6 | use super::{Cache, Error}; |
| 6 | −use crate::{bstr::ByteSlice, permission}; | |
| 7 | +use crate::bstr::ByteSlice; | |
| 7 | 8 | |
| 8 | 9 | /// A utility to deal with the cyclic dependency between the ref store and the configuration. The ref-store needs the |
| 9 | 10 | /// object hash kind, and the configuration needs the current branch name to resolve conditional includes with `onbranch`. |
| @@ -70,7 +71,7 @@ impl StageOne { · 60 unmodified lines | ||
| 70 | 71 | impl Cache { |
| 71 | 72 | pub fn from_stage_one( |
| 72 | 73 | StageOne { |
| 73 | − git_dir_config: mut config, | |
| 74 | + mut git_dir_config, | |
| 74 | 75 | buf: _, |
| 75 | 76 | is_bare, |
| 76 | 77 | object_hash, |
| @@ -79,15 +80,17 @@ impl Cache { · 2 unmodified lines | ||
| 79 | 80 | git_dir: &std::path::Path, |
| 80 | 81 | branch_name: Option<&git_ref::FullNameRef>, |
| 81 | 82 | mut filter_config_section: fn(&git_config::file::Metadata) -> bool, |
| 82 | − xdg_config_home_env: permission::env_var::Resource, | |
| 83 | − home_env: permission::env_var::Resource, | |
| 84 | 83 | git_install_dir: Option<&std::path::Path>, |
| 84 | + repository::permissions::Environment { | |
| 85 | + git_prefix, | |
| 86 | + home: home_env, | |
| 87 | + xdg_config_home: xdg_config_home_env, | |
| 88 | + }: repository::permissions::Environment, | |
| 85 | 89 | ) -> Result<Self, Error> { |
| 86 | 90 | let home = std::env::var_os("HOME") |
| 87 | 91 | .map(PathBuf::from) |
| 88 | 92 | .and_then(|home| home_env.check(home).ok().flatten()); |
| 89 | − // TODO: don't forget to use the canonicalized home for initializing the stacked config. | |
| 90 | − // like git here: https://github.com/git/git/blob/master/config.c#L208:L208 | |
| 93 | + | |
| 91 | 94 | let options = git_config::file::init::Options { |
| 92 | 95 | lossy: !cfg!(debug_assertions), |
| 93 | 96 | includes: git_config::file::includes::Options::follow( |
| @@ -98,6 +101,55 @@ impl Cache { · 4 unmodified lines | ||
| 98 | 101 | }, |
| 99 | 102 | ), |
| 100 | 103 | }; |
| 104 | + | |
| 105 | + let mut config = { | |
| 106 | + let home_env = &home_env; | |
| 107 | + let xdg_config_home_env = &xdg_config_home_env; | |
| 108 | + let git_prefix = &git_prefix; | |
| 109 | + let metas = [git_config::source::Kind::System, git_config::source::Kind::Global] | |
| 110 | + .iter() | |
| 111 | + .flat_map(|kind| kind.sources()) | |
| 112 | + .filter_map(|source| { | |
| 113 | + let path = source | |
| 114 | + .storage_location(&mut |name| { | |
| 115 | + match name { | |
| 116 | + git_ if git_.starts_with("GIT_") => Some(git_prefix), | |
| 117 | + "XDG_CONFIG_HOME" => Some(xdg_config_home_env), | |
| 118 | + "HOME" => Some(home_env), | |
| 119 | + _ => None, | |
| 120 | + } | |
| 121 | + .and_then(|perm| std::env::var_os(name).and_then(|val| perm.check(val).ok().flatten())) | |
| 122 | + }) | |
| 123 | + .and_then(|p| p.is_file().then(|| p)) // todo: allow it to skip non-existing ones in the options to save check | |
| 124 | + .map(|p| p.into_owned()); | |
| 125 | + | |
| 126 | + git_config::file::Metadata { | |
| 127 | + path, | |
| 128 | + source: *source, | |
| 129 | + level: 0, | |
| 130 | + trust: git_sec::Trust::Full, | |
| 131 | + } | |
| 132 | + .into() | |
| 133 | + }); | |
| 134 | + | |
| 135 | + let no_include_options = git_config::file::init::Options { | |
| 136 | + includes: git_config::file::includes::Options::no_includes(), | |
| 137 | + ..options | |
| 138 | + }; | |
| 139 | + git_dir_config.resolve_includes(options)?; | |
| 140 | + match git_config::File::from_paths_metadata(metas, no_include_options).map_err(|err| match err { | |
| 141 | + git_config::file::init::from_paths::Error::Init(err) => Error::from(err), | |
| 142 | + git_config::file::init::from_paths::Error::Io(err) => err.into(), | |
| 143 | + })? { | |
| 144 | + Some(mut globals) => { | |
| 145 | + globals.resolve_includes(options)?; | |
| 146 | + globals.append(git_dir_config); | |
| 147 | + globals | |
| 148 | + } | |
| 149 | + None => git_dir_config, | |
| 150 | + } | |
| 151 | + }; | |
| 152 | + | |
| 101 | 153 | config.resolve_includes(options)?; |
| 102 | 154 | |
| 103 | 155 | let excludes_file = config |
| @@ -285,9 +285,8 @@ impl ThreadSafeRepository { · 284 unmodified lines | ||
| 285 | 285 | common_dir_ref, |
| 286 | 286 | head.as_ref().and_then(|head| head.target.try_name()), |
| 287 | 287 | filter_config_section.unwrap_or(crate::config::section::is_trusted), |
| 288 | − env.xdg_config_home.clone(), | |
| 289 | − env.home.clone(), | |
| 290 | 288 | crate::path::install_dir().ok().as_deref(), |
| 289 | + env.clone(), | |
| 291 | 290 | )?; |
| 292 | 291 | |
| 293 | 292 | if **git_dir_perm != git_sec::ReadWrite::all() { |
| @@ -26,17 +26,24 @@ pub struct Environment { · 25 unmodified lines | ||
| 26 | 26 | pub git_prefix: permission::env_var::Resource, |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | +impl Environment { | |
| 30 | + /// Allow access to the entire environment. | |
| 31 | + pub fn allow_all() -> Self { | |
| 32 | + Environment { | |
| 33 | + xdg_config_home: Access::resource(git_sec::Permission::Allow), | |
| 34 | + home: Access::resource(git_sec::Permission::Allow), | |
| 35 | + git_prefix: Access::resource(git_sec::Permission::Allow), | |
| 36 | + } | |
| 37 | + } | |
| 38 | +} | |
| 39 | + | |
| 29 | 40 | impl Permissions { |
| 30 | 41 | /// Return permissions similar to what git does when the repository isn't owned by the current user, |
| 31 | 42 | /// thus refusing all operations in it. |
| 32 | 43 | pub fn strict() -> Self { |
| 33 | 44 | Permissions { |
| 34 | 45 | git_dir: Access::resource(git_sec::ReadWrite::READ), |
| 35 | − env: Environment { | |
| 36 | − xdg_config_home: Access::resource(git_sec::Permission::Allow), | |
| 37 | − home: Access::resource(git_sec::Permission::Allow), | |
| 38 | − git_prefix: Access::resource(git_sec::Permission::Allow), | |
| 39 | − }, | |
| 46 | + env: Environment::allow_all(), | |
| 40 | 47 | } |
| 41 | 48 | } |
| 42 | 49 | |
| @@ -48,11 +55,7 @@ impl Permissions { · 5 unmodified lines | ||
| 48 | 55 | pub fn secure() -> Self { |
| 49 | 56 | Permissions { |
| 50 | 57 | git_dir: Access::resource(git_sec::ReadWrite::all()), |
| 51 | − env: Environment { | |
| 52 | − xdg_config_home: Access::resource(git_sec::Permission::Allow), | |
| 53 | − home: Access::resource(git_sec::Permission::Allow), | |
| 54 | − git_prefix: Access::resource(git_sec::Permission::Allow), | |
| 55 | − }, | |
| 58 | + env: Environment::allow_all(), | |
| 56 | 59 | } |
| 57 | 60 | } |
| 58 | 61 | |
| @@ -61,11 +64,7 @@ impl Permissions { · 2 unmodified lines | ||
| 61 | 64 | pub fn all() -> Self { |
| 62 | 65 | Permissions { |
| 63 | 66 | git_dir: Access::resource(git_sec::ReadWrite::all()), |
| 64 | − env: Environment { | |
| 65 | − xdg_config_home: Access::resource(git_sec::Permission::Allow), | |
| 66 | − home: Access::resource(git_sec::Permission::Allow), | |
| 67 | − git_prefix: Access::resource(git_sec::Permission::Allow), | |
| 68 | − }, | |
| 67 | + env: Environment::allow_all(), | |
| 69 | 68 | } |
| 70 | 69 | } |
| 71 | 70 | } |
| @@ -1,3 +1,3 @@ | ||
| 1 | 1 | version https://git-lfs.github.com/spec/v1 |
| 2 | −oid sha256:20b64234f3e8047502160c3c56841b8ddf659798a8b02fc72b5a21bc85dee920 | |
| 3 | −size 9212 | |
| 2 | +oid sha256:5c16dcf86219e5dd5755e21472c40d289b246d62fb92594378f166a4920bad58 | |
| 3 | +size 9276 | |
| @@ -3,7 +3,7 @@ set -eu -o pipefail · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | git init -q |
| 5 | 5 | |
| 6 | −cat <<EOF>>.git/config | |
| 6 | +cat <<EOF >>.git/config | |
| 7 | 7 | [a] |
| 8 | 8 | bool = on |
| 9 | 9 | bad-bool = zero |
| @@ -20,7 +20,19 @@ cat <<EOF>>.git/config · 10 unmodified lines | ||
| 20 | 20 | EOF |
| 21 | 21 | |
| 22 | 22 | |
| 23 | −cat <<EOF>>a.config | |
| 23 | +cat <<EOF >>a.config | |
| 24 | 24 | [a] |
| 25 | 25 | override = from-a.config |
| 26 | 26 | EOF |
| 27 | +cat <<EOF >>b.config | |
| 28 | +[a] | |
| 29 | + system-override = from-b.config | |
| 30 | +EOF | |
| 31 | + | |
| 32 | +cat <<EOF >>system.config | |
| 33 | +[a] | |
| 34 | + system = from-system.config | |
| 35 | + system-override = base | |
| 36 | +[include] | |
| 37 | + path = ./b.config | |
| 38 | +EOF | |
| @@ -1,12 +1,37 @@ | ||
| 1 | 1 | use crate::named_repo; |
| 2 | 2 | use git_repository as git; |
| 3 | +use git_sec::{Access, Permission}; | |
| 4 | +use git_testtools::Env; | |
| 5 | +use serial_test::serial; | |
| 3 | 6 | use std::path::Path; |
| 4 | 7 | |
| 5 | 8 | #[test] |
| 9 | +#[serial] | |
| 6 | 10 | fn access_values() { |
| 7 | 11 | for trust in [git_sec::Trust::Full, git_sec::Trust::Reduced] { |
| 8 | 12 | let repo = named_repo("make_config_repo.sh").unwrap(); |
| 9 | − let repo = git::open_opts(repo.git_dir(), repo.open_options().clone().with(trust)).unwrap(); | |
| 13 | + let _env = Env::new().set( | |
| 14 | + "GIT_CONFIG_SYSTEM", | |
| 15 | + repo.work_dir() | |
| 16 | + .expect("present") | |
| 17 | + .join("system.config") | |
| 18 | + .canonicalize() | |
| 19 | + .unwrap() | |
| 20 | + .display() | |
| 21 | + .to_string(), | |
| 22 | + ); | |
| 23 | + let repo = git::open_opts( | |
| 24 | + repo.git_dir(), | |
| 25 | + repo.open_options().clone().with(trust).permissions(git::Permissions { | |
| 26 | + env: git::permissions::Environment { | |
| 27 | + xdg_config_home: Access::resource(Permission::Deny), | |
| 28 | + home: Access::resource(Permission::Deny), | |
| 29 | + ..git::permissions::Environment::allow_all() | |
| 30 | + }, | |
| 31 | + ..Default::default() | |
| 32 | + }), | |
| 33 | + ) | |
| 34 | + .unwrap(); | |
| 10 | 35 | |
| 11 | 36 | let config = repo.config_snapshot(); |
| 12 | 37 | |
| @@ -26,6 +51,14 @@ fn access_values() { · 13 unmodified lines | ||
| 26 | 51 | ); |
| 27 | 52 | |
| 28 | 53 | assert_eq!(config.string("a.override").expect("present").as_ref(), "from-a.config"); |
| 54 | + assert_eq!( | |
| 55 | + config.string("a.system").expect("present").as_ref(), | |
| 56 | + "from-system.config" | |
| 57 | + ); | |
| 58 | + assert_eq!( | |
| 59 | + config.string("a.system-override").expect("present").as_ref(), | |
| 60 | + "from-b.config" | |
| 61 | + ); | |
| 29 | 62 | |
| 30 | 63 | assert_eq!(config.boolean("core.missing"), None); |
| 31 | 64 | assert_eq!(config.try_boolean("core.missing"), None); |