Unverified Commit ca046360 by Edward Thomson Committed by GitHub

Merge pull request #5073 from libgit2/ethomson/config_section_validity

Configuration parsing: validate section headers with quotes
parents d97afb93 355b02a1
...@@ -17,8 +17,15 @@ const char *git_config_escaped = "\n\t\b\"\\"; ...@@ -17,8 +17,15 @@ const char *git_config_escaped = "\n\t\b\"\\";
static void set_parse_error(git_config_parser *reader, int col, const char *error_str) static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
{ {
const char *file = reader->file ? reader->file->path : "in-memory"; const char *file = reader->file ? reader->file->path : "in-memory";
git_error_set(GIT_ERROR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
error_str, file, reader->ctx.line_num, col); if (col)
git_error_set(GIT_ERROR_CONFIG,
"failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
error_str, file, reader->ctx.line_num, col);
else
git_error_set(GIT_ERROR_CONFIG,
"failed to parse config file: %s (in %s:%"PRIuZ")",
error_str, file, reader->ctx.line_num);
} }
...@@ -59,31 +66,30 @@ static int strip_comments(char *line, int in_quotes) ...@@ -59,31 +66,30 @@ static int strip_comments(char *line, int in_quotes)
} }
static int parse_section_header_ext(git_config_parser *reader, const char *line, const char *base_name, char **section_name) static int parse_subsection_header(git_config_parser *reader, const char *line, size_t pos, const char *base_name, char **section_name)
{ {
int c, rpos; int c, rpos;
char *first_quote, *last_quote; const char *first_quote, *last_quote;
const char *line_start = line; const char *line_start = line;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
size_t quoted_len, alloc_len, base_name_len = strlen(base_name); size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
/* /* Skip any additional whitespace before our section name */
* base_name is what came before the space. We should be at the while (git__isspace(line[pos]))
* first quotation mark, except for now, line isn't being kept in pos++;
* sync so we only really use it to calculate the length.
*/
first_quote = strchr(line, '"'); /* We should be at the first quotation mark. */
if (first_quote == NULL) { if (line[pos] != '"') {
set_parse_error(reader, 0, "Missing quotation marks in section header"); set_parse_error(reader, 0, "missing quotation marks in section header");
goto end_error; goto end_error;
} }
first_quote = &line[pos];
last_quote = strrchr(line, '"'); last_quote = strrchr(line, '"');
quoted_len = last_quote - first_quote; quoted_len = last_quote - first_quote;
if (quoted_len == 0) { if (quoted_len == 0) {
set_parse_error(reader, 0, "Missing closing quotation mark in section header"); set_parse_error(reader, 0, "missing closing quotation mark in section header");
goto end_error; goto end_error;
} }
...@@ -107,7 +113,7 @@ static int parse_section_header_ext(git_config_parser *reader, const char *line, ...@@ -107,7 +113,7 @@ static int parse_section_header_ext(git_config_parser *reader, const char *line,
switch (c) { switch (c) {
case 0: case 0:
set_parse_error(reader, 0, "Unexpected end-of-line in section header"); set_parse_error(reader, 0, "unexpected end-of-line in section header");
goto end_error; goto end_error;
case '"': case '"':
...@@ -117,7 +123,7 @@ static int parse_section_header_ext(git_config_parser *reader, const char *line, ...@@ -117,7 +123,7 @@ static int parse_section_header_ext(git_config_parser *reader, const char *line,
c = line[++rpos]; c = line[++rpos];
if (c == 0) { if (c == 0) {
set_parse_error(reader, rpos, "Unexpected end-of-line in section header"); set_parse_error(reader, rpos, "unexpected end-of-line in section header");
goto end_error; goto end_error;
} }
...@@ -134,7 +140,7 @@ end_parse: ...@@ -134,7 +140,7 @@ end_parse:
goto end_error; goto end_error;
if (line[rpos] != '"' || line[rpos + 1] != ']') { if (line[rpos] != '"' || line[rpos + 1] != ']') {
set_parse_error(reader, rpos, "Unexpected text after closing quotes"); set_parse_error(reader, rpos, "unexpected text after closing quotes");
git_buf_dispose(&buf); git_buf_dispose(&buf);
return -1; return -1;
} }
...@@ -165,7 +171,7 @@ static int parse_section_header(git_config_parser *reader, char **section_out) ...@@ -165,7 +171,7 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
name_end = strrchr(line, ']'); name_end = strrchr(line, ']');
if (name_end == NULL) { if (name_end == NULL) {
git__free(line); git__free(line);
set_parse_error(reader, 0, "Missing ']' in section header"); set_parse_error(reader, 0, "missing ']' in section header");
return -1; return -1;
} }
...@@ -185,14 +191,14 @@ static int parse_section_header(git_config_parser *reader, char **section_out) ...@@ -185,14 +191,14 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
do { do {
if (git__isspace(c)){ if (git__isspace(c)){
name[name_length] = '\0'; name[name_length] = '\0';
result = parse_section_header_ext(reader, line, name, section_out); result = parse_subsection_header(reader, line, pos, name, section_out);
git__free(line); git__free(line);
git__free(name); git__free(name);
return result; return result;
} }
if (!config_keychar(c) && c != '.') { if (!config_keychar(c) && c != '.') {
set_parse_error(reader, pos, "Unexpected character in header"); set_parse_error(reader, pos, "unexpected character in header");
goto fail_parse; goto fail_parse;
} }
...@@ -201,7 +207,7 @@ static int parse_section_header(git_config_parser *reader, char **section_out) ...@@ -201,7 +207,7 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
} while ((c = line[pos++]) != ']'); } while ((c = line[pos++]) != ']');
if (line[pos - 1] != ']') { if (line[pos - 1] != ']') {
set_parse_error(reader, pos, "Unexpected end of file"); set_parse_error(reader, pos, "unexpected end of file");
goto fail_parse; goto fail_parse;
} }
...@@ -386,7 +392,7 @@ static int parse_name( ...@@ -386,7 +392,7 @@ static int parse_name(
name_end++; name_end++;
if (line == name_end) { if (line == name_end) {
set_parse_error(reader, 0, "Invalid configuration key"); set_parse_error(reader, 0, "invalid configuration key");
return -1; return -1;
} }
...@@ -398,7 +404,7 @@ static int parse_name( ...@@ -398,7 +404,7 @@ static int parse_name(
if (*value_start == '=') { if (*value_start == '=') {
*value = value_start + 1; *value = value_start + 1;
} else if (*value_start) { } else if (*value_start) {
set_parse_error(reader, 0, "Invalid configuration key"); set_parse_error(reader, 0, "invalid configuration key");
return -1; return -1;
} }
......
...@@ -779,6 +779,76 @@ void test_config_read__bom(void) ...@@ -779,6 +779,76 @@ void test_config_read__bom(void)
git_buf_dispose(&buf); git_buf_dispose(&buf);
} }
void test_config_read__arbitrary_whitespace_before_subsection(void)
{
git_buf buf = GIT_BUF_INIT;
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some \t \"subsection\"]\n var = value\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.subsection.var"));
cl_assert_equal_s(buf.ptr, "value");
git_config_free(cfg);
git_buf_dispose(&buf);
}
void test_config_read__no_whitespace_after_subsection(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[some \"subsection\" ]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_space_section(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some section]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_quoted_first_section(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[\"some\"]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_unquoted_subsection(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub section]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__invalid_quoted_third_section(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub \"section\"]\n var = value\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__single_line(void) void test_config_read__single_line(void)
{ {
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment