mirror of https://github.com/halo-dev/halo
Make ApplicationContext inaccessible in ITemplateContext (#6680)
#### What type of PR is this? /kind improvement /area core /area plugin /milestone 2.20.x #### What this PR does / why we need it: This PR disables access to ApplicationContext using ITemplateContext. #### Does this PR introduce a user-facing change? ```release-note None ```pull/6686/head
parent
fb9aff00ca
commit
a87dedd916
|
@ -45,6 +45,6 @@ public class CommentElementTagProcessor extends AbstractElementTagProcessor {
|
|||
structureHandler.replaceWith("", false);
|
||||
return;
|
||||
}
|
||||
commentWidget.render(context, tag, structureHandler);
|
||||
commentWidget.render(new SecureTemplateContext(context), tag, structureHandler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,9 @@ public class GlobalHeadInjectionProcessor extends AbstractElementModelProcessor
|
|||
|
||||
// apply processors to modelToInsert
|
||||
getTemplateHeadProcessors(context)
|
||||
.concatMap(processor -> processor.process(context, modelToInsert, structureHandler))
|
||||
.concatMap(processor -> processor.process(
|
||||
new SecureTemplateContext(context), modelToInsert, structureHandler)
|
||||
)
|
||||
.then()
|
||||
.block();
|
||||
|
||||
|
|
|
@ -57,7 +57,8 @@ public class HaloPostTemplateHandler extends AbstractTemplateHandler {
|
|||
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 =
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -58,8 +58,8 @@ public class TemplateFooterElementTagProcessor extends AbstractElementTagProcess
|
|||
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();
|
||||
|
|
|
@ -82,7 +82,7 @@ class HaloPostTemplateHandlerTest {
|
|||
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));
|
||||
|
@ -97,7 +97,7 @@ class HaloPostTemplateHandlerTest {
|
|||
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));
|
||||
|
@ -114,9 +114,9 @@ class HaloPostTemplateHandlerTest {
|
|||
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));
|
||||
|
@ -131,7 +131,7 @@ class HaloPostTemplateHandlerTest {
|
|||
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));
|
||||
|
|
Loading…
Reference in New Issue