use std::path::PathBuf; use super::{Error, Options};use crate::{bstr::BString, config, 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, } }} /// Instantiationimpl 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 modificationimpl 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 methodsimpl 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][git_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][git_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: git_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 [`git_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: git_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`][git_sec::Trust::Full] or /// whose source is not [`repository-local`][git_config::source::Kind::Repository]. pub fn filter_config_section(mut self, filter: fn(&git_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. 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. pub fn open(self, path: impl Into<PathBuf>) -> Result<ThreadSafeRepository, Error> { ThreadSafeRepository::open_opts(path, self) }} impl git_sec::trust::DefaultForLevel for Options { fn default_for_level(level: git_sec::Trust) -> Self { match level { git_sec::Trust::Full => Options { object_store_slots: Default::default(), permissions: Permissions::default_for_level(level), git_dir_trust: git_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, }, git_sec::Trust::Reduced => Options { object_store_slots: git_odb::store::init::Slots::Given(32), // limit resource usage permissions: Permissions::default_for_level(level), git_dir_trust: git_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, }, } }}
use std::path::PathBuf;
use super::{Error, Options};
use crate::{bstr::BString, config, 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][git_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][git_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: git_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 [`git_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: git_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`][git_sec::Trust::Full] or
/// whose source is not [`repository-local`][git_config::source::Kind::Repository].
pub fn filter_config_section(mut self, filter: fn(&git_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.
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.
pub fn open(self, path: impl Into<PathBuf>) -> Result<ThreadSafeRepository, Error> {
ThreadSafeRepository::open_opts(path, self)
}
}
impl git_sec::trust::DefaultForLevel for Options {
fn default_for_level(level: git_sec::Trust) -> Self {
match level {
git_sec::Trust::Full => Options {
object_store_slots: Default::default(),
permissions: Permissions::default_for_level(level),
git_dir_trust: git_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,
},
git_sec::Trust::Reduced => Options {
object_store_slots: git_odb::store::init::Slots::Given(32), // limit resource usage
permissions: Permissions::default_for_level(level),
git_dir_trust: git_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,
},
}
}
}