Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,13 @@ static String missingInRuntime(CamelCatalog catalog, String scheme) {

static final Set<String> FILE_SCHEMES = Set.of("file", "ftp", "ftps", "sftp", "file-watch", "smb");

/**
* A doubled backslash before a character that a single backslash would escape in a regex (\\. \\d \\( ...): the
* user meant the escape. A doubled backslash before any other character (\\myfile) is left alone: \myfile is not a
* regex escape, so a literal backslash is the only thing it can mean.
*/
static final Pattern DOUBLED_BACKSLASH_ESCAPE = Pattern.compile("\\\\\\\\[.dswDSWbB()\\[\\]{}+*?|^$]");

/**
* include and exclude on the file components are regular expressions: include=*.txt fails at startup with a
* PatternSyntaxException wrapped in a binding error. Says to write .*\\.txt or use antInclude.
Expand All @@ -326,6 +333,15 @@ static void checkRegexOptions(List<String> errors, String fullUri, int uriLineId
if (!name.equals("include") && !name.equals("exclude") || value.startsWith("{{")) {
continue;
}
if (DOUBLED_BACKSLASH_ESCAPE.matcher(value).find()) {
// '.*\\.json$' in single quotes: YAML keeps both backslashes, and in a regex \\ is one literal
// backslash, so the pattern matches a file name with a backslash in it: no file matches and the route
// runs in silence (CAMEL-24854)
errors.add(linePrefix(optionLineMap.getOrDefault(name, uriLineIdx)) + fullUri.substring(0, colon) + ": "
+ name + "=" + value + " matches a literal backslash in the file name (in a regex \\\\ is one"
+ " backslash and \\. is a dot): write " + name + "='" + value.replace("\\\\", "\\") + "'");
continue;
}
try {
Pattern.compile(value);
} catch (java.util.regex.PatternSyntaxException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.regex.Pattern;

import org.apache.camel.util.StringHelper;
import org.apache.camel.util.json.Jsoner;

/**
* Line-level helpers over a YAML source shared by the checks of {@link SourceValidator}: the enclosing EIP of a line,
Expand Down Expand Up @@ -157,9 +158,23 @@ static boolean isBlockScalarIndicator(String val) {
&& val.substring(1).chars().allMatch(c -> c == '-' || c == '+' || Character.isDigit(c));
}

/**
* The value of a quoted scalar: inside double quotes YAML reads \\ as one backslash and \" as a quote (so
* ".*\\.pdf" is the regex .*\.pdf), inside single quotes a backslash is a backslash.
*/
static String unquote(String val) {
if (val.length() >= 2 && val.startsWith("\"") && val.endsWith("\"")) {
return val.substring(1, val.length() - 1);
String inner = val.substring(1, val.length() - 1);
if (inner.indexOf('\\') < 0) {
return inner;
}
try {
// the JSON escapes are the YAML ones that matter here (\\ \" \n \t and unicode)
return Jsoner.unescape(inner);
} catch (RuntimeException e) {
// a YAML-only escape such as \e or \x41: the text as written
return inner;
}
}
if (val.length() >= 2 && val.startsWith("'") && val.endsWith("'")) {
return val.substring(1, val.length() - 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,4 +592,35 @@ void aDynamicDirectoryOnAFileEndpointSaysToUseFileNameOrToD() {
assertThat(SourceValidator.validateYamlEndpoints(fromYaml, catalog))
.anyMatch(e -> e.startsWith("Line 3: file: the directory archived/${header.monthDir} cannot be dynamic"));
}

/** CAMEL-24854: a doubled backslash in an include regex (kept as is inside single quotes) matches no file. */
@Test
void aDoubledBackslashInAnIncludeRegexIsReported() {
String yaml = """
- route:
from:
uri: file:orders
parameters:
include: '.*\\\\.json$'
steps:
- to:
uri: log:done
""";
List<String> errors = SourceValidator.validateYamlEndpoints(yaml, catalog);
assertThat(errors)
.anyMatch(e -> e.startsWith("Line 5: file: include=.*\\\\.json$ matches a literal backslash in the file name")
&& e.endsWith("write include='.*\\.json$'"));

List<String> ok = SourceValidator.validateYamlEndpoints(yaml.replace("\\\\.json", "\\.json"), catalog);
assertThat(ok).noneMatch(e -> e.contains("backslash"));

// in double quotes YAML reads \\ as one backslash: ".*\\.json$" is the regex .*\.json$, nothing to report
List<String> doubleQuoted
= SourceValidator.validateYamlEndpoints(yaml.replace("'.*\\\\.json$'", "\".*\\\\.json$\""), catalog);
assertThat(doubleQuoted).noneMatch(e -> e.contains("backslash"));

// \\myfile is a backslash on purpose: \myfile is not a regex escape, so there is nothing else it can mean
List<String> literal = SourceValidator.validateYamlEndpoints(yaml.replace(".*\\\\.json$", ".*\\\\myfile.*"), catalog);
assertThat(literal).noneMatch(e -> e.contains("backslash"));
}
}
Loading