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

codeigniter4 / CodeIgniter4 / 21545744984

31 Jan 2026 02:14PM UTC coverage: 85.383% (+0.001%) from 85.382%
21545744984

Pull #9722

github

web-flow
Merge 03d9c5fc1 into 1b377d163
Pull Request #9722: feat: Add script-src-elem option to CSP options.

2 of 2 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

22121 of 25908 relevant lines covered (85.38%)

205.27 hits per line

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

98.35
/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> [name => property]
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
        'script-src-elem' => 'scriptSrcElem',
51
        'style-src'       => 'styleSrc',
52
        'manifest-src'    => 'manifestSrc',
53
        'sandbox'         => 'sandbox',
54
        'report-uri'      => 'reportURI',
55
    ];
56

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

169
    /**
170
     * Used for security enforcement
171
     *
172
     * @var array|string
173
     */
174
    protected $sandbox = [];
175

176
    /**
177
     * A set of endpoints to which csp violation reports will be sent when
178
     * particular behaviors are prevented.
179
     *
180
     * @var string|null
181
     */
182
    protected $reportURI;
183

184
    /**
185
     * Used for security enforcement
186
     *
187
     * @var bool
188
     */
189
    protected $upgradeInsecureRequests = false;
190

191
    /**
192
     * Used for security enforcement
193
     *
194
     * @var bool
195
     */
196
    protected $reportOnly = false;
197

198
    /**
199
     * Used for security enforcement
200
     *
201
     * @var list<string>
202
     */
203
    protected $validSources = [
204
        'self',
205
        'none',
206
        'unsafe-inline',
207
        'unsafe-eval',
208
    ];
209

210
    /**
211
     * Used for security enforcement
212
     *
213
     * @var array
214
     */
215
    protected $nonces = [];
216

217
    /**
218
     * Nonce for style
219
     *
220
     * @var string
221
     */
222
    protected $styleNonce;
223

224
    /**
225
     * Nonce for script
226
     *
227
     * @var string
228
     */
229
    protected $scriptNonce;
230

231
    /**
232
     * Nonce tag for style
233
     *
234
     * @var string
235
     */
236
    protected $styleNonceTag = '{csp-style-nonce}';
237

238
    /**
239
     * Nonce tag for script
240
     *
241
     * @var string
242
     */
243
    protected $scriptNonceTag = '{csp-script-nonce}';
244

245
    /**
246
     * Replace nonce tag automatically
247
     *
248
     * @var bool
249
     */
250
    protected $autoNonce = true;
251

252
    /**
253
     * An array of header info since we have
254
     * to build ourselves before passing to Response.
255
     *
256
     * @var array
257
     */
258
    protected $tempHeaders = [];
259

260
    /**
261
     * An array of header info to build
262
     * that should only be reported.
263
     *
264
     * @var array
265
     */
266
    protected $reportOnlyHeaders = [];
267

268
    /**
269
     * Whether Content Security Policy is being enforced.
270
     *
271
     * @var bool
272
     */
273
    protected $CSPEnabled = false;
274

275
    /**
276
     * Constructor.
277
     *
278
     * Stores our default values from the Config file.
279
     */
280
    public function __construct(ContentSecurityPolicyConfig $config)
281
    {
282
        $appConfig        = config(App::class);
612✔
283
        $this->CSPEnabled = $appConfig->CSPEnabled;
612✔
284

285
        foreach (get_object_vars($config) as $setting => $value) {
612✔
286
            if (property_exists($this, $setting)) {
612✔
287
                $this->{$setting} = $value;
612✔
288
            }
289
        }
290

291
        if (! is_array($this->styleSrc)) {
612✔
292
            $this->styleSrc = [$this->styleSrc];
612✔
293
        }
294

295
        if (! is_array($this->scriptSrc)) {
612✔
296
            $this->scriptSrc = [$this->scriptSrc];
612✔
297
        }
298
    }
299

300
    /**
301
     * Whether Content Security Policy is being enforced.
302
     */
303
    public function enabled(): bool
304
    {
305
        return $this->CSPEnabled;
130✔
306
    }
307

