gix/open/options.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
use std::path::PathBuf;
use super::{Error, Options};
use crate::{bstr::BString, config, open::Permissions, ThreadSafeRepository};
impl Default for Options {
fn default() -> Self {
Options {
object_store_slots: Default::default(),
permissions: Default::default(),
git_dir_trust: None,
filter_config_section: None,
lossy_config: None,
lenient_config: true,
bail_if_untrusted: false,
open_path_as_is: false,
api_config_overrides: Vec::new(),
cli_config_overrides: Vec::new(),
current_dir: None,
}
}
}
/// Instantiation
impl Options {
/// Options configured to prevent accessing anything else than the repository configuration file, prohibiting
/// accessing the environment or spreading beyond the git repository location.
pub fn isolated() -> Self {
Options::default().permissions(Permissions::isolated())
}
}
/// Generic modification
impl Options {
/// An adapter to allow calling any builder method on this instance despite only having a mutable reference.
pub fn modify(&mut self, f: impl FnOnce(Self) -> Self) {
*self = f(std::mem::take(self));
}
}
/// Builder methods
impl Options {
/// Apply the given configuration `values` like `init.defaultBranch=special` or `core.bool-implicit-true` in memory to as early
/// as the configuration is initialized to allow affecting the repository instantiation phase, both on disk or when opening.
/// The configuration is marked with [source API][gix_config::Source::Api].
pub fn config_overrides(mut self, values: impl IntoIterator<Item = impl Into<BString>>) -> Self {
self.api_config_overrides = values.into_iter().map(Into::into).collect();
self
}
/// Set configuration values of the form `core.abbrev=5` or `remote.origin.url = foo` or `core.bool-implicit-true` for application
/// as CLI overrides to the repository configuration, marked with [source CLI][gix_config::Source::Cli].
/// These are equivalent to CLI overrides passed with `-c` in `git`, for example.
pub fn cli_overrides(mut self, values: impl IntoIterator<Item = impl Into<BString>>) -> Self {
self.cli_config_overrides = values.into_iter().map(Into::into).collect();
self
}
/// Set the amount of slots to use for the object database. It's a value that doesn't need changes on the client, typically,
/// but should be controlled on the server.
pub fn object_store_slots(mut self, slots: gix_odb::store::init::Slots) -> Self {
self.object_store_slots = slots;
self
}
// TODO: tests
/// Set the given permissions, which are typically derived by a `Trust` level.
pub fn permissions(mut self, permissions: Permissions) -> Self {
self.permissions = permissions;
self
}
/// If `true`, default `false`, we will not modify the incoming path to open to assure it is a `.git` directory.
///
/// If `false`, we will try to open the input directory as is, even though it doesn't appear to be a `git` repository
/// due to the lack of `.git` suffix or because its basename is not `.git` as in `worktree/.git`.
pub fn open_path_as_is(mut self, enable: bool) -> Self {
self.open_path_as_is = enable;
self
}
/// Set the trust level of the `.git` directory we are about to open.
///
/// This can be set manually to force trust even though otherwise it might
/// not be fully trusted, leading to limitations in how configuration files
/// are interpreted.
///
/// If not called explicitly, it will be determined by looking at its
/// ownership via [`gix_sec::Trust::from_path_ownership()`].
///
/// # Security Warning
///
/// Use with extreme care and only if it's absolutely known that the repository
/// is always controlled by the desired user. Using this capability _only_ saves
/// a permission check and only so if the [`open()`][Self::open()] method is used,
/// as opposed to discovery.
pub fn with(mut self, trust: gix_sec::Trust) -> Self {
self.git_dir_trust = trust.into();
self
}
/// If true, default false, and if the repository's trust level is not `Full`
/// (see [`with()`][Self::with()] for more), then the open operation will fail.
///
/// Use this to mimic `git`s way of handling untrusted repositories. Note that `gitoxide` solves
/// this by not using configuration from untrusted sources and by generally being secured against
/// doctored input files which at worst could cause out-of-memory at the time of writing.
pub fn bail_if_untrusted(mut self, toggle: bool) -> Self {
self.bail_if_untrusted = toggle;
self
}
/// Set the filter which determines if a configuration section can be used to read values from,
/// hence it returns true if it is eligible.
///
/// The default filter selects sections whose trust level is [`full`][gix_sec::Trust::Full] or
/// whose source is not [`repository-local`][gix_config::source::Kind::Repository].
pub fn filter_config_section(mut self, filter: fn(&gix_config::file::Metadata) -> bool) -> Self {
self.filter_config_section = Some(filter);
self
}
/// By default, in release mode configuration will be read without retaining non-essential information like
/// comments or whitespace to optimize lookup performance.
///
/// Some application might want to toggle this to false in they want to display or edit configuration losslessly
/// with all whitespace and comments included.
pub fn lossy_config(mut self, toggle: bool) -> Self {
self.lossy_config = toggle.into();
self
}
/// If set, default is false, invalid configuration values will cause an error even if these can safely be defaulted.
///
/// This is recommended for all applications that prefer correctness over usability.
/// `git` itself defaults to strict configuration mode, flagging incorrect configuration immediately.
///
/// Failure to read configuration files due to IO errors will also be a hard error if this mode is enabled, otherwise
/// these errors will merely be logged.
pub fn strict_config(mut self, toggle: bool) -> Self {
self.lenient_config = !toggle;
self
}
/// Open a repository at `path` with the options set so far.
#[allow(clippy::result_large_err)]
pub fn open(self, path: impl Into<PathBuf>) -> Result<ThreadSafeRepository, Error> {
ThreadSafeRepository::open_opts(path, self)
}
}
impl Options {
pub(crate) fn current_dir_or_empty(&self) -> &std::path::Path {
self.current_dir.as_deref().unwrap_or(std::path::Path::new(""))
}
}
impl gix_sec::trust::DefaultForLevel for Options {
fn default_for_level(level: gix_sec::Trust) -> Self {
match level {
gix_sec::Trust::Full => Options {
object_store_slots: Default::default(),
permissions: Permissions::default_for_level(level),
git_dir_trust: gix_sec::Trust::Full.into(),
filter_config_section: Some(config::section::is_trusted),
lossy_config: None,
bail_if_untrusted: false,
lenient_config: true,
open_path_as_is: false,
api_config_overrides: Vec::new(),
cli_config_overrides: Vec::new(),
current_dir: None,
},
gix_sec::Trust::Reduced => Options {
object_store_slots: gix_odb::store::init::Slots::Given(32), // limit resource usage
permissions: Permissions::default_for_level(level),
git_dir_trust: gix_sec::Trust::Reduced.into(),
filter_config_section: Some(config::section::is_trusted),
bail_if_untrusted: false,
lenient_config: true,
open_path_as_is: false,
lossy_config: None,
api_config_overrides: Vec::new(),
cli_config_overrides: Vec::new(),
current_dir: None,
},
}
}
}