|
@@ -0,0 +1,58 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+
|
|
|
|
|
+declare(strict_types=1);
|
|
|
|
|
+
|
|
|
|
|
+namespace App\Tests\Integration\App;
|
|
|
|
|
+
|
|
|
|
|
+use App\Tests\Integration\Support\AppTestCase;
|
|
|
|
|
+use Slim\Views\Twig;
|
|
|
|
|
+use Twig\Extension\EscaperExtension;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * SEC_REVIEW F63 — pin Twig's autoescape strategy to `'html'`. The
|
|
|
|
|
+ * current Twig major defaults to `'html'`, but the SEC_REVIEW asked
|
|
|
|
|
+ * us not to rely on the default in case a future major bump or a
|
|
|
|
|
+ * Slim-twig wrapper change silently flips it.
|
|
|
|
|
+ */
|
|
|
|
|
+final class TwigConfigTest extends AppTestCase
|
|
|
|
|
+{
|
|
|
|
|
+ protected function setUp(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $this->bootApp();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testAutoescapeStrategyIsExplicitlyHtml(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ /** @var Twig $twig */
|
|
|
|
|
+ $twig = $this->container->get(Twig::class);
|
|
|
|
|
+ $escaper = $twig->getEnvironment()->getExtension(EscaperExtension::class);
|
|
|
|
|
+
|
|
|
|
|
+ // `getDefaultStrategy` takes the template name; for the
|
|
|
|
|
+ // pinned `'html'` policy it returns the strategy regardless
|
|
|
|
|
+ // of name.
|
|
|
|
|
+ self::assertSame('html', $escaper->getDefaultStrategy('any.twig'));
|
|
|
|
|
+ self::assertSame('html', $escaper->getDefaultStrategy('pages/login.twig'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function testRenderedTemplateAutoescapesUserInput(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ // Defence-in-depth: render a string with a script tag through
|
|
|
|
|
+ // `{{ value }}` and confirm the output is HTML-escaped. The
|
|
|
|
|
+ // unit test above pins the configured strategy; this one
|
|
|
|
|
+ // proves the pipeline actually applies it.
|
|
|
|
|
+ /** @var Twig $twig */
|
|
|
|
|
+ $twig = $this->container->get(Twig::class);
|
|
|
|
|
+ $env = $twig->getEnvironment();
|
|
|
|
|
+
|
|
|
|
|
+ $env->setLoader(new \Twig\Loader\ArrayLoader([
|
|
|
|
|
+ 'inline.twig' => 'value=[{{ value }}]',
|
|
|
|
|
+ ]));
|
|
|
|
|
+
|
|
|
|
|
+ $rendered = $env->render('inline.twig', ['value' => '<script>alert(1)</script>']);
|
|
|
|
|
+
|
|
|
|
|
+ self::assertSame(
|
|
|
|
|
+ 'value=[<script>alert(1)</script>]',
|
|
|
|
|
+ $rendered,
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+}
|