1313
1414namespace CodeIgniter \HTTP ;
1515
16+ use InvalidArgumentException ;
1617use Stringable ;
1718
1819/**
@@ -54,7 +55,7 @@ class Header implements Stringable
5455 */
5556 public function __construct (string $ name , $ value = null )
5657 {
57- $ this ->name = $ name ;
58+ $ this ->setName ( $ name) ;
5859 $ this ->setValue ($ value );
5960 }
6061
@@ -81,9 +82,12 @@ public function getValue()
8182 * Sets the name of the header, overwriting any previous value.
8283 *
8384 * @return $this
85+ *
86+ * @throws InvalidArgumentException
8487 */
8588 public function setName (string $ name )
8689 {
90+ $ this ->validateName ($ name );
8791 $ this ->name = $ name ;
8892
8993 return $ this ;
@@ -95,10 +99,16 @@ public function setName(string $name)
9599 * @param array<int|string, array<string, string>|string>|string|null $value
96100 *
97101 * @return $this
102+ *
103+ * @throws InvalidArgumentException
98104 */
99105 public function setValue ($ value = null )
100106 {
101- $ this ->value = is_array ($ value ) ? $ value : (string ) $ value ;
107+ $ value = is_array ($ value ) ? $ value : (string ) $ value ;
108+
109+ $ this ->validateValue ($ value );
110+
111+ $ this ->value = $ value ;
102112
103113 return $ this ;
104114 }
@@ -110,13 +120,17 @@ public function setValue($value = null)
110120 * @param array<string, string>|string|null $value
111121 *
112122 * @return $this
123+ *
124+ * @throws InvalidArgumentException
113125 */
114126 public function appendValue ($ value = null )
115127 {
116128 if ($ value === null ) {
117129 return $ this ;
118130 }
119131
132+ $ this ->validateValue ($ value );
133+
120134 if (! is_array ($ this ->value )) {
121135 $ this ->value = [$ this ->value ];
122136 }
@@ -135,13 +149,17 @@ public function appendValue($value = null)
135149 * @param array<string, string>|string|null $value
136150 *
137151 * @return $this
152+ *
153+ * @throws InvalidArgumentException
138154 */
139155 public function prependValue ($ value = null )
140156 {
141157 if ($ value === null ) {
142158 return $ this ;
143159 }
144160
161+ $ this ->validateValue ($ value );
162+
145163 if (! is_array ($ this ->value )) {
146164 $ this ->value = [$ this ->value ];
147165 }
@@ -193,4 +211,54 @@ public function __toString(): string
193211 {
194212 return $ this ->name . ': ' . $ this ->getValueLine ();
195213 }
214+
215+ /**
216+ * Validate header name.
217+ *
218+ * Regex is based on code from a guzzlehttp/psr7 library.
219+ *
220+ * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
221+ *
222+ * @throws InvalidArgumentException
223+ */
224+ private function validateName (string $ name ): void
225+ {
226+ if (preg_match ('/^[a-zA-Z0-9 \'`#$%&*+.^_|~!-]+$/D ' , $ name ) !== 1 ) {
227+ throw new InvalidArgumentException ('The header name is not valid as per RFC 7230. ' );
228+ }
229+ }
230+
231+ /**
232+ * Validate header value.
233+ *
234+ * Regex is based on code from a guzzlehttp/psr7 library.
235+ *
236+ * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
237+ *
238+ * @param array<int|string, array<string, string>|string>|int|string $value
239+ *
240+ * @throws InvalidArgumentException
241+ */
242+ private function validateValue (array |int |string $ value ): void
243+ {
244+ if (is_int ($ value )) {
245+ return ;
246+ }
247+
248+ if (is_array ($ value )) {
249+ foreach ($ value as $ key => $ val ) {
250+ $ this ->validateValue ($ key );
251+ $ this ->validateValue ($ val );
252+ }
253+
254+ return ;
255+ }
256+
257+ // The regular expression excludes obs-fold per RFC 7230#3.2.4, as sending folded lines
258+ // is deprecated and rare. This obscure HTTP/1.1 feature is unlikely to impact legitimate
259+ // use cases. Libraries like Guzzle and AMPHP follow the same principle.
260+ if (preg_match ('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D ' , $ value ) !== 1 ) {
261+ throw new InvalidArgumentException ('The header value is not valid as per RFC 7230. ' );
262+ }
263+ }
196264}
0 commit comments