Skip to content

Commit 64d0893

Browse files
[5.x] Antlers hardening (#14092)
Co-authored-by: John Koster <john@stillat.com>
1 parent 3c008e0 commit 64d0893

70 files changed

Lines changed: 816 additions & 277 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Entries/Entry.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ public function autoGeneratedTitle()
10411041

10421042
// Since the slug is generated from the title, we'll avoid augmenting
10431043
// the slug which could result in an infinite loop in some cases.
1044-
$title = $this->withLocale($this->site()->lang(), fn () => (string) Antlers::parseUserContent($format, $this->augmented()->except('slug')->all()));
1044+
$title = $this->withLocale($this->site()->lang(), fn () => (string) Antlers::parse($format, $this->augmented()->except('slug')->all()));
10451045

10461046
return trim($title);
10471047
}
@@ -1065,7 +1065,7 @@ private function resolvePreviewTargetUrl($format)
10651065
}, $format);
10661066
}
10671067

1068-
return (string) Antlers::parseUserContent($format, array_merge($this->routeData(), [
1068+
return (string) Antlers::parse($format, array_merge($this->routeData(), [
10691069
'config' => Cascade::config(),
10701070
'site' => $this->site(),
10711071
'uri' => $this->uri(),

src/Facades/Antlers.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
/**
1010
* @method static Parser parser()
1111
* @method static mixed usingParser(Parser $parser, \Closure $callback)
12-
* @method static AntlersString parse(string $str, array $variables = [])
13-
* @method static AntlersString parseUserContent(string $str, array $variables = [])
14-
* @method static string parseLoop(string $content, array $data, bool $supplement = true, array $context = [])
12+
* @method static AntlersString parse(string $str, array $variables = [], bool $trusted = false)
13+
* @method static string parseLoop(string $content, array $data, bool $supplement = true, array $context = [], bool $trusted = false)
1514
* @method static array identifiers(string $content)
1615
*
1716
* @see \Statamic\View\Antlers\Antlers

src/Facades/Endpoint/Parse.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ class Parse
1717
* @param string $str String to parse
1818
* @param array $variables Variables to use
1919
* @param array $context Contextual variables to also use
20-
* @param bool $php Whether PHP should be allowed
20+
* @param bool $trusted Whether the template should be treated as trusted
2121
* @return string
2222
*/
23-
public function template($str, $variables = [], $context = [], $php = false)
23+
public function template($str, $variables = [], $context = [], $trusted = false)
2424
{
25-
return Antlers::parse($str, $variables, $context, $php);
25+
return Antlers::parse($str, array_merge($variables, $context), $trusted);
2626
}
2727

2828
/**
@@ -32,12 +32,12 @@ public function template($str, $variables = [], $context = [], $php = false)
3232
* @param array $data Variables to use, in a multidimensional array
3333
* @param bool $supplement Whether to supplement with contextual values
3434
* @param array $context Contextual variables to also use
35-
* @param bool $php Whether PHP should be allowed
35+
* @param bool $trusted Whether the template should be treated as trusted
3636
* @return string
3737
*/
38-
public function templateLoop($content, $data, $supplement = true, $context = [], $php = false)
38+
public function templateLoop($content, $data, $supplement = true, $context = [], $trusted = false)
3939
{
40-
return Antlers::parseLoop($content, $data, $supplement, $context, $php);
40+
return Antlers::parseLoop($content, $data, $supplement, $context, $trusted);
4141
}
4242

4343
/**

src/Facades/Parse.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
use Statamic\View\Antlers\AntlersString;
77

88
/**
9-
* @method static AntlersString template($str, $variables = [], $context = [], $php = false)
10-
* @method static string templateLoop($content, $data, $supplement = true, $context = [], $php = false)
9+
* @method static AntlersString template($str, $variables = [], $context = [], $trusted = false)
10+
* @method static string templateLoop($content, $data, $supplement = true, $context = [], $trusted = false)
1111
* @method static array YAML($str)
1212
* @method static array frontMatter($string)
1313
* @method static mixed env($val)

src/Forms/Email.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ protected function parseConfig(array $config)
245245
return collect($config)->map(function ($value) {
246246
$value = Parse::env($value); // deprecated
247247

248-
return (string) Antlers::parseUserContent($value, array_merge(
248+
return (string) Antlers::parse($value, array_merge(
249249
['config' => Cascade::config()],
250250
$this->getGlobalsData(),
251251
$this->submissionData,

src/Modifiers/CoreModifiers.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use Statamic\Support\Dumper;
3232
use Statamic\Support\Html;
3333
use Statamic\Support\Str;
34+
use Statamic\View\Antlers\Language\Runtime\GlobalRuntimeState;
3435
use Stringy\StaticStringy as Stringy;
3536

3637
class CoreModifiers extends Modifier
@@ -119,7 +120,9 @@ public function ampersandList($value, $params)
119120
*/
120121
public function antlers($value, $params, $context)
121122
{
122-
return (string) Antlers::parse($value, $context);
123+
$trusted = Arr::get($params, 0) === 'trusted' && ! GlobalRuntimeState::$isEvaluatingUserData;
124+
125+
return (string) Antlers::parse($value, $context, $trusted);
123126
}
124127

125128
/**
@@ -1872,7 +1875,7 @@ public function partial($value, $params, $context)
18721875

18731876
$partial = 'partials/'.$name.'.html';
18741877

1875-
return Parse::template(File::disk('resources')->get($partial), $value);
1878+
return Parse::template(File::disk('resources')->get($partial), $value, trusted: true);
18761879
}
18771880

18781881
/**

src/Providers/ViewServiceProvider.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,99 @@ private function registerAntlers()
102102
$runtimeConfig->guardedContentVariablePatterns = config('statamic.antlers.guardedContentVariables', []);
103103
$runtimeConfig->guardedContentTagPatterns = config('statamic.antlers.guardedContentTags', []);
104104
$runtimeConfig->guardedContentModifiers = config('statamic.antlers.guardedContentModifiers', []);
105+
$runtimeConfig->allowedContentTagPatterns = config('statamic.antlers.allowedContentTags', [
106+
'obfuscate:*',
107+
'trans:*',
108+
'trans_choice:*',
109+
'widont:*',
110+
]);
111+
$runtimeConfig->allowedContentModifiers = config('statamic.antlers.allowedContentModifiers', [
112+
'add_query_param',
113+
'add_slashes',
114+
'ascii',
115+
'at',
116+
'background_position',
117+
'bool_string',
118+
'camelize',
119+
'cdata',
120+
'ceil',
121+
'collapse_whitespace',
122+
'count_substring',
123+
'dashify',
124+
'decode',
125+
'deslugify',
126+
'divide',
127+
'ends_with',
128+
'ensure_left',
129+
'ensure_right',
130+
'entities',
131+
'explode',
132+
'extension',
133+
'floor',
134+
'format',
135+
'format_number',
136+
'format_translated',
137+
'has_lower_case',
138+
'has_upper_case',
139+
'headline',
140+
'hex_to_rgb',
141+
'insert',
142+
'is_alpha',
143+
'is_alphanumeric',
144+
'is_blank',
145+
'is_email',
146+
'is_external_url',
147+
'is_json',
148+
'is_lowercase',
149+
'is_numeric',
150+
'is_uppercase',
151+
'is_url',
152+
'join',
153+
'kebab',
154+
'lcfirst',
155+
'localize',
156+
'upper',
157+
'lower',
158+
'md5',
159+
'mod',
160+
'multiply',
161+
'obfuscate',
162+
'obfuscate_email',
163+
'parse_url',
164+
'pathinfo',
165+
'rawurlencode',
166+
'remove_left',
167+
'remove_query_param',
168+
'remove_right',
169+
'replace',
170+
'round',
171+
'safe_truncate',
172+
'sanitize',
173+
'slugify',
174+
'snake',
175+
'starts_with',
176+
'str_pad',
177+
'str_pad_both',
178+
'str_pad_left',
179+
'str_pad_right',
180+
'strip_tags',
181+
'studly',
182+
'subtract',
183+
'substr',
184+
'sum',
185+
'swap_case',
186+
'title',
187+
'to_bool',
188+
'to_string',
189+
'trans',
190+
'trans_choice',
191+
'trim',
192+
'truncate',
193+
'ucfirst',
194+
'urldecode',
195+
'urlencode',
196+
'widont',
197+
]);
105198
$runtimeConfig->allowPhpInUserContent = config('statamic.antlers.allowPhpInContent', false);
106199
$runtimeConfig->allowMethodsInUserContent = config('statamic.antlers.allowMethodsInContent', false);
107200

src/Tags/Tags.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Statamic\Facades\Antlers;
1111
use Statamic\Support\Arr;
1212
use Statamic\Support\Traits\Hookable;
13+
use Statamic\View\Antlers\Language\Runtime\GlobalRuntimeState;
1314

1415
abstract class Tags
1516
{
@@ -207,8 +208,10 @@ public function parse($data = [])
207208
}
208209

209210
return Antlers::usingParser($this->parser, function ($antlers) use ($data) {
211+
$trusted = ! GlobalRuntimeState::$isEvaluatingUserData;
212+
210213
return $antlers
211-
->parse($this->content, array_merge($this->context->all(), $data))
214+
->parse($this->content, array_merge($this->context->all(), $data), $trusted)
212215
->withoutExtractions();
213216
});
214217
}
@@ -244,8 +247,10 @@ public function parseLoop($data, $supplement = true)
244247
}
245248

246249
return Antlers::usingParser($this->parser, function ($antlers) use ($data, $supplement) {
250+
$trusted = ! GlobalRuntimeState::$isEvaluatingUserData;
251+
247252
return $antlers
248-
->parseLoop($this->content, $data, $supplement, $this->context->all())
253+
->parseLoop($this->content, $data, $supplement, $this->context->all(), $trusted)
249254
->withoutExtractions();
250255
});
251256
}

src/View/Antlers/Antlers.php

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,16 @@ public function usingParser(Parser $parser, Closure $callback)
2727
return $contents;
2828
}
2929

30-
public function parse($str, $variables = [])
30+
public function parse($str, $variables = [], $trusted = false)
3131
{
32-
return $this->parser()->parse($str, $variables);
33-
}
34-
35-
public function parseUserContent($str, $variables = [])
36-
{
37-
$isEvaluatingUserData = GlobalRuntimeState::$isEvaluatingUserData;
38-
GlobalRuntimeState::$isEvaluatingUserData = true;
32+
$parser = $this->parser();
33+
$previousState = GlobalRuntimeState::$isEvaluatingUserData;
34+
GlobalRuntimeState::$isEvaluatingUserData = ! $trusted;
3935

4036
try {
41-
return $this->parser()->parse($str, $variables);
37+
return $parser->parse($str, $variables);
4238
} finally {
43-
GlobalRuntimeState::$isEvaluatingUserData = $isEvaluatingUserData;
39+
GlobalRuntimeState::$isEvaluatingUserData = $previousState;
4440
}
4541
}
4642

@@ -51,11 +47,12 @@ public function parseUserContent($str, $variables = [])
5147
* @param array $data
5248
* @param bool $supplement
5349
* @param array $context
50+
* @param bool $trusted
5451
* @return string
5552
*/
56-
public function parseLoop($content, $data, $supplement = true, $context = [])
53+
public function parseLoop($content, $data, $supplement = true, $context = [], $trusted = false)
5754
{
58-
return new AntlersLoop($this->parser(), $content, $data, $supplement, $context);
55+
return new AntlersLoop($this->parser(), $content, $data, $supplement, $context, $trusted);
5956
}
6057

6158
public function identifiers(string $content): array

src/View/Antlers/AntlersLoop.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,40 @@
22

33
namespace Statamic\View\Antlers;
44

5+
use Statamic\View\Antlers\Language\Runtime\GlobalRuntimeState;
6+
57
class AntlersLoop extends AntlersString
68
{
79
protected $parser;
810
protected $string;
911
protected $variables;
1012
protected $supplement;
1113
protected $context;
14+
protected $trusted;
1215

13-
public function __construct($parser, $string, $variables, $supplement, $context)
16+
public function __construct($parser, $string, $variables, $supplement, $context, $trusted = false)
1417
{
1518
$this->parser = $parser;
1619
$this->string = $string;
1720
$this->variables = $variables;
1821
$this->supplement = $supplement;
1922
$this->context = $context;
23+
$this->trusted = $trusted;
2024
}
2125

2226
public function __toString()
27+
{
28+
$previousIsEvaluatingUserData = GlobalRuntimeState::$isEvaluatingUserData;
29+
GlobalRuntimeState::$isEvaluatingUserData = ! $this->trusted;
30+
31+
try {
32+
return $this->renderLoopContent();
33+
} finally {
34+
GlobalRuntimeState::$isEvaluatingUserData = $previousIsEvaluatingUserData;
35+
}
36+
}
37+
38+
private function renderLoopContent()
2339
{
2440
$total = count($this->variables);
2541
$i = 0;

0 commit comments

Comments
 (0)