Skip to content

Commit 12b5eba

Browse files
committed
Added allow_non_convergent to pattern_matching promise in edit_line
Ticket: ENT-3417 Signed-off-by: Victor Moene <[email protected]>
1 parent 4bff865 commit 12b5eba

File tree

2 files changed

+47
-12
lines changed

2 files changed

+47
-12
lines changed

cf-agent/files_editline.c

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,7 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
12631263
assert(a != NULL);
12641264
assert(pp != NULL);
12651265
assert(edcontext != NULL);
1266+
bool allow_non_convergent = PromiseGetConstraintAsBoolean(ctx, "allow_non_convergent", pp);
12661267

12671268
char line_buff[CF_EXPANDSIZE];
12681269
char after[CF_BUFSIZE];
@@ -1318,9 +1319,14 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13181319
// TODO: gripe if that truncated !
13191320

13201321
// Substitute into line_buff:
1321-
snprintf(line_buff + start_off, sizeof(line_buff) - start_off,
1322+
int needed = snprintf(line_buff + start_off, sizeof(line_buff) - start_off,
13221323
"%s%s", BufferData(replace), after);
1323-
// TODO: gripe if that truncated or failed !
1324+
if (needed < 0 || needed >= sizeof(line_buff) - start_off) {
1325+
RecordInterruption(ctx, pp, a, "Replacement string is too large. '%s' in '%s'",
1326+
a->column.column_separator, edcontext->filename);
1327+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1328+
break;
1329+
}
13241330
notfound = false;
13251331
replaced = true;
13261332

@@ -1330,17 +1336,32 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13301336
break;
13311337
}
13321338
}
1333-
1339+
char line_buff_cp[CF_EXPANDSIZE];
13341340
if (NotAnchored(pp->promiser) && BlockTextMatch(ctx, pp->promiser, line_buff, &start_off, &end_off))
13351341
{
1336-
RecordInterruption(ctx, pp, a,
1337-
"Promised replacement '%s' on line '%s' for pattern '%s'"
1338-
" is not convergent while editing '%s'"
1339-
" (regular expression matches the replacement string)",
1340-
line_buff, ip->name, pp->promiser, edcontext->filename);
1341-
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1342-
PromiseRef(LOG_LEVEL_ERR, pp);
1343-
break;
1342+
strlcpy(line_buff_cp, line_buff, sizeof(line_buff_cp));
1343+
strlcpy(after, line_buff_cp + end_off, sizeof(after));
1344+
int needed = snprintf(line_buff_cp + start_off, sizeof(line_buff_cp) - start_off,
1345+
"%s%s", BufferData(replace), after);
1346+
1347+
if (needed < 0 || needed >= sizeof(line_buff_cp) - start_off) {
1348+
RecordInterruption(ctx, pp, a, "Replacement string is too large. '%s' in '%s'",
1349+
a->column.column_separator, edcontext->filename);
1350+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1351+
break;
1352+
}
1353+
1354+
if (!allow_non_convergent || (strlen(line_buff) != strlen(line_buff_cp)))
1355+
{
1356+
RecordInterruption(ctx, pp, a,
1357+
"Promised replacement '%s' on line '%s' for pattern '%s'"
1358+
" is not convergent while editing '%s'"
1359+
" (regular expression matches the replacement string)",
1360+
line_buff, ip->name, pp->promiser, edcontext->filename);
1361+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1362+
PromiseRef(LOG_LEVEL_ERR, pp);
1363+
break;
1364+
}
13441365
}
13451366

13461367
if (!MakingChanges(ctx, pp, a, result, "replace pattern '%s' in '%s'", pp->promiser,
@@ -1366,8 +1387,21 @@ static bool ReplacePatterns(EvalContext *ctx, Item *file_start, Item *file_end,
13661387
break;
13671388
}
13681389

1369-
if (BlockTextMatch(ctx, pp->promiser, ip->name, &start_off, &end_off))
1390+
if (BlockTextMatch(ctx, pp->promiser, ip->name, &start_off, &end_off) && (!allow_non_convergent
1391+
|| (strlen(line_buff) != strlen(line_buff_cp))))
13701392
{
1393+
strlcpy(line_buff_cp, line_buff, sizeof(line_buff_cp));
1394+
strlcpy(after, line_buff_cp + end_off, sizeof(after));
1395+
int needed = snprintf(line_buff_cp + start_off, sizeof(line_buff_cp) - start_off,
1396+
"%s%s", BufferData(replace), after);
1397+
1398+
if (needed >= sizeof(line_buff_cp) - start_off) {
1399+
RecordInterruption(ctx, pp, a, "Buffer overflow: replacement string is too large. '%s' in '%s'",
1400+
a->column.column_separator, edcontext->filename);
1401+
*result = PromiseResultUpdate(*result, PROMISE_RESULT_INTERRUPTED);
1402+
break;
1403+
}
1404+
13711405
RecordInterruption(ctx, pp, a,
13721406
"Promised replacement '%s' for pattern '%s' is not properly convergent while editing '%s'"
13731407
" (pattern still matches the end-state replacement string '%s', consider use"

libpromises/mod_files.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ static const ConstraintSyntax CF_COLUMN_BODIES[] =
131131
static const ConstraintSyntax CF_REPLACE_BODIES[] =
132132
{
133133
ConstraintSyntaxNewBody("replace_with", &replace_with_body, "Search-replace pattern", SYNTAX_STATUS_NORMAL),
134+
ConstraintSyntaxNewBool("allow_non_convergent", "Allow to use non-convergent regular expressions in replace_patterns. Defaults to false", SYNTAX_STATUS_NORMAL),
134135
ConstraintSyntaxNewNull()
135136
};
136137

0 commit comments

Comments
 (0)