Skip to content

Commit

Permalink
Merge pull request #42783 from mkouba/issue-42761
Browse files Browse the repository at this point in the history
Qute: ignore template files that contain whitespace in its name
  • Loading branch information
gsmet authored Aug 27, 2024
2 parents 68d7859 + ec09ddc commit 6a41ad8
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 25 deletions.
2 changes: 2 additions & 0 deletions docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,8 @@ If you want to use Qute in your Quarkus application, add the following dependenc
In Quarkus, a preconfigured engine instance is provided and available for injection - a bean with scope `@ApplicationScoped`, bean type `io.quarkus.qute.Engine` and qualifier `@Default` is registered automatically.
Moreover, all templates located in the `src/main/resources/templates` directory are validated and can be easily injected.

NOTE: A valid template file name is a sequence of non-whitespace characters. For example, a template file named `foo and bar.html` will be ignored.

[source,java]
----
import io.quarkus.qute.Engine;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
import io.quarkus.qute.ErrorCode;
import io.quarkus.qute.Expression;
import io.quarkus.qute.Expression.VirtualMethodPart;
import io.quarkus.qute.Identifiers;
import io.quarkus.qute.LoopSectionHelper;
import io.quarkus.qute.NamespaceResolver;
import io.quarkus.qute.ParameterDeclaration;
Expand Down Expand Up @@ -2180,6 +2181,10 @@ private void scanPathTree(PathTree pathTree, TemplateRootsBuildItem templateRoot
if (PathTreeUtils.containsCaseSensitivePath(pathTree, templateRoot)) {
pathTree.walkIfContains(templateRoot, visit -> {
if (Files.isRegularFile(visit.getPath())) {
if (!Identifiers.isValid(visit.getPath().getFileName().toString())) {
LOGGER.warnf("Invalid file name detected [%s] - template is ignored", visit.getPath());
return;
}
LOGGER.debugf("Found template: %s", visit.getPath());
// remove templateRoot + /
final String relativePath = visit.getRelativePath();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.quarkus.qute.deployment.identifiers;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.fail;

import java.util.logging.LogRecord;

import jakarta.inject.Inject;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.qute.Engine;
import io.quarkus.test.QuarkusUnitTest;

public class InvalidTemplateFileNameIgnoredTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addAsResource(new StringAsset(
"ignored"),
"templates/foo o.txt"))
.setLogRecordPredicate(log -> log.getLoggerName().contains("QuteProcessor"))
.assertLogRecords(records -> {
for (LogRecord r : records) {
if (r.getMessage().startsWith("Invalid file name detected")) {
return;
}
}
fail();
});

@Inject
Engine engine;

@Test
public void testTemplateFileIgnored() {
assertFalse(engine.isTemplateLoaded("foo o.txt"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ default Template parse(String content, Variant variant) {
String mapResult(Object result, Expression expression);

/**
* A valid identifier is a sequence of non-whitespace characters.
*
* @param id
* @param template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public String mapResult(Object result, Expression expression) {
}

public Template putTemplate(String id, Template template) {
if (!Identifiers.isValid(id)) {
throw new IllegalArgumentException("Invalid identifier found: [" + id + "]");
}
return templates.put(id, template);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.quarkus.qute;

public final class Identifiers {

/**
* A valid identifier is a sequence of non-whitespace characters.
*
* @param value
* @return {@code true} if the value represents a valid identifier, {@code false} otherwise
*/
public static boolean isValid(String value) {
if (value == null || value.isBlank()) {
return false;
}
int offset = 0;
int length = value.length();
while (offset < length) {
int c = value.codePointAt(offset);
if (!Character.isWhitespace(c)) {
offset += Character.charCount(c);
continue;
}
return false;
}
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -400,20 +400,6 @@ private boolean isValidIdentifierStart(char character) {
|| Character.isAlphabetic(character);
}

static boolean isValidIdentifier(String value) {
int offset = 0;
int length = value.length();
while (offset < length) {
int c = value.codePointAt(offset);
if (!Character.isWhitespace(c)) {
offset += Character.charCount(c);
continue;
}
return false;
}
return true;
}

private boolean isLineSeparatorStart(char character) {
return character == LINE_SEPARATOR_CR || character == LINE_SEPARATOR_LF;
}
Expand Down Expand Up @@ -1084,7 +1070,7 @@ private static Part createPart(Supplier<Integer> idGenerator, String namespace,
.build();
}
} else {
if (!isValidIdentifier(value)) {
if (!Identifiers.isValid(value)) {
throw error(ParserError.INVALID_IDENTIFIER, "invalid identifier found [{value}]", origin)
.argument("value", value)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand Down Expand Up @@ -116,4 +117,11 @@ public void accept(TemplateInstance templateInstance) {
assertEquals(engine1.getSectionHelperFactories().size(), engine2.getSectionHelperFactories().size());
}

@Test
public void testInvalidTemplateIdentifier() {
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
() -> Engine.builder().build().putTemplate("foo o", null));
assertEquals("Invalid identifier found: [foo o]", e.getMessage());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.qute;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

public class IdentifiersTest {

@Test
public void testIsValid() {
assertTrue(Identifiers.isValid("foo"));
assertTrue(Identifiers.isValid("foo-bar"));
assertTrue(Identifiers.isValid("fíí_"));
assertFalse(Identifiers.isValid("foo bar"));
assertFalse(Identifiers.isValid(""));
assertFalse(Identifiers.isValid(" "));
assertTrue(Identifiers.isValid("%ů="));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,16 @@ public void testRemoveStandaloneLines() {

@Test
public void testValidIdentifiers() {
assertTrue(Parser.isValidIdentifier("foo"));
assertTrue(Parser.isValidIdentifier("_foo"));
assertTrue(Parser.isValidIdentifier("foo$$bar"));
assertTrue(Parser.isValidIdentifier("1Foo_$"));
assertTrue(Parser.isValidIdentifier("1"));
assertTrue(Parser.isValidIdentifier("1?"));
assertTrue(Parser.isValidIdentifier("1:"));
assertTrue(Parser.isValidIdentifier("-foo"));
assertTrue(Parser.isValidIdentifier("foo["));
assertTrue(Parser.isValidIdentifier("foo^"));
assertTrue(Identifiers.isValid("foo"));
assertTrue(Identifiers.isValid("_foo"));
assertTrue(Identifiers.isValid("foo$$bar"));
assertTrue(Identifiers.isValid("1Foo_$"));
assertTrue(Identifiers.isValid("1"));
assertTrue(Identifiers.isValid("1?"));
assertTrue(Identifiers.isValid("1:"));
assertTrue(Identifiers.isValid("-foo"));
assertTrue(Identifiers.isValid("foo["));
assertTrue(Identifiers.isValid("foo^"));
Engine engine = Engine.builder().addDefaults().build();
assertThatExceptionOfType(TemplateException.class)
.isThrownBy(() -> engine.parse("{foo\nfoo}"))
Expand Down

0 comments on commit 6a41ad8

Please sign in to comment.