Exgit.RefStore.Disk (exgit v0.1.0)

Copy Markdown View Source

Filesystem-backed ref store.

Defense-in-depth ref name validation

Every public entry point (read_ref/2, resolve_ref/2, write_ref/4, delete_ref/2) revalidates its ref argument against Exgit.RefName.valid?/1 before any Path.join or file touch. The clone/fetch perimeter already filters hostile ref names in safe_ls_refs/2, but a direct caller of this module — or a follow-up resolve_ref/2 that reads a ref: ../../etc/passwd target out of a compromised on-disk ref file — would otherwise reach File.read with an attacker-controlled path. We reject those inputs with {:error, :invalid_ref_name} and emit a [:exgit, :security, :ref_rejected] telemetry event.

Summary

Types

ref_value()

@type ref_value() :: binary() | {:symbolic, String.t()}

t()

@type t() :: %Exgit.RefStore.Disk{root: Path.t()}

Functions

delete_ref(disk, ref)

@spec delete_ref(t(), String.t()) :: :ok | {:error, :not_found | :invalid_ref_name}

list_refs(disk, prefix \\ "refs/")

@spec list_refs(t(), String.t()) :: [{String.t(), ref_value()}]

new(root)

@spec new(Path.t()) :: t()

read_ref(disk, ref)

@spec read_ref(t(), String.t()) :: {:ok, ref_value()} | {:error, :not_found | term()}

resolve_ref(store, ref)

@spec resolve_ref(t(), String.t()) ::
  {:ok, binary()}
  | {:error, :not_found | :too_deep | :cycle | :invalid_ref_name}

write_ref(store, ref, value, opts \\ [])

@spec write_ref(t(), String.t(), ref_value(), keyword()) :: :ok | {:error, term()}