Index: subversion/include/private/svn_config_private.h =================================================================== --- subversion/include/private/svn_config_private.h (revision 1847937) +++ subversion/include/private/svn_config_private.h (working copy) @@ -98,6 +98,20 @@ svn_config__constructor_create( * passed to the callback in the same order as they're defined in * STREAM. * + * If STRICT_SECTIONS is FALSE, the parser treats the first ] on a section + * line as the section name terminator and ignores everything else on the + * line. Otherwise, it uses the *last* ] as the section name terminator and + * allows only whitespace after it. This allows section names to contain + * brackets and can be used to define character classes in authz rules. For + * example, with STRICT_SECTIONS=FALSE, the following section definition: + * + * [:glob:/[Dd]ockerfile] + * + * is parsed as ":glob:/[Dd" and the remaining "ockerfile]" is ignored, but + * with STRICT_SECTIONS=TRUE, it will be parsed as ":glob:/[Dd]ockerfile" + * and anything other than whitespace after the terminating ] will be + * flagged as an error. + * * The lifetome of section names, option names and values passed to * the constructor does not extend past the invocation of each * callback; see calback docs, above. @@ -108,6 +122,7 @@ svn_error_t * svn_config__parse_stream(svn_stream_t *stream, svn_config__constructor_t *constructor, void *constructor_baton, + svn_boolean_t strict_sections, apr_pool_t *scratch_pool); /* Index: subversion/include/private/svn_string_private.h =================================================================== --- subversion/include/private/svn_string_private.h (revision 1847937) +++ subversion/include/private/svn_string_private.h (working copy) @@ -123,6 +123,10 @@ svn_membuf__nzero(svn_membuf_t *membuf, apr_size_t #endif +/** Strip whitespace from the end of @a str (modified in place). */ +void +svn_stringbuf__strip_trailing_whitespace(svn_stringbuf_t *str); + /** Returns the #svn_string_t information contained in the data and * len members of @a strbuf. This is effectively a typecast, converting * @a strbuf into an #svn_string_t. This first will become invalid and must Index: subversion/libsvn_repos/authz_parse.c =================================================================== --- subversion/libsvn_repos/authz_parse.c (revision 1847937) +++ subversion/libsvn_repos/authz_parse.c (working copy) @@ -1332,7 +1332,7 @@ svn_authz__parse(authz_full_t **authz, close_section, rules_add_value, cb->parser_pool), - cb, cb->parser_pool)); + cb, TRUE, cb->parser_pool)); /* * Pass 1.6487: Parse the global groups file. @@ -1353,7 +1353,7 @@ svn_authz__parse(authz_full_t **authz, close_section, groups_add_value, cb->parser_pool), - cb, cb->parser_pool)); + cb, TRUE, cb->parser_pool)); } /* Index: subversion/libsvn_subr/config.c =================================================================== --- subversion/libsvn_subr/config.c (revision 1847937) +++ subversion/libsvn_subr/config.c (working copy) @@ -194,7 +194,7 @@ svn_config_parse(svn_config_t **cfgp, svn_stream_t NULL, NULL, svn_config__default_add_value_fn, scratch_pool), - cfg, scratch_pool); + cfg, FALSE, scratch_pool); if (err == SVN_NO_ERROR) *cfgp = cfg; Index: subversion/libsvn_subr/config_file.c =================================================================== --- subversion/libsvn_subr/config_file.c (revision 1847937) +++ subversion/libsvn_subr/config_file.c (working copy) @@ -40,6 +40,7 @@ #include "svn_ctype.h" #include "private/svn_config_private.h" +#include "private/svn_string_private.h" #include "private/svn_subr_private.h" #include "svn_private_config.h" @@ -59,6 +60,9 @@ typedef struct parse_context_t svn_config__constructor_t *constructor; void *constructor_baton; + /* Defines the behaviour of the section parser. */ + svn_boolean_t strict_sections; + /* The stream struct */ svn_stream_t *stream; @@ -550,10 +554,9 @@ parse_option(int *pch, parse_context_t *ctx, apr_p } -/* Read chars until enounter ']', then skip everything to the end of - * the line. Set *PCH to the character that ended the line (either - * newline or EOF), and set CTX->section to the string of characters - * seen before ']'. +/* Set *PCH to the character that ended the line (either newline or EOF), + * and set CTX->section to the parsed section name. Note that how the + * section name is parsed depends on CTX->strict_sections. * * This is meant to be called immediately after reading the '[' that * starts a section name. @@ -564,23 +567,60 @@ parse_section_name(int *pch, parse_context_t *ctx, { svn_error_t *err = SVN_NO_ERROR; int ch; - char *terminal; SVN_ERR(parser_get_line(ctx, ctx->section, &ch)); - terminal = strchr(ctx->section->data, ']'); - if (!terminal) + if (ctx->strict_sections) { - ch = EOF; - err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, - _("line %d: Section header must end with ']'"), - ctx->line); + const char *terminal; + + /* The last non-whitespace char in the line must be the closing ']'. */ + + svn_stringbuf__strip_trailing_whitespace(ctx->section); + terminal = (0 >= ctx->section->len ? NULL + : &ctx->section->data[ctx->section->len - 1]); + + if (!terminal || *terminal != ']') + { + ch = EOF; + if (strchr(ctx->section->data, ']')) + err = svn_error_createf( + SVN_ERR_MALFORMED_FILE, NULL, + _("line %d: Invalid characters after ']'"), + ctx->line); + else + err = svn_error_createf( + SVN_ERR_MALFORMED_FILE, NULL, + _("line %d: Section header must end with ']'"), + ctx->line); + } + else + { + /* Cut away the ']'. */ + svn_stringbuf_chop(ctx->section, 1); + } } else { - /* Everything from the ']' to the end of the line is ignored. */ - *terminal = 0; - ctx->section->len = terminal - ctx->section->data; + /* The first ']' terminates the section name, then skip everything to + the end of the line. */ + + char *const terminal = strchr(ctx->section->data, ']'); + + if (!terminal) + { + ch = EOF; + err = svn_error_createf( + SVN_ERR_MALFORMED_FILE, NULL, + _("line %d: Section header must end with ']'"), + ctx->line); + } + else + { + /* Everything from the ']' to the end of the line is ignored. */ + *terminal = 0; + ctx->section->len = terminal - ctx->section->data; + } } *pch = ch; @@ -739,7 +779,7 @@ svn_config__parse_file(svn_config_t *cfg, const ch NULL, NULL, svn_config__default_add_value_fn, scratch_pool), - cfg, scratch_pool); + cfg, FALSE, scratch_pool); if (err != SVN_NO_ERROR) { @@ -759,6 +799,7 @@ svn_error_t * svn_config__parse_stream(svn_stream_t *stream, svn_config__constructor_t *constructor, void *constructor_baton, + svn_boolean_t strict_sections, apr_pool_t *scratch_pool) { parse_context_t *ctx; @@ -769,6 +810,7 @@ svn_config__parse_stream(svn_stream_t *stream, ctx->constructor = constructor; ctx->constructor_baton = constructor_baton; + ctx->strict_sections = strict_sections; ctx->stream = stream; ctx->line = 1; ctx->ungotten_char = EOF; Index: subversion/libsvn_subr/string.c =================================================================== --- subversion/libsvn_subr/string.c (revision 1847937) +++ subversion/libsvn_subr/string.c (working copy) @@ -831,6 +831,13 @@ svn_stringbuf_first_non_whitespace(const svn_strin return string_first_non_whitespace(str->data, str->len); } +void +svn_stringbuf__strip_trailing_whitespace(svn_stringbuf_t *str) +{ + while ((str->len > 0) && svn_ctype_isspace(str->data[str->len - 1])) + str->len--; + str->data[str->len] = '\0'; +} void svn_stringbuf_strip_whitespace(svn_stringbuf_t *str) @@ -849,9 +856,7 @@ svn_stringbuf_strip_whitespace(svn_stringbuf_t *st } /* Now that we've trimmed the front, trim the end, wasting more RAM. */ - while ((str->len > 0) && svn_ctype_isspace(str->data[str->len - 1])) - str->len--; - str->data[str->len] = '\0'; + svn_stringbuf__strip_trailing_whitespace(str); }