Skip to content

Commit ca04636

Browse files
authored
Merge pull request libgit2#5073 from libgit2/ethomson/config_section_validity
Configuration parsing: validate section headers with quotes
2 parents d97afb9 + 355b02a commit ca04636

File tree

2 files changed

+98
-22
lines changed

2 files changed

+98
-22
lines changed

src/config_parse.c

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@ const char *git_config_escaped = "\n\t\b\"\\";
1717
static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
1818
{
1919
const char *file = reader->file ? reader->file->path : "in-memory";
20-
git_error_set(GIT_ERROR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
21-
error_str, file, reader->ctx.line_num, col);
20+
21+
if (col)
22+
git_error_set(GIT_ERROR_CONFIG,
23+
"failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
24+
error_str, file, reader->ctx.line_num, col);
25+
else
26+
git_error_set(GIT_ERROR_CONFIG,
27+
"failed to parse config file: %s (in %s:%"PRIuZ")",
28+
error_str, file, reader->ctx.line_num);
2229
}
2330

2431

@@ -59,31 +66,30 @@ static int strip_comments(char *line, int in_quotes)
5966
}
6067

6168

62-
static int parse_section_header_ext(git_config_parser *reader, const char *line, const char *base_name, char **section_name)
69+
static int parse_subsection_header(git_config_parser *reader, const char *line, size_t pos, const char *base_name, char **section_name)
6370
{
6471
int c, rpos;
65-
char *first_quote, *last_quote;
72+
const char *first_quote, *last_quote;
6673
const char *line_start = line;
6774
git_buf buf = GIT_BUF_INIT;
6875
size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
6976

70-
/*
71-
* base_name is what came before the space. We should be at the
72-
* first quotation mark, except for now, line isn't being kept in
73-
* sync so we only really use it to calculate the length.
74-
*/
77+
/* Skip any additional whitespace before our section name */
78+
while (git__isspace(line[pos]))
79+
pos++;
7580

76-
first_quote = strchr(line, '"');
77-
if (first_quote == NULL) {
78-
set_parse_error(reader, 0, "Missing quotation marks in section header");
81+
/* We should be at the first quotation mark. */
82+
if (line[pos] != '"') {
83+
set_parse_error(reader, 0, "missing quotation marks in section header");
7984
goto end_error;
8085
}
8186

87+
first_quote = &line[pos];
8288
last_quote = strrchr(line, '"');
8389
quoted_len = last_quote - first_quote;
8490

8591
if (quoted_len == 0) {
86-
set_parse_error(reader, 0, "Missing closing quotation mark in section header");
92+
set_parse_error(reader, 0, "missing closing quotation mark in section header");
8793
goto end_error;
8894
}
8995

@@ -107,7 +113,7 @@ static int parse_section_header_ext(git_config_parser *reader, const char *line,
107113

108114
switch (c) {
109115
case 0:
110-
set_parse_error(reader, 0, "Unexpected end-of-line in section header");
116+
set_parse_error(reader, 0, "unexpected end-of-line in section header");
111117
goto end_error;
112118

113119
case '"':
@@ -117,7 +123,7 @@ static int parse_section_header_ext(git_config_parser *reader, const char *line,
117123
c = line[++rpos];
118124

119125
if (c == 0) {
120-
set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
126+
set_parse_error(reader, rpos, "unexpected end-of-line in section header");
121127
goto end_error;
122128
}
123129

@@ -134,7 +140,7 @@ static int parse_section_header_ext(git_config_parser *reader, const char *line,
134140
goto end_error;
135141

136142
if (line[rpos] != '"' || line[rpos + 1] != ']') {
137-
set_parse_error(reader, rpos, "Unexpected text after closing quotes");
143+
set_parse_error(reader, rpos, "unexpected text after closing quotes");
138144
git_buf_dispose(&buf);
139145
return -1;
140146
}
@@ -165,7 +171,7 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
165171
name_end = strrchr(line, ']');
166172
if (name_end == NULL) {
167173
git__free(line);
168-
set_parse_error(reader, 0, "Missing ']' in section header");
174+
set_parse_error(reader, 0, "missing ']' in section header");
169175
return -1;
170176
}
171177

@@ -185,14 +191,14 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
185191
do {
186192
if (git__isspace(c)){
187193
name[name_length] = '\0';
188-
result = parse_section_header_ext(reader, line, name, section_out);
194+
result = parse_subsection_header(reader, line, pos, name, section_out);
189195
git__free(line);
190196
git__free(name);
191197
return result;
192198
}
193199

194200
if (!config_keychar(c) && c != '.') {
195-
set_parse_error(reader, pos, "Unexpected character in header");
201+
set_parse_error(reader, pos, "unexpected character in header");
196202
goto fail_parse;
197203
}
198204

@@ -201,7 +207,7 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
201207
} while ((c = line[pos++]) != ']');
202208

203209
if (line[pos - 1] != ']') {
204-
set_parse_error(reader, pos, "Unexpected end of file");
210+
set_parse_error(reader, pos, "unexpected end of file");
205211
goto fail_parse;
206212
}
207213

@@ -386,7 +392,7 @@ static int parse_name(
386392
name_end++;
387393

388394
if (line == name_end) {
389-
set_parse_error(reader, 0, "Invalid configuration key");
395+
set_parse_error(reader, 0, "invalid configuration key");
390396
return -1;
391397
}
392398

@@ -398,7 +404,7 @@ static int parse_name(
398404
if (*value_start == '=') {
399405
*value = value_start + 1;
400406
} else if (*value_start) {
401-
set_parse_error(reader, 0, "Invalid configuration key");
407+
set_parse_error(reader, 0, "invalid configuration key");
402408
return -1;
403409
}
404410

tests/config/read.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,76 @@ void test_config_read__bom(void)
779779
git_buf_dispose(&buf);
780780
}
781781

782+
void test_config_read__arbitrary_whitespace_before_subsection(void)
783+
{
784+
git_buf buf = GIT_BUF_INIT;
785+
git_config *cfg;
786+
787+
cl_set_cleanup(&clean_test_config, NULL);
788+
cl_git_mkfile("./testconfig", "[some \t \"subsection\"]\n var = value\n");
789+
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
790+
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.subsection.var"));
791+
cl_assert_equal_s(buf.ptr, "value");
792+
793+
git_config_free(cfg);
794+
git_buf_dispose(&buf);
795+
}
796+
797+
void test_config_read__no_whitespace_after_subsection(void)
798+
{
799+
git_config *cfg;
800+
801+
cl_set_cleanup(&clean_test_config, NULL);
802+
cl_git_mkfile("./testconfig", "[some \"subsection\" ]\n var = value\n");
803+
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
804+
805+
git_config_free(cfg);
806+
}
807+
808+
void test_config_read__invalid_space_section(void)
809+
{
810+
git_config *cfg;
811+
812+
cl_set_cleanup(&clean_test_config, NULL);
813+
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some section]\n var = value\n");
814+
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
815+
816+
git_config_free(cfg);
817+
}
818+
819+
void test_config_read__invalid_quoted_first_section(void)
820+
{
821+
git_config *cfg;
822+
823+
cl_set_cleanup(&clean_test_config, NULL);
824+
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[\"some\"]\n var = value\n");
825+
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
826+
827+
git_config_free(cfg);
828+
}
829+
830+
void test_config_read__invalid_unquoted_subsection(void)
831+
{
832+
git_config *cfg;
833+
834+
cl_set_cleanup(&clean_test_config, NULL);
835+
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub section]\n var = value\n");
836+
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
837+
838+
git_config_free(cfg);
839+
}
840+
841+
void test_config_read__invalid_quoted_third_section(void)
842+
{
843+
git_config *cfg;
844+
845+
cl_set_cleanup(&clean_test_config, NULL);
846+
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub \"section\"]\n var = value\n");
847+
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
848+
849+
git_config_free(cfg);
850+
}
851+
782852
void test_config_read__single_line(void)
783853
{
784854
git_buf buf = GIT_BUF_INIT;

0 commit comments

Comments
 (0)