| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 |
- <?php
- declare(strict_types=1);
- namespace App\Tests\Integration\Public;
- use App\Tests\Integration\Support\AppTestCase;
- /**
- * SEC_REVIEW F58 — `/api/docs` loads the RapiDoc viewer from jsDelivr.
- * The `<script>` tag must carry an SRI `integrity` hash and
- * `crossorigin="anonymous"` so the browser refuses to execute the JS
- * if a CDN compromise or in-flight content modification serves
- * different bytes.
- */
- final class DocsControllerTest extends AppTestCase
- {
- public function testDocsPageEmbedsRapiDocWithSriIntegrity(): void
- {
- $resp = $this->request('GET', '/api/docs');
- self::assertSame(200, $resp->getStatusCode());
- self::assertStringContainsString('text/html', $resp->getHeaderLine('Content-Type'));
- $html = (string) $resp->getBody();
- // Script tag points at the locked RapiDoc version.
- self::assertStringContainsString(
- 'https://cdn.jsdelivr.net/npm/rapidoc@9.3.4/dist/rapidoc-min.js',
- $html,
- );
- // Integrity hash present and well-formed (sha384- + base64).
- self::assertMatchesRegularExpression(
- '/integrity="sha384-[A-Za-z0-9+\/=]{64}"/',
- $html,
- 'expected sha384 SRI on the rapidoc <script> tag',
- );
- // crossorigin=anonymous required alongside SRI for cross-origin scripts.
- self::assertStringContainsString('crossorigin="anonymous"', $html);
- }
- public function testOpenapiSpecIsServed(): void
- {
- // Sanity check that we didn't break the spec endpoint while
- // editing the controller.
- $resp = $this->request('GET', '/api/v1/openapi.yaml');
- // The spec file isn't shipped in tests; either 200 with content
- // or 500 with `spec_unavailable`. Both are acceptable here —
- // we just want to prove the route still wires through the
- // edited controller.
- self::assertContains($resp->getStatusCode(), [200, 500]);
- }
- }
|