Skip to main content

git_zero_server/upload_pack/
policy.rs

1//! Explicit host permission for wants; packet/body contents never grant access.
2#[cfg(feature = "read")]
3use git_zero_core::{Repository, Result, io::Write};
4
5/// The host must authenticate and supply one immutable, authorized repository
6/// projection. Validation proves its exact reachable union; it does not grant ACLs.
7#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
8pub enum WantPolicy {
9    #[default]
10    AdvertisedOnly,
11    /// Permit all object kinds in the supplied validated visible closure.
12    AllVisibleReachable,
13}
14
15/// Additional request policy, separate from existing selection storage literals.
16/// Occurrences include duplicates. Existing wrappers preserve their strict rule
17/// without adding a new occurrence limit. Hosts must still bound request bytes.
18///
19/// This is not a total logical-work limit: metadata scans remain bounded by
20/// object/ref capacities, and structural reads by the existing shared graph cap.
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub struct WantOptions {
23    pub policy: WantPolicy,
24    pub max_want_lines: usize,
25}
26
27impl Default for WantOptions {
28    fn default() -> Self {
29        Self::STRICT
30    }
31}
32
33impl WantOptions {
34    pub const STRICT: Self = Self {
35        policy: WantPolicy::AdvertisedOnly,
36        max_want_lines: usize::MAX,
37    };
38
39    pub const fn all_visible_reachable(max_want_lines: usize) -> Self {
40        Self {
41            policy: WantPolicy::AllVisibleReachable,
42            max_want_lines,
43        }
44    }
45}
46
47#[cfg(feature = "read")]
48pub(crate) const REACHABLE: &[u8] = b" allow-reachable-sha1-in-want";
49
50#[cfg(feature = "read")]
51pub(crate) struct Discard;
52#[cfg(feature = "read")]
53impl Write for Discard {
54    fn write_all(&mut self, _: &[u8]) -> Result<()> {
55        Ok(())
56    }
57}
58
59#[cfg(feature = "read")]
60pub(crate) fn info_refs(
61    repository: &impl Repository,
62    route: &crate::route::Route<'_>,
63    output: &mut impl Write,
64    options: WantOptions,
65) -> Result<crate::Response> {
66    if options.policy == WantPolicy::AdvertisedOnly
67        || route.service != Some("git-upload-pack")
68        || route.version == crate::route::Version::V2
69    {
70        return super::advertisement::serve_info_refs(repository, route, output, true);
71    }
72    // Reuse the exact packet generator for preflight, including the unborn
73    // first-ref carrier and resolved symref. No service/success prefix escapes
74    // when the added capability would overflow a packet. Defaults stay intact.
75    let ordinary = git_zero_core::repository::UPLOAD_CAPABILITIES;
76    let mut capabilities =
77        [0; git_zero_core::repository::UPLOAD_CAPABILITIES.len() + REACHABLE.len()];
78    capabilities[..ordinary.len()].copy_from_slice(ordinary);
79    capabilities[ordinary.len()..].copy_from_slice(REACHABLE);
80    super::advertisement::serve_capability_refs(repository, route.version, output, &capabilities)
81}