308
    /**
309
     * Get the nonce for the style tag.
310
     */
311
    public function getStyleNonce(): string
312
    {
313
        if ($this->styleNonce === null) {
8✔
314
            $this->styleNonce = bin2hex(random_bytes(12));
8✔
315
            $this->styleSrc[] = 'nonce-' . $this->styleNonce;
8✔
316
        }
317

318
        return $this->styleNonce;
8✔
319
    }
320

321
    /**
322
     * Get the nonce for the script tag.
323
     */
324
    public function getScriptNonce(): string
325
    {
326
        if ($this->scriptNonce === null) {
10✔
327
            $this->scriptNonce = bin2hex(random_bytes(12));
10✔
328
            $this->scriptSrc[] = 'nonce-' . $this->scriptNonce;
10✔
329
        }
330

331
        return $this->scriptNonce;
10✔
332
    }
333

334
    /**
335
     * Compiles and sets the appropriate headers in the request.
336
     *
337
     * Should be called just prior to sending the response to the user agent.
338
     *
339
     * @return void
340
     */
341
    public function finalize(ResponseInterface $response)
342
    {
343
        if ($this->autoNonce) {
38✔
344
            $this->generateNonces($response);
36✔
345
        }
346

347
        $this->buildHeaders($response);
38✔
348
    }
349

350
    /**
351
     * If TRUE, nothing will be restricted. Instead all violations will
352
     * be reported to the reportURI for monitoring. This is useful when
353
     * you are just starting to implement the policy, and will help
354
     * determine what errors need to be addressed before you turn on
355
     * all filtering.
356
     *
357
     * @return $this
358
     */
359
    public function reportOnly(bool $value = true)
360
    {
361
        $this->reportOnly = $value;
8✔
362

363
        return $this;
8✔
364
    }
365

366
    /**
367
     * Adds a new baseURI value. Can be either a URI class or a simple string.
368
     *
369
     * baseURI restricts the URLs that can appear in a page's <base> element.
370
     *
371
     * @see http://www.w3.org/TR/CSP/#directive-base-uri
372
     *
373
     * @param array|string $uri
374
     *
375
     * @return $this
376
     */
377
    public function addBaseURI($uri, ?bool $explicitReporting = null)
378
    {
379
        $this->addOption($uri, 'baseURI', $explicitReporting ?? $this->reportOnly);
2✔
380

381
        return $this;
2✔
382
    }
383

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

402
        return $this;
1✔
403
    }
404

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

422
        return $this;
1✔
423
    }
424

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

442
        return $this;
1✔
443
    }
444

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

461
        return $this;
1✔
462
    }
463

464
    /**
465
     * Adds a new valid endpoint for a form's action. Can be either
466
     * a URI class or a simple string.
467
     *
468
     * @see http://www.w3.org/TR/CSP/#directive-form-action
469
     *
470
     * @param array|string $uri
471
     *
472
     * @return $this
473
     */
474
    public function addFormAction($uri, ?bool $explicitReporting = null)
475
    {
476
        $this->addOption($uri, 'formAction', $explicitReporting ?? $this->reportOnly);
1✔
477

478
        return $this;
1✔
479
    }
480

481
    /**
482
     * Adds a new resource that should allow embedding the resource using
483
     * <frame>, <iframe>, <object>, <embed>, or <applet>
484
     *
485
     * @see http://www.w3.org/TR/CSP/#directive-frame-ancestors
486
     *
487
     * @param array|string $uri
488
     *
489
     * @return $this
490
     */
491
    public function addFrameAncestor($uri, ?bool $explicitReporting = null)
492
    {
493
        $this->addOption($uri, 'frameAncestors', $explicitReporting ?? $this->reportOnly);
1✔
494

495
        return $this;
1✔
496
    }
497

498
    /**
499
     * Adds a new valid endpoint for valid frame sources. Can be either
500
     * a URI class or a simple string.
501
     *
502
     * @see http://www.w3.org/TR/CSP/#directive-frame-src
503
     *
504
     * @param array|string $uri
505
     *
506
     * @return $this
507
     */
508
    public function addFrameSrc($uri, ?bool $explicitReporting = null)
