Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ApplicationContext inaccessible in ITemplateContext #6680

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 @@ -45,6 +45,6 @@ protected void doProcess(ITemplateContext context, IProcessableElementTag tag,
structureHandler.replaceWith("", false);
return;
}
commentWidget.render(context, tag, structureHandler);
commentWidget.render(new SecureTemplateContext(context), tag, structureHandler);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ protected void doProcess(ITemplateContext context, IModel model,

// apply processors to modelToInsert
getTemplateHeadProcessors(context)
.concatMap(processor -> processor.process(context, modelToInsert, structureHandler))
.concatMap(processor -> processor.process(
new SecureTemplateContext(context), modelToInsert, structureHandler)
)
.then()
.block();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ private IProcessableElementTag handleElementTag(
var context = getContext();
for (ElementTagPostProcessor elementTagPostProcessor : postProcessors) {
tagProcessorChain = tagProcessorChain.flatMap(
tag -> elementTagPostProcessor.process(context, tag).defaultIfEmpty(tag)
tag -> elementTagPostProcessor.process(new SecureTemplateContext(context), tag)
.defaultIfEmpty(tag)
);
}
processedTag =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package run.halo.app.theme.dialect;

import static org.thymeleaf.spring6.expression.ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.context.IdentifierSequences;
import org.thymeleaf.engine.TemplateData;
import org.thymeleaf.expression.IExpressionObjects;
import org.thymeleaf.inline.IInliner;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.templatemode.TemplateMode;

/**
* Secure template context.
*
* @author johnniang
* @since 2.20.0
*/
class SecureTemplateContext implements ITemplateContext {

private static final Set<String> DANGEROUS_VARIABLES =
Set.of(THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME);

private final ITemplateContext delegate;

public SecureTemplateContext(ITemplateContext delegate) {
this.delegate = delegate;
}

@Override
public TemplateData getTemplateData() {
return delegate.getTemplateData();
}

@Override
public TemplateMode getTemplateMode() {
return delegate.getTemplateMode();
}

@Override
public List<TemplateData> getTemplateStack() {
return delegate.getTemplateStack();
}

@Override
public List<IProcessableElementTag> getElementStack() {
return delegate.getElementStack();
}

@Override
public Map<String, Object> getTemplateResolutionAttributes() {
return delegate.getTemplateResolutionAttributes();
}

@Override
public IModelFactory getModelFactory() {
return delegate.getModelFactory();
}

@Override
public boolean hasSelectionTarget() {
return delegate.hasSelectionTarget();
}

@Override
public Object getSelectionTarget() {
return delegate.getSelectionTarget();
}

@Override
public IInliner getInliner() {
return delegate.getInliner();
}

@Override
public String getMessage(
Class<?> origin,
String key,
Object[] messageParameters,
boolean useAbsentMessageRepresentation
) {
return delegate.getMessage(origin, key, messageParameters, useAbsentMessageRepresentation);
}

@Override
public String buildLink(String base, Map<String, Object> parameters) {
return delegate.buildLink(base, parameters);
}

@Override
public IdentifierSequences getIdentifierSequences() {
return delegate.getIdentifierSequences();
}

@Override
public IEngineConfiguration getConfiguration() {
return delegate.getConfiguration();
}

@Override
public IExpressionObjects getExpressionObjects() {
return delegate.getExpressionObjects();
}

@Override
public Locale getLocale() {
return delegate.getLocale();
}

@Override
public boolean containsVariable(String name) {
if (DANGEROUS_VARIABLES.contains(name)) {
return false;
}
return delegate.containsVariable(name);
}

@Override
public Set<String> getVariableNames() {
return delegate.getVariableNames()
.stream()
.filter(name -> !DANGEROUS_VARIABLES.contains(name))
.collect(Collectors.toSet());
}

@Override
public Object getVariable(String name) {
if (DANGEROUS_VARIABLES.contains(name)) {
return null;
}
return delegate.getVariable(name);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

SecureTemplateContext that = (SecureTemplateContext) o;
return Objects.equals(delegate, that.delegate);
}

@Override
public int hashCode() {
return Objects.hashCode(delegate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ protected void doProcess(ITemplateContext context, IProcessableElementTag tag,
modelToInsert.add(context.getModelFactory().createText(globalFooterText));

getTemplateFooterProcessors(context)
.concatMap(processor -> processor.process(context, tag,
structureHandler, modelToInsert)
.concatMap(processor -> processor.process(
new SecureTemplateContext(context), tag, structureHandler, modelToInsert)
)
.then()
.block();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void shouldHandleStandaloneElementIfNoElementTagProcessors(
void shouldHandleStandaloneElementIfOneElementTagProcessorProvided() {
var processor = mock(ElementTagPostProcessor.class);
var newTag = mock(IStandaloneElementTag.class);
when(processor.process(templateContext, standaloneElementTag))
when(processor.process(new SecureTemplateContext(templateContext), standaloneElementTag))
.thenReturn(Mono.just(newTag));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor));
Expand All @@ -97,7 +97,7 @@ void shouldHandleStandaloneElementIfOneElementTagProcessorProvided() {
void shouldHandleStandaloneElementIfTagTypeChanged() {
var processor = mock(ElementTagPostProcessor.class);
var newTag = mock(IStandaloneElementTag.class);
when(processor.process(templateContext, standaloneElementTag))
when(processor.process(new SecureTemplateContext(templateContext), standaloneElementTag))
.thenReturn(Mono.just(newTag));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor));
Expand All @@ -114,9 +114,9 @@ void shouldHandleStandaloneElementIfMoreElementTagProcessorsProvided() {
var processor2 = mock(ElementTagPostProcessor.class);
var newTag1 = mock(IStandaloneElementTag.class);
var newTag2 = mock(IStandaloneElementTag.class);
when(processor1.process(templateContext, standaloneElementTag))
when(processor1.process(new SecureTemplateContext(templateContext), standaloneElementTag))
.thenReturn(Mono.just(newTag1));
when(processor2.process(templateContext, newTag1))
when(processor2.process(new SecureTemplateContext(templateContext), newTag1))
.thenReturn(Mono.just(newTag2));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor1, processor2));
Expand All @@ -131,7 +131,7 @@ void shouldHandleStandaloneElementIfMoreElementTagProcessorsProvided() {
void shouldNotHandleIfProcessedTagTypeChanged() {
var processor = mock(ElementTagPostProcessor.class);
var newTag = mock(IOpenElementTag.class);
when(processor.process(templateContext, standaloneElementTag))
when(processor.process(new SecureTemplateContext(templateContext), standaloneElementTag))
.thenReturn(Mono.just(newTag));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor));
Expand Down
Loading