Skip to content

Commit 452bf57

Browse files
committed
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 parent 5671e81 commit 452bf57

File tree

4 files changed

+62
-4
lines changed

4 files changed

+62
-4
lines changed

include/git2/common.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ typedef enum {
157157
GIT_OPT_SET_SSL_CERT_LOCATIONS,
158158
GIT_OPT_SET_USER_AGENT,
159159
GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
160+
GIT_OPT_ENABLE_SYMBOLIC_REF_TARGET_VALIDATION,
160161
GIT_OPT_SET_SSL_CIPHERS,
161162
GIT_OPT_GET_USER_AGENT,
162163
} git_libgit2_opt_t;
@@ -270,6 +271,18 @@ typedef enum {
270271
* > example, when this is enabled, the parent(s) and tree inputs
271272
* > will be validated when creating a new commit. This defaults
272273
* > to disabled.
274+
*
275+
* * opts(GIT_OPT_ENABLE_SYMBOLIC_REF_TARGET_VALIDATION, int enabled)
276+
*
277+
* > Validate the target of a symbolic ref when creating it.
278+
* > For example, 'foobar' is not a valid ref,
279+
* > therefore 'foobar' is not a valid target
280+
* > for a symbolic ref by default,
281+
* > where as 'refs/heads/foobar' is.
282+
* > Disabling this bypasses validation so that an arbitrary
283+
* > strings such as 'foobar' can be used for a symbolic ref target.
284+
* > This defaults to enabled.
285+
*
273286
* * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
274287
*
275288
* > Set the SSL ciphers use for HTTPS connections.

src/refs.c

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <git2/signature.h>
2525
#include <git2/commit.h>
2626

27+
bool git_reference__enable_symbolic_ref_target_validation = true;
28+
2729
GIT__USE_STRMAP
2830

2931
#define DEFAULT_NESTING_LEVEL 5
@@ -175,10 +177,11 @@ int git_reference_name_to_id(
175177
return 0;
176178
}
177179

178-
static int reference_normalize_for_repo(
180+
static int reference__normalize_for_repo(
179181
git_refname_t out,
180182
git_repository *repo,
181-
const char *name)
183+
const char *name,
184+
bool validate)
182185
{
183186
int precompose;
184187
unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
@@ -187,9 +190,29 @@ static int reference_normalize_for_repo(
187190
precompose)
188191
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
189192

193+
if (!validate) {
194+
flags |= GIT_REF_VALIDATION_DISABLE;
195+
}
196+
190197
return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
191198
}
192199

200+
static int reference_normalize_for_repo(
201+
git_refname_t out,
202+
git_repository *repo,
203+
const char *name)
204+
{
205+
return reference__normalize_for_repo(out, repo, name, true);
206+
}
207+
208+
static int reference_normalize_for_repo_without_validation(
209+
git_refname_t out,
210+
git_repository *repo,
211+
const char *name)
212+
{
213+
return reference__normalize_for_repo(out, repo, name, false);
214+
}
215+
193216
int git_reference_lookup_resolved(
194217
git_reference **ref_out,
195218
git_repository *repo,
@@ -404,7 +427,13 @@ static int reference__create(
404427
} else {
405428
git_refname_t normalized_target;
406429

407-
if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
430+
if (git_reference__enable_symbolic_ref_target_validation) {
431+
error = reference_normalize_for_repo(normalized_target, repo, symbolic);
432+
} else {
433+
error = reference_normalize_for_repo_without_validation(normalized_target, repo, symbolic);
434+
}
435+
436+
if (error < 0)
408437
return error;
409438

410439
ref = git_reference__alloc_symbolic(normalized, normalized_target);
@@ -876,6 +905,7 @@ int git_reference__normalize_name(
876905
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
877906
unsigned int process_flags;
878907
bool normalize = (buf != NULL);
908+
bool validate = (flags & GIT_REF_VALIDATION_DISABLE) == 0;
879909

880910
#ifdef GIT_USE_ICONV
881911
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
@@ -886,7 +916,7 @@ int git_reference__normalize_name(
886916
process_flags = flags;
887917
current = (char *)name;
888918

889-
if (*current == '/')
919+
if (validate && *current == '/')
890920
goto cleanup;
891921

892922
if (normalize)
@@ -902,6 +932,13 @@ int git_reference__normalize_name(
902932
}
903933
#endif
904934

935+
if (!validate) {
936+
git_buf_sets(buf, current);
937+
938+
error = git_buf_oom(buf) ? -1 : 0;
939+
goto cleanup;
940+
}
941+
905942
while (true) {
906943
segment_len = ensure_segment_validity(current);
907944
if (segment_len < 0) {

src/refs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "buffer.h"
1616
#include "oid.h"
1717

18+
extern bool git_reference__enable_symbolic_ref_target_validation;
19+
1820
#define GIT_REFS_DIR "refs/"
1921
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
2022
#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
@@ -53,6 +55,7 @@
5355
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
5456

5557
#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
58+
#define GIT_REF_VALIDATION_DISABLE (1u << 15)
5659

5760
#define GIT_REFNAME_MAX 1024
5861

src/settings.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "cache.h"
1616
#include "global.h"
1717
#include "object.h"
18+
#include "refs.h"
1819

1920
void git_libgit2_version(int *major, int *minor, int *rev)
2021
{
@@ -191,6 +192,10 @@ int git_libgit2_opts(int key, ...)
191192
git_object__strict_input_validation = (va_arg(ap, int) != 0);
192193
break;
193194

195+
case GIT_OPT_ENABLE_SYMBOLIC_REF_TARGET_VALIDATION:
196+
git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
197+
break;
198+
194199
case GIT_OPT_SET_SSL_CIPHERS:
195200
#ifdef GIT_OPENSSL
196201
{

0 commit comments

Comments
 (0)