509
    {
510
        $this->addOption($uri, 'frameSrc', $explicitReporting ?? $this->reportOnly);
1✔
511

512
        return $this;
1✔
513
    }
514

515
    /**
516
     * Adds a new valid endpoint for valid image sources. Can be either
517
     * a URI class or a simple string.
518
     *
519
     * @see http://www.w3.org/TR/CSP/#directive-img-src
520
     *
521
     * @param array|string $uri
522
     *
523
     * @return $this
524
     */
525
    public function addImageSrc($uri, ?bool $explicitReporting = null)
526
    {
527
        $this->addOption($uri, 'imageSrc', $explicitReporting ?? $this->reportOnly);
4✔
528

529
        return $this;
4✔
530
    }
531

532
    /**
533
     * Adds a new valid endpoint for valid video and audio. Can be either
534
     * a URI class or a simple string.
535
     *
536
     * @see http://www.w3.org/TR/CSP/#directive-media-src
537
     *
538
     * @param array|string $uri
539
     *
540
     * @return $this
541
     */
542
    public function addMediaSrc($uri, ?bool $explicitReporting = null)
543
    {
544
        $this->addOption($uri, 'mediaSrc', $explicitReporting ?? $this->reportOnly);
1✔
545

546
        return $this;
1✔
547
    }
548

549
    /**
550
     * Adds a new valid endpoint for manifest sources. Can be either
551
     * a URI class or simple string.
552
     *
553
     * @see https://www.w3.org/TR/CSP/#directive-manifest-src
554
     *
555
     * @param array|string $uri
556
     *
557
     * @return $this
558
     */
559
    public function addManifestSrc($uri, ?bool $explicitReporting = null)
560
    {
561
        $this->addOption($uri, 'manifestSrc', $explicitReporting ?? $this->reportOnly);
1✔
562

563
        return $this;
1✔
564
    }
565

566
    /**
567
     * Adds a new valid endpoint for Flash and other plugin sources. Can be either
568
     * a URI class or a simple string.
569
     *
570
     * @see http://www.w3.org/TR/CSP/#directive-object-src
571
     *
572
     * @param array|string $uri
573
     *
574
     * @return $this
575
     */
576
    public function addObjectSrc($uri, ?bool $explicitReporting = null)
577
    {
578
        $this->addOption($uri, 'objectSrc', $explicitReporting ?? $this->reportOnly);
1✔
579

580
        return $this;
1✔
581
    }
582

583
    /**
584
     * Limits the types of plugins that can be used. Can be either
585
     * a URI class or a simple string.
586
     *
587
     * @see http://www.w3.org/TR/CSP/#directive-plugin-types
588
     *
589
     * @param array|string $mime One or more plugin mime types, separate by spaces
590
     *
591
     * @return $this
592
     */
593
    public function addPluginType($mime, ?bool $explicitReporting = null)
594
    {
595
        $this->addOption($mime, 'pluginTypes', $explicitReporting ?? $this->reportOnly);
2✔
596

597
        return $this;
2✔
598
    }
599

600
    /**
601
     * Specifies a URL where a browser will send reports when a content
602
     * security policy is violated. Can be either a URI class or a simple string.
603
     *
604
     * @see http://www.w3.org/TR/CSP/#directive-report-uri
605
     *
606
     * @param string $uri URL to send reports. Set `''` if you want to remove
607
     *                    this directive at runtime.
608
     *
609
     * @return $this
610
     */
611
    public function setReportURI(string $uri)
612
    {
613
        $this->reportURI = $uri;
3✔
614

615
        return $this;
3✔
616
    }
617

618
    /**
619
     * specifies an HTML sandbox policy that the user agent applies to
620
     * the protected resource.
621
     *
622
     * @see http://www.w3.org/TR/CSP/#directive-sandbox
623
     *
624
     * @param array|string $flags An array of sandbox flags that can be added to the directive.
625
     *
626
     * @return $this
627
     */
628
    public function addSandbox($flags, ?bool $explicitReporting = null)
629
    {
630
        $this->addOption($flags, 'sandbox', $explicitReporting ?? $this->reportOnly);
1✔
631

632
        return $this;
1✔
633
    }
