1
0

DocsControllerTest.php 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Tests\Integration\Public;
  4. use App\Tests\Integration\Support\AppTestCase;
  5. /**
  6. * SEC_REVIEW F58 — `/api/docs` loads the RapiDoc viewer from jsDelivr.
  7. * The `<script>` tag must carry an SRI `integrity` hash and
  8. * `crossorigin="anonymous"` so the browser refuses to execute the JS
  9. * if a CDN compromise or in-flight content modification serves
  10. * different bytes.
  11. */
  12. final class DocsControllerTest extends AppTestCase
  13. {
  14. public function testDocsPageEmbedsRapiDocWithSriIntegrity(): void
  15. {
  16. $resp = $this->request('GET', '/api/docs');
  17. self::assertSame(200, $resp->getStatusCode());
  18. self::assertStringContainsString('text/html', $resp->getHeaderLine('Content-Type'));
  19. $html = (string) $resp->getBody();
  20. // Script tag points at the locked RapiDoc version.
  21. self::assertStringContainsString(
  22. 'https://cdn.jsdelivr.net/npm/rapidoc@9.3.4/dist/rapidoc-min.js',
  23. $html,
  24. );
  25. // Integrity hash present and well-formed (sha384- + base64).
  26. self::assertMatchesRegularExpression(
  27. '/integrity="sha384-[A-Za-z0-9+\/=]{64}"/',
  28. $html,
  29. 'expected sha384 SRI on the rapidoc <script> tag',
  30. );
  31. // crossorigin=anonymous required alongside SRI for cross-origin scripts.
  32. self::assertStringContainsString('crossorigin="anonymous"', $html);
  33. }
  34. public function testOpenapiSpecIsServed(): void
  35. {
  36. // Sanity check that we didn't break the spec endpoint while
  37. // editing the controller.
  38. $resp = $this->request('GET', '/api/v1/openapi.yaml');
  39. // The spec file isn't shipped in tests; either 200 with content
  40. // or 500 with `spec_unavailable`. Both are acceptable here —
  41. // we just want to prove the route still wires through the
  42. // edited controller.
  43. self::assertContains($resp->getStatusCode(), [200, 500]);
  44. }
  45. }