Skip to content

Commit d2309bf

Browse files
authored
templates: Be strict about undefined variables (#5201)
2 parents 35d2ce8 + eeeec35 commit d2309bf

File tree

13 files changed

+66
-25
lines changed

13 files changed

+66
-25
lines changed

crates/cli/src/commands/server.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,14 @@ impl Options {
160160
)?;
161161

162162
// Load and compile the templates
163-
let templates =
164-
templates_from_config(&config.templates, &site_config, &url_builder).await?;
163+
let templates = templates_from_config(
164+
&config.templates,
165+
&site_config,
166+
&url_builder,
167+
// Don't use strict mode in production yet
168+
false,
169+
)
170+
.await?;
165171
shutdown.register_reloadable(&templates);
166172

167173
let http_client = mas_http::reqwest_client();

crates/cli/src/commands/templates.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,14 @@ impl Options {
6565
&account_config,
6666
&captcha_config,
6767
)?;
68-
let templates =
69-
templates_from_config(&template_config, &site_config, &url_builder).await?;
68+
let templates = templates_from_config(
69+
&template_config,
70+
&site_config,
71+
&url_builder,
72+
// Use strict mode in template checks
73+
true,
74+
)
75+
.await?;
7076
templates.check_render(clock.now(), &mut rng)?;
7177

7278
Ok(ExitCode::SUCCESS)

crates/cli/src/commands/worker.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,14 @@ impl Options {
5252
)?;
5353

5454
// Load and compile the templates
55-
let templates =
56-
templates_from_config(&config.templates, &site_config, &url_builder).await?;
55+
let templates = templates_from_config(
56+
&config.templates,
57+
&site_config,
58+
&url_builder,
59+
// Don't use strict mode on task workers for now
60+
false,
61+
)
62+
.await?;
5763

5864
let mailer = mailer_from_config(&config.email, &templates)?;
5965
test_mailer_in_background(&mailer, Duration::from_secs(30));

crates/cli/src/util.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ pub async fn templates_from_config(
232232
config: &TemplatesConfig,
233233
site_config: &SiteConfig,
234234
url_builder: &UrlBuilder,
235+
strict: bool,
235236
) -> Result<Templates, anyhow::Error> {
236237
Templates::load(
237238
config.path.clone(),
@@ -240,6 +241,7 @@ pub async fn templates_from_config(
240241
config.translations_path.clone(),
241242
site_config.templates_branding(),
242243
site_config.templates_features(),
244+
strict,
243245
)
244246
.await
245247
.with_context(|| format!("Failed to load the templates at {}", config.path))

crates/handlers/src/test_utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ impl TestState {
176176
workspace_root.join("translations"),
177177
site_config.templates_branding(),
178178
site_config.templates_features(),
179+
// Strict mode in testing
180+
true,
179181
)
180182
.await?;
181183

crates/templates/src/context/branding.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ impl Object for SiteBranding {
5858
fn get_value(self: &Arc<Self>, name: &Value) -> Option<Value> {
5959
match name.as_str()? {
6060
"server_name" => Some(self.server_name.clone().into()),
61-
"policy_uri" => self.policy_uri.clone().map(Value::from),
62-
"tos_uri" => self.tos_uri.clone().map(Value::from),
63-
"imprint" => self.imprint.clone().map(Value::from),
61+
"policy_uri" => Some(Value::from(self.policy_uri.clone())),
62+
"tos_uri" => Some(Value::from(self.tos_uri.clone())),
63+
"imprint" => Some(Value::from(self.imprint.clone())),
6464
_ => None,
6565
}
6666
}

crates/templates/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use camino::{Utf8Path, Utf8PathBuf};
1717
use mas_i18n::Translator;
1818
use mas_router::UrlBuilder;
1919
use mas_spa::ViteManifest;
20-
use minijinja::Value;
20+
use minijinja::{UndefinedBehavior, Value};
2121
use rand::Rng;
2222
use serde::Serialize;
2323
use thiserror::Error;
@@ -71,6 +71,9 @@ pub struct Templates {
7171
vite_manifest_path: Utf8PathBuf,
7272
translations_path: Utf8PathBuf,
7373
path: Utf8PathBuf,
74+
/// Whether template rendering is in strict mode (for testing,
75+
/// until this can be rolled out in production.)
76+
strict: bool,
7477
}
7578

7679
/// There was an issue while loading the templates
@@ -151,6 +154,7 @@ impl Templates {
151154
translations_path: Utf8PathBuf,
152155
branding: SiteBranding,
153156
features: SiteFeatures,
157+
strict: bool,
154158
) -> Result<Self, TemplateLoadingError> {
155159
let (translator, environment) = Self::load_(
156160
&path,
@@ -159,6 +163,7 @@ impl Templates {
159163
&translations_path,
160164
branding.clone(),
161165
features,
166+
strict,
162167
)
163168
.await?;
164169
Ok(Self {
@@ -170,6 +175,7 @@ impl Templates {
170175
translations_path,
171176
branding,
172177
features,
178+
strict,
173179
})
174180
}
175181

@@ -180,6 +186,7 @@ impl Templates {
180186
translations_path: &Utf8Path,
181187
branding: SiteBranding,
182188
features: SiteFeatures,
189+
strict: bool,
183190
) -> Result<(Arc<Translator>, Arc<minijinja::Environment<'static>>), TemplateLoadingError> {
184191
let path = path.to_owned();
185192
let span = tracing::Span::current();
@@ -205,6 +212,15 @@ impl Templates {
205212
span.in_scope(move || {
206213
let mut loaded: HashSet<_> = HashSet::new();
207214
let mut env = minijinja::Environment::new();
215+
// Don't allow use of undefined variables
216+
env.set_undefined_behavior(if strict {
217+
UndefinedBehavior::Strict
218+
} else {
219+
// For now, allow semi-strict, because we don't have total test coverage of
220+
// tests and some tests rely on if conditions against sometimes-undefined
221+
// variables
222+
UndefinedBehavior::SemiStrict
223+
});
208224
let root = path.canonicalize_utf8()?;
209225
info!(%root, "Loading templates from filesystem");
210226
for entry in walkdir::WalkDir::new(&root)
@@ -275,6 +291,7 @@ impl Templates {
275291
&self.translations_path,
276292
self.branding.clone(),
277293
self.features,
294+
self.strict,
278295
)
279296
.await?;
280297

@@ -524,6 +541,8 @@ mod tests {
524541
translations_path,
525542
branding,
526543
features,
544+
// Use strict mode in tests
545+
true,
527546
)
528547
.await
529548
.unwrap();

templates/base.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{{ captcha.head() }}
2929
</head>
3030
<body>
31-
<div class="layout-container{% if consent_page %} consent{% endif %}">
31+
<div class="layout-container{% if consent_page is defined %} consent{% endif %}">
3232
{% block content %}{% endblock content %}
3333
{% include "components/footer.html" %}
3434
</div>

templates/components/footer.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,27 @@
77
-#}
88

99
<footer class="legal-footer">
10-
{%- if branding.policy_uri or branding.tos_uri -%}
10+
{%- if branding.policy_uri is not none or branding.tos_uri is not none -%}
1111
<nav>
12-
{%- if branding.policy_uri -%}
12+
{%- if branding.policy_uri is not none -%}
1313
<a href="{{ branding.policy_uri }}" referrerpolicy="no-referrer" title="{{ _('branding.privacy_policy.alt') }}" class="cpd-link" data-kind="primary">
1414
{{- _("branding.privacy_policy.link") -}}
1515
</a>
1616
{%- endif -%}
1717

18-
{%- if branding.policy_uri and branding.tos_uri -%}
18+
{%- if branding.policy_uri is not none and branding.tos_uri is not none -%}
1919
<div class="separator" aria-hidden="true"></div>
2020
{%- endif -%}
2121

22-
{%- if branding.tos_uri -%}
22+
{%- if branding.tos_uri is not none -%}
2323
<a href="{{ branding.tos_uri }}" referrerpolicy="no-referrer" title="{{ _('branding.terms_and_conditions.alt') }}" class="cpd-link" data-kind="primary">
2424
{{- _("branding.terms_and_conditions.link") -}}
2525
</a>
2626
{%- endif -%}
2727
</nav>
2828
{%- endif -%}
2929

30-
{%- if branding.imprint -%}
30+
{%- if branding.imprint is not none -%}
3131
<p class="imprint">{{ branding.imprint }}</p>
3232
{%- endif -%}
3333
</footer>

templates/form_post.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ <h1 class="title">{{ _("common.loading") }}</h1>
1515
</div>
1616
</header>
1717

18-
<form method="post" class="flex flex-col"{% if redirect_uri %} action="{{ redirect_uri }}"{% endif %}>
18+
<form method="post" class="flex flex-col"{% if redirect_uri is not none %} action="{{ redirect_uri }}"{% endif %}>
1919
{% for key, value in params|items %}
2020
<input type="hidden" name="{{ key }}" value="{{ value }}" />
2121
{% endfor %}

0 commit comments

Comments
 (0)