634

635
    /**
636
     * Adds a new valid endpoint for javascript file sources. Can be either
637
     * a URI class or a simple string.
638
     *
639
     * @see http://www.w3.org/TR/CSP/#directive-connect-src
640
     *
641
     * @param array|string $uri
642
     *
643
     * @return $this
644
     */
645
    public function addScriptSrc($uri, ?bool $explicitReporting = null)
646
    {
647
        $this->addOption($uri, 'scriptSrc', $explicitReporting ?? $this->reportOnly);
2✔
648

649
        return $this;
2✔
650
    }
651

652
    /**
653
     * Adds a new valid endpoint for javascript file sources. Can be either
654
     * a URI class or a simple string.
655
     *
656
     * @see https://www.w3.org/TR/CSP/#directive-script-src-elem
657
     *
658
     * @param array|string $uri
659
     *
660
     * @return $this
661
     */
662
    public function addScriptSrcElem($uri, ?bool $explicitReporting = null)
663
    {
664
        $this->addOption($uri, 'scriptSrcElem', $explicitReporting ?? $this->reportOnly);
1✔
665

666
        return $this;
1✔
667
    }
668

669
    /**
670
     * Adds a new valid endpoint for CSS file sources. Can be either
671
     * a URI class or a simple string.
672
     *
673
     * @see http://www.w3.org/TR/CSP/#directive-connect-src
674
     *
675
     * @param array|string $uri
676
     *
677
     * @return $this
678
     */
679
    public function addStyleSrc($uri, ?bool $explicitReporting = null)
680
    {
681
        $this->addOption($uri, 'styleSrc', $explicitReporting ?? $this->reportOnly);
4✔
682

683
        return $this;
4✔
684
    }
685

686
    /**
687
     * Sets whether the user agents should rewrite URL schemes, changing
688
     * HTTP to HTTPS.
689
     *
690
     * @return $this
691
     */
692
    public function upgradeInsecureRequests(bool $value = true)
693
    {
694
        $this->upgradeInsecureRequests = $value;
1✔
695

696
        return $this;
1✔
697
    }
698

699
    /**
700
     * DRY method to add an string or array to a class property.
701
     *
702
     * @param list<string>|string $options
703
     *
704
     * @return void
705
     */
706
    protected function addOption($options, string $target, ?bool $explicitReporting = null)
707
    {
708
        // Ensure we have an array to work with...
709
        if (is_string($this->{$target})) {
25✔
710
            $this->{$target} = [$this->{$target}];
9✔
711
        }
712

713
        if (is_array($options)) {
25✔
714
            foreach ($options as $opt) {
2✔
715
                $this->{$target}[$opt] = $explicitReporting ?? $this->reportOnly;
2✔
716
            }
717
        } else {
718
            $this->{$target}[$options] = $explicitReporting ?? $this->reportOnly;
23✔
719
        }
720
    }
721

722
    /**
723
     * Scans the body of the request message and replaces any nonce
724
     * placeholders with actual nonces, that we'll then add to our
725
     * headers.
726
     *
727
     * @return void
728
     */
729
    protected function generateNonces(ResponseInterface $response)
730
    {
731
        $body = $response->getBody();
36✔
732

733
        if (empty($body)) {
36✔
734
            return;
2✔
735
        }
736

737
        // Replace style and script placeholders with nonces
738
        $pattern = '/(' . preg_quote($this->styleNonceTag, '/')
34✔
739
            . '|' . preg_quote($this->scriptNonceTag, '/') . ')/';
34✔
740

741
        $body = preg_replace_callback($pattern, function ($match): string {
34✔
742
            $nonce = $match[0] === $this->styleNonceTag ? $this->getStyleNonce() : $this->getScriptNonce();
4✔
743

744
            return "nonce=\"{$nonce}\"";
4✔
745
        }, $body);
34✔
746

747
        $response->setBody($body);
34✔
748
    }
749

750
    /**
751
     * Based on the current state of the elements, will add the appropriate
752
     * Content-Security-Policy and Content-Security-Policy-Report-Only headers
753
     * with their values to the response object.
754
     *
755
     * @return void
756
     */
757
    protected function buildHeaders(ResponseInterface $response)
758
    {
759
        // Ensure both headers are available and arrays...
760
        $response->setHeader('Content-Security-Policy', []);
38✔
761
        $response->setHeader('Content-Security-Policy-Report-Only', []);
38✔
762

763
        // inject default base & default URIs if needed
764
        if (empty($this->baseURI)) {
38✔
765
            $this->baseURI = 'self';
36✔
766
        }
767

768
        if (empty($this->defaultSrc)) {
38✔
769
            $this->defaultSrc = 'self';
37✔
770
        }
771

772
        foreach ($this->directives as $name => $property) {
38✔
773
            if (! empty($this->{$property})) {
38✔
774
                $this->addToHeader($name, $this->{$property});
38✔
775
            }
776
        }
777

778
        // Compile our own header strings here since if we just
779
        // append it to the response, it will be joined with
780
        // commas, not semi-colons as we need.
781
        if (! empty($this->tempHeaders)) {
38✔
782
            $header = '';
36✔
783

784
            foreach ($this->tempHeaders as $name => $value) {
36✔
785
                $header .= " {$name} {$value};";
36✔
786
            }
787

788
            // add token only if needed
789
            if ($this->upgradeInsecureRequests) {
36✔
790
                $header .= ' upgrade-insecure-requests;';
1✔
791
            }
792

793
            $response->appendHeader('Content-Security-Policy', $header);
36✔
794
        }
795

796
        if (! empty($this->reportOnlyHeaders)) {
38✔
797
            $header = '';
14✔
798

799
            foreach ($this->reportOnlyHeaders as $name => $value) {
14✔
800
                $header .= " {$name} {$value};";
14✔
801
            }
802

803
            $response->appendHeader('Content-Security-Policy-Report-Only', $header);
14✔
804
        }
805

806
        $this->tempHeaders       = [];
38✔
807
        $this->reportOnlyHeaders = [];
38✔
808
    }
809

810
    /**
811
     * Adds a directive and it's options to the appropriate header. The $values
812
     * array might have options that are geared toward either the regular or the
813
     * reportOnly header, since it's viable to have both simultaneously.
814
     *
815
     * @param array|string|null $values
816
     *
817
     * @return void
818
     */
819
    protected function addToHeader(string $name, $values = null)
820
    {
821
        if (is_string($values)) {
38✔
822
            $values = [$values => $this->reportOnly];
38✔
823
        }
824

825
        $sources       = [];
38✔
826
        $reportSources = [];
38✔
827

828
        foreach ($values as $value => $reportOnly) {
38✔
829
            if (is_numeric($value) && is_string($reportOnly) && ($reportOnly !== '')) {
38✔
830
                $value      = $reportOnly;
38✔
831
                $reportOnly = $this->reportOnly;
38✔
832
            }
833

834
            if (str_starts_with($value, 'nonce-')) {
38✔
835
                $value = "'{$value}'";
5✔
836
            }
837

838
            if ($reportOnly === true) {
38✔
839
                $reportSources[] = in_array($value, $this->validSources, true) ? "'{$value}'" : $value;
14✔
840
            } else {
841
                $sources[] = in_array($value, $this->validSources, true) ? "'{$value}'" : $value;
36✔
842
            }
843
        }
844

845
        if ($sources !== []) {
38✔
846
            $this->tempHeaders[$name] = implode(' ', $sources);
36✔
847
        }
848

849
        if ($reportSources !== []) {
38✔
850
            $this->reportOnlyHeaders[$name] = implode(' ', $reportSources);
14✔
851
        }
852
    }
853

854
    /**
855
     * Clear the directive.
856
     *
857
     * @param string $directive CSP directive
858
     */
859
    public function clearDirective(string $directive): void
860
    {
861
        if ($directive === 'report-uris') {
1✔
UNCOV
862
            $this->{$this->directives[$directive]} = null;
×
863

UNCOV
864
            return;
×
865
        }
866

867
        $this->{$this->directives[$directive]} = [];
1✔
868
    }
869
}
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