Make symbolic ref target validation optional Introduce GIT_OPT_ENABLE_SYMBOLIC_REF_TARGET_VALIDATION option. Setting this option to 0 allows validation of a symbolic ref's target to be bypassed. This option is enabled by default. This mechanism is added primarily to address a discrepancy between git behaviour and libgit2 behaviour, whereby the former allows the symbolic ref target to carry an arbitrary string and the latter does not, so: $ git symbolic-ref refs/heads/foo bar $ cat .git/refs/heads/foo ref: bar where as attempting the same via libgit2 raises an error: The given reference name 'bar' is not valid this mechanism also allows those that might want to make use of git's more lenient treatment of symbolic ref targets to do so.
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
diff --git a/include/git2/common.h b/include/git2/common.h
index 18abe46..02d2630 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -157,6 +157,7 @@ typedef enum {
GIT_OPT_SET_SSL_CERT_LOCATIONS,
GIT_OPT_SET_USER_AGENT,
GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
+ GIT_OPT_ENABLE_SYMBOLIC_REF_TARGET_VALIDATION,
GIT_OPT_SET_SSL_CIPHERS,
GIT_OPT_GET_USER_AGENT,
} git_libgit2_opt_t;
@@ -270,6 +271,18 @@ typedef enum {
* > example, when this is enabled, the parent(s) and tree inputs
* > will be validated when creating a new commit. This defaults
* > to disabled.
+ *
+ * * opts(GIT_OPT_ENABLE_SYMBOLIC_REF_TARGET_VALIDATION, int enabled)
+ *
+ * > Validate the target of a symbolic ref when creating it.
+ * > For example, 'foobar' is not a valid ref,
+ * > therefore 'foobar' is not a valid target
+ * > for a symbolic ref by default,
+ * > where as 'refs/heads/foobar' is.
+ * > Disabling this bypasses validation so that an arbitrary
+ * > strings such as 'foobar' can be used for a symbolic ref target.
+ * > This defaults to enabled.
+ *
* * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
*
* > Set the SSL ciphers use for HTTPS connections.
diff --git a/src/refs.c b/src/refs.c
index bff443a..c77653d 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -24,6 +24,8 @@
#include <git2/signature.h>
#include <git2/commit.h>
+bool git_reference__enable_symbolic_ref_target_validation = true;
+
GIT__USE_STRMAP
#define DEFAULT_NESTING_LEVEL 5
@@ -175,10 +177,11 @@ int git_reference_name_to_id(
return 0;
}
-static int reference_normalize_for_repo(
+static int reference__normalize_for_repo(
git_refname_t out,
git_repository *repo,
- const char *name)
+ const char *name,
+ bool validate)
{
int precompose;
unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
@@ -187,9 +190,29 @@ static int reference_normalize_for_repo(
precompose)
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
+ if (!validate) {
+ flags |= GIT_REF_VALIDATION_DISABLE;
+ }
+
return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
}
+static int reference_normalize_for_repo(
+ git_refname_t out,
+ git_repository *repo,
+ const char *name)
+{
+ return reference__normalize_for_repo(out, repo, name, true);
+}
+
+static int reference_normalize_for_repo_without_validation(
+ git_refname_t out,
+ git_repository *repo,
+ const char *name)
+{
+ return reference__normalize_for_repo(out, repo, name, false);
+}
+
int git_reference_lookup_resolved(
git_reference **ref_out,
git_repository *repo,
@@ -404,7 +427,13 @@ static int reference__create(
} else {
git_refname_t normalized_target;
- if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
+ if (git_reference__enable_symbolic_ref_target_validation) {
+ error = reference_normalize_for_repo(normalized_target, repo, symbolic);
+ } else {
+ error = reference_normalize_for_repo_without_validation(normalized_target, repo, symbolic);
+ }
+
+ if (error < 0)
return error;
ref = git_reference__alloc_symbolic(normalized, normalized_target);
@@ -876,6 +905,7 @@ int git_reference__normalize_name(
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
unsigned int process_flags;
bool normalize = (buf != NULL);
+ bool validate = (flags & GIT_REF_VALIDATION_DISABLE) == 0;
#ifdef GIT_USE_ICONV
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
@@ -886,7 +916,7 @@ int git_reference__normalize_name(
process_flags = flags;
current = (char *)name;
- if (*current == '/')
+ if (validate && *current == '/')
goto cleanup;
if (normalize)
@@ -902,6 +932,13 @@ int git_reference__normalize_name(
}
#endif
+ if (!validate) {
+ git_buf_sets(buf, current);
+
+ error = git_buf_oom(buf) ? -1 : 0;
+ goto cleanup;
+ }
+
while (true) {
segment_len = ensure_segment_validity(current);
if (segment_len < 0) {
diff --git a/src/refs.h b/src/refs.h
index fda9532..c4786dd 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -15,6 +15,8 @@
#include "buffer.h"
#include "oid.h"
+extern bool git_reference__enable_symbolic_ref_target_validation;
+
#define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
@@ -53,6 +55,7 @@
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
+#define GIT_REF_VALIDATION_DISABLE (1u << 15)
#define GIT_REFNAME_MAX 1024
diff --git a/src/settings.c b/src/settings.c
index cb2317f..1fcdce2 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -15,6 +15,7 @@
#include "cache.h"
#include "global.h"
#include "object.h"
+#include "refs.h"
void git_libgit2_version(int *major, int *minor, int *rev)
{
@@ -191,6 +192,10 @@ int git_libgit2_opts(int key, ...)
git_object__strict_input_validation = (va_arg(ap, int) != 0);
break;
+ case GIT_OPT_ENABLE_SYMBOLIC_REF_TARGET_VALIDATION:
+ git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
+ break;
+
case GIT_OPT_SET_SSL_CIPHERS:
#ifdef GIT_OPENSSL
{