• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 8586246081

07 Apr 2024 04:43AM UTC coverage: 86.602% (+1.0%) from 85.607%
8586246081

push

github

web-flow
Merge pull request #8720 from codeigniter4/4.5

Merge 4.5 into develop

2273 of 2603 new or added lines in 188 files covered. (87.32%)

53 existing lines in 18 files now uncovered.

19947 of 23033 relevant lines covered (86.6%)

189.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.32
/system/HTTP/ContentSecurityPolicy.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\HTTP;
15

16
use Config\App;
17
use Config\ContentSecurityPolicy as ContentSecurityPolicyConfig;
18

19
/**
20
 * Provides tools for working with the Content-Security-Policy header
21
 * to help defeat XSS attacks.
22
 *
23
 * @see http://www.w3.org/TR/CSP/
24
 * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/
25
 * @see http://content-security-policy.com/
26
 * @see https://www.owasp.org/index.php/Content_Security_Policy
27
 * @see \CodeIgniter\HTTP\ContentSecurityPolicyTest
28
 */
29
class ContentSecurityPolicy
30
{
31
    /**
32
     * CSP directives
33
     *
34
     * @var array<string, string>
35
     */
36
    protected array $directives = [
37
        'base-uri'        => 'baseURI',
38
        'child-src'       => 'childSrc',
39
        'connect-src'     => 'connectSrc',
40
        'default-src'     => 'defaultSrc',
41
        'font-src'        => 'fontSrc',
42
        'form-action'     => 'formAction',
43
        'frame-ancestors' => 'frameAncestors',
44
        'frame-src'       => 'frameSrc',
45
        'img-src'         => 'imageSrc',
46
        'media-src'       => 'mediaSrc',
47
        'object-src'      => 'objectSrc',
48
        'plugin-types'    => 'pluginTypes',
49
        'script-src'      => 'scriptSrc',
50
        'style-src'       => 'styleSrc',
51
        'manifest-src'    => 'manifestSrc',
52
        'sandbox'         => 'sandbox',
53
        'report-uri'      => 'reportURI',
54
    ];
55

56
    /**
57
     * Used for security enforcement
58
     *
59
     * @var array|string
60
     */
61
    protected $baseURI = [];
62

63
    /**
64
     * Used for security enforcement
65
     *
66
     * @var array|string
67
     */
68
    protected $childSrc = [];
69

70
    /**
71
     * Used for security enforcement
72
     *
73
     * @var array
74
     */
75
    protected $connectSrc = [];
76

77
    /**
78
     * Used for security enforcement
79
     *
80
     * @var array|string
81
     */
82
    protected $defaultSrc = [];
83

84
    /**
85
     * Used for security enforcement
86
     *
87
     * @var array|string
88
     */
89
    protected $fontSrc = [];
90

91
    /**
92
     * Used for security enforcement
93
     *
94
     * @var array|string
95
     */
96
    protected $formAction = [];
97

98
    /**
99
     * Used for security enforcement
100
     *
101
     * @var array|string
102
     */
103
    protected $frameAncestors = [];
104

105
    /**
106
     * Used for security enforcement
107
     *
108
     * @var array|string
109
     */
110
    protected $frameSrc = [];
111

112
    /**
113
     * Used for security enforcement
114
     *
115
     * @var array|string
116
     */
117
    protected $imageSrc = [];
118

119
    /**
120
     * Used for security enforcement
121
     *
122
     * @var array|string
123
     */
124
    protected $mediaSrc = [];
125

126
    /**
127
     * Used for security enforcement
128
     *
129
     * @var array|string
130
     */
131
    protected $objectSrc = [];
132

133
    /**
134
     * Used for security enforcement
135
     *
136
     * @var array|string
137
     */
138
    protected $pluginTypes = [];
139

140
    /**
141
     * Used for security enforcement
142
     *
143
     * @var array|string
144
     */
145
    protected $scriptSrc = [];
146

147
    /**
148
     * Used for security enforcement
149
     *
150
     * @var array|string
151
     */
152
    protected $styleSrc = [];
153

154
    /**
155
     * Used for security enforcement
156
     *
157
     * @var array|string
158
     */
159
    protected $manifestSrc = [];
160

161
    /**
162
     * Used for security enforcement
163
     *
164
     * @var array|string
165
     */
166
    protected $sandbox = [];
167

168
    /**
169
     * Used for security enforcement
170
     *
171
     * @var string|null
172
     */
173
    protected $reportURI;
174

175
    /**
176
     * Used for security enforcement
177
     *
178
     * @var bool
179
     */
180
    protected $upgradeInsecureRequests = false;
181

182
    /**
183
     * Used for security enforcement
184
     *
185
     * @var bool
186
     */
187
    protected $reportOnly = false;
188

189
    /**
190
     * Used for security enforcement
191
     *
192
     * @var array
193
     */
194
    protected $validSources = [
195
        'self',
196
        'none',
197
        'unsafe-inline',
198
        'unsafe-eval',
199
    ];
200

201
    /**
202
     * Used for security enforcement
203
     *
204
     * @var array
205
     */
206
    protected $nonces = [];
207

208
    /**
209
     * Nonce for style
210
     *
211
     * @var string
212
     */
213
    protected $styleNonce;
214

215
    /**
216
     * Nonce for script
217
     *
218
     * @var string
219
     */
220
    protected $scriptNonce;
221

222
    /**
223
     * Nonce tag for style
224
     *
225
     * @var string
226
     */
227
    protected $styleNonceTag = '{csp-style-nonce}';
228

229
    /**
230
     * Nonce tag for script
231
     *
232
     * @var string
233
     */
234
    protected $scriptNonceTag = '{csp-script-nonce}';
235

236
    /**
237
     * Replace nonce tag automatically
238
     *
239
     * @var bool
240
     */
241
    protected $autoNonce = true;
242

243
    /**
244
     * An array of header info since we have
245
     * to build ourself before passing to Response.
246
     *
247
     * @var array
248
     */
249
    protected $tempHeaders = [];
250

251
    /**
252
     * An array of header info to build
253
     * that should only be reported.
254
     *
255
     * @var array
256
     */
257
    protected $reportOnlyHeaders = [];
258

259
    /**
260
     * Whether Content Security Policy is being enforced.
261
     *
262
     * @var bool
263
     */
264
    protected $CSPEnabled = false;
265

266
    /**
267
     * Constructor.
268
     *
269
     * Stores our default values from the Config file.
270
     */
271
    public function __construct(ContentSecurityPolicyConfig $config)
272
    {
273
        $appConfig        = config(App::class);
518✔
274
        $this->CSPEnabled = $appConfig->CSPEnabled;
518✔
275

276
        foreach (get_object_vars($config) as $setting => $value) {
518✔
277
            if (property_exists($this, $setting)) {
518✔
278
                $this->{$setting} = $value;
518✔
279
            }
280
        }
281

282
        if (! is_array($this->styleSrc)) {
518✔
283
            $this->styleSrc = [$this->styleSrc];
518✔
284
        }
285

286
        if (! is_array($this->scriptSrc)) {
518✔
287
            $this->scriptSrc = [$this->scriptSrc];
518✔
288
        }
289
    }
290

291
    /**
292
     * Whether Content Security Policy is being enforced.
293
     */
294
    public function enabled(): bool
295
    {
296
        return $this->CSPEnabled;
115✔
297
    }
298

299
    /**
300
     * Get the nonce for the style tag.
301
     */
302
    public function getStyleNonce(): string
303
    {
304
        if ($this->styleNonce === null) {
8✔
305
            $this->styleNonce = bin2hex(random_bytes(12));
8✔
306
            $this->styleSrc[] = 'nonce-' . $this->styleNonce;
8✔
307
        }
308

309
        return $this->styleNonce;
8✔
310
    }
311

312
    /**
313
     * Get the nonce for the script tag.
314
     */
315
    public function getScriptNonce(): string
316
    {
317
        if ($this->scriptNonce === null) {
10✔
318
            $this->scriptNonce = bin2hex(random_bytes(12));
10✔
319
            $this->scriptSrc[] = 'nonce-' . $this->scriptNonce;
10✔
320
        }
321

322
        return $this->scriptNonce;
10✔
323
    }
324

325
    /**
326
     * Compiles and sets the appropriate headers in the request.
327
     *
328
     * Should be called just prior to sending the response to the user agent.
329
     *
330
     * @return void
331
     */
332
    public function finalize(ResponseInterface $response)
333
    {
334
        if ($this->autoNonce) {
36✔
335
            $this->generateNonces($response);
34✔
336
        }
337

338
        $this->buildHeaders($response);
36✔
339
    }
340

341
    /**
342
     * If TRUE, nothing will be restricted. Instead all violations will
343
     * be reported to the reportURI for monitoring. This is useful when
344
     * you are just starting to implement the policy, and will help
345
     * determine what errors need to be addressed before you turn on
346
     * all filtering.
347
     *
348
     * @return $this
349
     */
350
    public function reportOnly(bool $value = true)
351
    {
352
        $this->reportOnly = $value;
7✔
353

354
        return $this;
7✔
355
    }
356

357
    /**
358
     * Adds a new base_uri value. Can be either a URI class or a simple string.
359
     *
360
     * base_uri restricts the URLs that can appear in a page's <base> element.
361
     *
362
     * @see http://www.w3.org/TR/CSP/#directive-base-uri
363
     *
364
     * @param array|string $uri
365
     *
366
     * @return $this
367
     */
368
    public function addBaseURI($uri, ?bool $explicitReporting = null)
369
    {
370
        $this->addOption($uri, 'baseURI', $explicitReporting ?? $this->reportOnly);
2✔
371

372
        return $this;
2✔
373
    }
374

375
    /**
376
     * Adds a new valid endpoint for a form's action. Can be either
377
     * a URI class or a simple string.
378
     *
379
     * child-src lists the URLs for workers and embedded frame contents.
380
     * For example: child-src https://youtube.com would enable embedding
381
     * videos from YouTube but not from other origins.
382
     *
383
     * @see http://www.w3.org/TR/CSP/#directive-child-src
384
     *
385
     * @param array|string $uri
386
     *
387
     * @return $this
388
     */
389
    public function addChildSrc($uri, ?bool $explicitReporting = null)
390
    {
391
        $this->addOption($uri, 'childSrc', $explicitReporting ?? $this->reportOnly);
1✔
392

393
        return $this;
1✔
394
    }
395

396
    /**
397
     * Adds a new valid endpoint for a form's action. Can be either
398
     * a URI class or a simple string.
399
     *
400
     * connect-src limits the origins to which you can connect
401
     * (via XHR, WebSockets, and EventSource).
402
     *
403
     * @see http://www.w3.org/TR/CSP/#directive-connect-src
404
     *
405
     * @param array|string $uri
406
     *
407
     * @return $this
408
     */
409
    public function addConnectSrc($uri, ?bool $explicitReporting = null)
410
    {
411
        $this->addOption($uri, 'connectSrc', $explicitReporting ?? $this->reportOnly);
1✔
412

413
        return $this;
1✔
414
    }
415

416
    /**
417
     * Adds a new valid endpoint for a form's action. Can be either
418
     * a URI class or a simple string.
419
     *
420
     * default_src is the URI that is used for many of the settings when
421
     * no other source has been set.
422
     *
423
     * @see http://www.w3.org/TR/CSP/#directive-default-src
424
     *
425
     * @param array|string $uri
426
     *
427
     * @return $this
428
     */
429
    public function setDefaultSrc($uri, ?bool $explicitReporting = null)
430
    {
431
        $this->defaultSrc = [(string) $uri => $explicitReporting ?? $this->reportOnly];
1✔
432

433
        return $this;
1✔
434
    }
435

436
    /**
437
     * Adds a new valid endpoint for a form's action. Can be either
438
     * a URI class or a simple string.
439
     *
440
     * font-src specifies the origins that can serve web fonts.
441
     *
442
     * @see http://www.w3.org/TR/CSP/#directive-font-src
443
     *
444
     * @param array|string $uri
445
     *
446
     * @return $this
447
     */
448
    public function addFontSrc($uri, ?bool $explicitReporting = null)
449
    {
450
        $this->addOption($uri, 'fontSrc', $explicitReporting ?? $this->reportOnly);
1✔
451

452
        return $this;
1✔
453
    }
454

455
    /**
456
     * Adds a new valid endpoint for a form's action. Can be either
457
     * a URI class or a simple string.
458
     *
459
     * @see http://www.w3.org/TR/CSP/#directive-form-action
460
     *
461
     * @param array|string $uri
462
     *
463
     * @return $this
464
     */
465
    public function addFormAction($uri, ?bool $explicitReporting = null)
466
    {
467
        $this->addOption($uri, 'formAction', $explicitReporting ?? $this->reportOnly);
1✔
468

469
        return $this;
1✔
470
    }
471

472
    /**
473
     * Adds a new resource that should allow embedding the resource using
474
     * <frame>, <iframe>, <object>, <embed>, or <applet>
475
     *
476
     * @see http://www.w3.org/TR/CSP/#directive-frame-ancestors
477
     *
478
     * @param array|string $uri
479
     *
480
     * @return $this
481
     */
482
    public function addFrameAncestor($uri, ?bool $explicitReporting = null)
483
    {
484
        $this->addOption($uri, 'frameAncestors', $explicitReporting ?? $this->reportOnly);
1✔
485

486
        return $this;
1✔
487
    }
488

489
    /**
490
     * Adds a new valid endpoint for valid frame sources. Can be either
491
     * a URI class or a simple string.
492
     *
493
     * @see http://www.w3.org/TR/CSP/#directive-frame-src
494
     *
495
     * @param array|string $uri
496
     *
497
     * @return $this
498
     */
499
    public function addFrameSrc($uri, ?bool $explicitReporting = null)
500
    {
501
        $this->addOption($uri, 'frameSrc', $explicitReporting ?? $this->reportOnly);
1✔
502

503
        return $this;
1✔
504
    }
505

506
    /**
507
     * Adds a new valid endpoint for valid image sources. Can be either
508
     * a URI class or a simple string.
509
     *
510
     * @see http://www.w3.org/TR/CSP/#directive-img-src
511
     *
512
     * @param array|string $uri
513
     *
514
     * @return $this
515
     */
516
    public function addImageSrc($uri, ?bool $explicitReporting = null)
517
    {
518
        $this->addOption($uri, 'imageSrc', $explicitReporting ?? $this->reportOnly);
1✔
519

520
        return $this;
1✔
521
    }
522

523
    /**
524
     * Adds a new valid endpoint for valid video and audio. Can be either
525
     * a URI class or a simple string.
526
     *
527
     * @see http://www.w3.org/TR/CSP/#directive-media-src
528
     *
529
     * @param array|string $uri
530
     *
531
     * @return $this
532
     */
533
    public function addMediaSrc($uri, ?bool $explicitReporting = null)
534
    {
535
        $this->addOption($uri, 'mediaSrc', $explicitReporting ?? $this->reportOnly);
1✔
536

537
        return $this;
1✔
538
    }
539

540
    /**
541
     * Adds a new valid endpoint for manifest sources. Can be either
542
     * a URI class or simple string.
543
     *
544
     * @see https://www.w3.org/TR/CSP/#directive-manifest-src
545
     *
546
     * @param array|string $uri
547
     *
548
     * @return $this
549
     */
550
    public function addManifestSrc($uri, ?bool $explicitReporting = null)
551
    {
552
        $this->addOption($uri, 'manifestSrc', $explicitReporting ?? $this->reportOnly);
1✔
553

554
        return $this;
1✔
555
    }
556

557
    /**
558
     * Adds a new valid endpoint for Flash and other plugin sources. Can be either
559
     * a URI class or a simple string.
560
     *
561
     * @see http://www.w3.org/TR/CSP/#directive-object-src
562
     *
563
     * @param array|string $uri
564
     *
565
     * @return $this
566
     */
567
    public function addObjectSrc($uri, ?bool $explicitReporting = null)
568
    {
569
        $this->addOption($uri, 'objectSrc', $explicitReporting ?? $this->reportOnly);
1✔
570

571
        return $this;
1✔
572
    }
573

574
    /**
575
     * Limits the types of plugins that can be used. Can be either
576
     * a URI class or a simple string.
577
     *
578
     * @see http://www.w3.org/TR/CSP/#directive-plugin-types
579
     *
580
     * @param array|string $mime One or more plugin mime types, separate by spaces
581
     *
582
     * @return $this
583
     */
584
    public function addPluginType($mime, ?bool $explicitReporting = null)
585
    {
586
        $this->addOption($mime, 'pluginTypes', $explicitReporting ?? $this->reportOnly);
2✔
587

588
        return $this;
2✔
589
    }
590

591
    /**
592
     * Specifies a URL where a browser will send reports when a content
593
     * security policy is violated. Can be either a URI class or a simple string.
594
     *
595
     * @see http://www.w3.org/TR/CSP/#directive-report-uri
596
     *
597
     * @return $this
598
     */
599
    public function setReportURI(string $uri)
600
    {
601
        $this->reportURI = $uri;
2✔
602

603
        return $this;
2✔
604
    }
605

606
    /**
607
     * specifies an HTML sandbox policy that the user agent applies to
608
     * the protected resource.
609
     *
610
     * @see http://www.w3.org/TR/CSP/#directive-sandbox
611
     *
612
     * @param array|string $flags An array of sandbox flags that can be added to the directive.
613
     *
614
     * @return $this
615
     */
616
    public function addSandbox($flags, ?bool $explicitReporting = null)
617
    {
618
        $this->addOption($flags, 'sandbox', $explicitReporting ?? $this->reportOnly);
1✔
619

620
        return $this;
1✔
621
    }
622

623
    /**
624
     * Adds a new valid endpoint for javascript file sources. Can be either
625
     * a URI class or a simple string.
626
     *
627
     * @see http://www.w3.org/TR/CSP/#directive-connect-src
628
     *
629
     * @param array|string $uri
630
     *
631
     * @return $this
632
     */
633
    public function addScriptSrc($uri, ?bool $explicitReporting = null)
634
    {
635
        $this->addOption($uri, 'scriptSrc', $explicitReporting ?? $this->reportOnly);
2✔
636

637
        return $this;
2✔
638
    }
639

640
    /**
641
     * Adds a new valid endpoint for CSS file sources. Can be either
642
     * a URI class or a simple string.
643
     *
644
     * @see http://www.w3.org/TR/CSP/#directive-connect-src
645
     *
646
     * @param array|string $uri
647
     *
648
     * @return $this
649
     */
650
    public function addStyleSrc($uri, ?bool $explicitReporting = null)
651
    {
652
        $this->addOption($uri, 'styleSrc', $explicitReporting ?? $this->reportOnly);
4✔
653

654
        return $this;
4✔
655
    }
656

657
    /**
658
     * Sets whether the user agents should rewrite URL schemes, changing
659
     * HTTP to HTTPS.
660
     *
661
     * @return $this
662
     */
663
    public function upgradeInsecureRequests(bool $value = true)
664
    {
665
        $this->upgradeInsecureRequests = $value;
1✔
666

667
        return $this;
1✔
668
    }
669

670
    /**
671
     * DRY method to add an string or array to a class property.
672
     *
673
     * @param list<string>|string $options
674
     *
675
     * @return void
676
     */
677
    protected function addOption($options, string $target, ?bool $explicitReporting = null)
678
    {
679
        // Ensure we have an array to work with...
680
        if (is_string($this->{$target})) {
21✔
681
            $this->{$target} = [$this->{$target}];
5✔
682
        }
683

684
        if (is_array($options)) {
21✔
685
            foreach ($options as $opt) {
2✔
686
                $this->{$target}[$opt] = $explicitReporting ?? $this->reportOnly;
2✔
687
            }
688
        } else {
689
            $this->{$target}[$options] = $explicitReporting ?? $this->reportOnly;
19✔
690
        }
691
    }
692

693
    /**
694
     * Scans the body of the request message and replaces any nonce
695
     * placeholders with actual nonces, that we'll then add to our
696
     * headers.
697
     *
698
     * @return void
699
     */
700
    protected function generateNonces(ResponseInterface $response)
701
    {
702
        $body = $response->getBody();
34✔
703

704
        if (empty($body)) {
34✔
705
            return;
2✔
706
        }
707

708
        // Replace style and script placeholders with nonces
709
        $pattern = '/(' . preg_quote($this->styleNonceTag, '/')
32✔
710
            . '|' . preg_quote($this->scriptNonceTag, '/') . ')/';
32✔
711

712
        $body = preg_replace_callback($pattern, function ($match) {
32✔
713
            $nonce = $match[0] === $this->styleNonceTag ? $this->getStyleNonce() : $this->getScriptNonce();
4✔
714

715
            return "nonce=\"{$nonce}\"";
4✔
716
        }, $body);
32✔
717

718
        $response->setBody($body);
32✔
719
    }
720

721
    /**
722
     * Based on the current state of the elements, will add the appropriate
723
     * Content-Security-Policy and Content-Security-Policy-Report-Only headers
724
     * with their values to the response object.
725
     *
726
     * @return void
727
     */
728
    protected function buildHeaders(ResponseInterface $response)
729
    {
730
        // Ensure both headers are available and arrays...
731
        $response->setHeader('Content-Security-Policy', []);
36✔
732
        $response->setHeader('Content-Security-Policy-Report-Only', []);
36✔
733

734
        // inject default base & default URIs if needed
735
        if (empty($this->baseURI)) {
36✔
736
            $this->baseURI = 'self';
34✔
737
        }
738

739
        if (empty($this->defaultSrc)) {
36✔
740
            $this->defaultSrc = 'self';
35✔
741
        }
742

743
        foreach ($this->directives as $name => $property) {
36✔
744
            if (! empty($this->{$property})) {
36✔
745
                $this->addToHeader($name, $this->{$property});
36✔
746
            }
747
        }
748

749
        // Compile our own header strings here since if we just
750
        // append it to the response, it will be joined with
751
        // commas, not semi-colons as we need.
752
        if (! empty($this->tempHeaders)) {
36✔
753
            $header = '';
34✔
754

755
            foreach ($this->tempHeaders as $name => $value) {
34✔
756
                $header .= " {$name} {$value};";
34✔
757
            }
758

759
            // add token only if needed
760
            if ($this->upgradeInsecureRequests) {
34✔
761
                $header .= ' upgrade-insecure-requests;';
1✔
762
            }
763

764
            $response->appendHeader('Content-Security-Policy', $header);
34✔
765
        }
766

767
        if (! empty($this->reportOnlyHeaders)) {
36✔
768
            $header = '';
13✔
769

770
            foreach ($this->reportOnlyHeaders as $name => $value) {
13✔
771
                $header .= " {$name} {$value};";
13✔
772
            }
773

774
            $response->appendHeader('Content-Security-Policy-Report-Only', $header);
13✔
775
        }
776

777
        $this->tempHeaders       = [];
36✔
778
        $this->reportOnlyHeaders = [];
36✔
779
    }
780

781
    /**
782
     * Adds a directive and it's options to the appropriate header. The $values
783
     * array might have options that are geared toward either the regular or the
784
     * reportOnly header, since it's viable to have both simultaneously.
785
     *
786
     * @param array|string|null $values
787
     *
788
     * @return void
789
     */
790
    protected function addToHeader(string $name, $values = null)
791
    {
792
        if (is_string($values)) {
36✔
793
            $values = [$values => $this->reportOnly];
36✔
794
        }
795

796
        $sources       = [];
36✔
797
        $reportSources = [];
36✔
798

799
        foreach ($values as $value => $reportOnly) {
36✔
800
            if (is_numeric($value) && is_string($reportOnly) && ($reportOnly !== '')) {
36✔
801
                $value      = $reportOnly;
36✔
802
                $reportOnly = $this->reportOnly;
36✔
803
            }
804

805
            if (str_starts_with($value, 'nonce-')) {
36✔
806
                $value = "'{$value}'";
5✔
807
            }
808

809
            if ($reportOnly === true) {
36✔
810
                $reportSources[] = in_array($value, $this->validSources, true) ? "'{$value}'" : $value;
13✔
811
            } else {
812
                $sources[] = in_array($value, $this->validSources, true) ? "'{$value}'" : $value;
34✔
813
            }
814
        }
815

816
        if ($sources !== []) {
36✔
817
            $this->tempHeaders[$name] = implode(' ', $sources);
34✔
818
        }
819

820
        if ($reportSources !== []) {
36✔
821
            $this->reportOnlyHeaders[$name] = implode(' ', $reportSources);
13✔
822
        }
823
    }
824

825
    /**
826
     * Clear the directive.
827
     *
828
     * @param string $directive CSP directive
829
     */
830
    public function clearDirective(string $directive): void
831
    {
832
        if ($directive === 'report-uris') {
1✔
NEW
833
            $this->{$this->directives[$directive]} = null;
×
834

NEW
835
            return;
×
836
        }
837

838
        $this->{$this->directives[$directive]} = [];
1✔
839
    }
840
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc