| 81 | /** |
| 82 | * @ticket 38231 |
| 83 | * @dataProvider data_download_url_should_respect_filename_from_content_disposition_header |
| 84 | * |
| 85 | * @covers ::download_url |
| 86 | * |
| 87 | * @param $filter A callback containing a fake Content-Disposition header. |
| 88 | */ |
| 89 | public function test_download_url_should_respect_filename_from_content_disposition_header( $filter ) { |
| 90 | add_filter( 'pre_http_request', array( $this, $filter ), 10, 3 ); |
| 91 | |
| 92 | $filename = download_url( 'url_with_content_disposition_header' ); |
| 93 | $this->assertStringContainsString( 'filename-from-content-disposition-header', $filename ); |
| 94 | $this->assertFileExists( $filename ); |
| 95 | $this->unlink( $filename ); |
| 96 | |
| 97 | remove_filter( 'pre_http_request', array( $this, $filter ) ); |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Data provider for test_download_url_should_respect_filename_from_content_disposition_header. |
| 102 | * |
| 103 | * @return array |
| 104 | */ |
| 105 | public function data_download_url_should_respect_filename_from_content_disposition_header() { |
| 106 | return array( |
| 107 | 'valid parameters' => array( 'filter_content_disposition_header_with_filename' ), |
| 108 | 'path traversal' => array( 'filter_content_disposition_header_with_filename_with_path_traversal' ), |
| 109 | 'no quotes' => array( 'filter_content_disposition_header_with_filename_without_quotes' ), |
| 110 | ); |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * Filter callback for data_download_url_should_respect_filename_from_content_disposition_header. |
| 115 | * |
| 116 | * @since 5.9.0 |
| 117 | * |
| 118 | * @return array |
| 119 | */ |
| 120 | public function filter_content_disposition_header_with_filename( $response, $args, $url ) { |
| 121 | return array( |
| 122 | 'response' => array( |
| 123 | 'code' => 200, |
| 124 | ), |
| 125 | 'headers' => array( |
| 126 | 'content-disposition' => 'attachment; filename="filename-from-content-disposition-header.txt"', |
| 127 | ), |
| 128 | ); |
| 129 | } |
| 130 | |
| 131 | /** |
| 132 | * Filter callback for data_download_url_should_respect_filename_from_content_disposition_header. |
| 133 | * |
| 134 | * @since 5.9.0 |
| 135 | * |
| 136 | * @return array |
| 137 | */ |
| 138 | public function filter_content_disposition_header_with_filename_with_path_traversal( $response, $args, $url ) { |
| 139 | return array( |
| 140 | 'response' => array( |
| 141 | 'code' => 200, |
| 142 | ), |
| 143 | 'headers' => array( |
| 144 | 'content-disposition' => 'attachment; filename="../../filename-from-content-disposition-header.txt"', |
| 145 | ), |
| 146 | ); |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Filter callback for data_download_url_should_respect_filename_from_content_disposition_header. |
| 151 | * |
| 152 | * @since 5.9.0 |
| 153 | * |
| 154 | * @return array |
| 155 | */ |
| 156 | public function filter_content_disposition_header_with_filename_without_quotes( $response, $args, $url ) { |
| 157 | return array( |
| 158 | 'response' => array( |
| 159 | 'code' => 200, |
| 160 | ), |
| 161 | 'headers' => array( |
| 162 | 'content-disposition' => 'attachment; filename=filename-from-content-disposition-header.txt', |
| 163 | ), |
| 164 | ); |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * @ticket 38231 |
| 169 | * @dataProvider data_download_url_should_reject_filename_from_invalid_content_disposition_header |
| 170 | * |
| 171 | * @covers ::download_url |
| 172 | * |
| 173 | * @param $filter A callback containing a fake Content-Disposition header. |
| 174 | */ |
| 175 | public function test_download_url_should_reject_filename_from_invalid_content_disposition_header( $filter ) { |
| 176 | add_filter( 'pre_http_request', array( $this, $filter ), 10, 3 ); |
| 177 | |
| 178 | $filename = download_url( 'url_with_content_disposition_header' ); |
| 179 | $this->assertStringContainsString( 'url_with_content_disposition_header', $filename ); |
| 180 | $this->unlink( $filename ); |
| 181 | |
| 182 | remove_filter( 'pre_http_request', array( $this, $filter ) ); |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * Data provider for test_download_url_should_reject_filename_from_invalid_content_disposition_header. |
| 187 | * |
| 188 | * @return array |
| 189 | */ |
| 190 | public function data_download_url_should_reject_filename_from_invalid_content_disposition_header() { |
| 191 | return array( |
| 192 | 'no context' => array( 'filter_content_disposition_header_with_filename_without_context' ), |
| 193 | 'inline context' => array( 'filter_content_disposition_header_with_filename_with_inline_context' ), |
| 194 | 'form-data context' => array( 'filter_content_disposition_header_with_filename_with_form_data_context' ), |
| 195 | ); |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Filter callback for data_download_url_should_reject_filename_from_invalid_content_disposition_header. |
| 200 | * |
| 201 | * @since 5.9.0 |
| 202 | * |
| 203 | * @return array |
| 204 | */ |
| 205 | public function filter_content_disposition_header_with_filename_without_context( $response, $args, $url ) { |
| 206 | return array( |
| 207 | 'response' => array( |
| 208 | 'code' => 200, |
| 209 | ), |
| 210 | 'headers' => array( |
| 211 | 'content-disposition' => 'filename="filename-from-content-disposition-header.txt"', |
| 212 | ), |
| 213 | ); |
| 214 | } |
| 215 | |
| 216 | /** |
| 217 | * Filter callback for data_download_url_should_reject_filename_from_invalid_content_disposition_header. |
| 218 | * |
| 219 | * @since 5.9.0 |
| 220 | * |
| 221 | * @return array |
| 222 | */ |
| 223 | public function filter_content_disposition_header_with_filename_with_inline_context( $response, $args, $url ) { |
| 224 | return array( |
| 225 | 'response' => array( |
| 226 | 'code' => 200, |
| 227 | ), |
| 228 | 'headers' => array( |
| 229 | 'content-disposition' => 'inline; filename="filename-from-content-disposition-header.txt"', |
| 230 | ), |
| 231 | ); |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * Filter callback for data_download_url_should_reject_filename_from_invalid_content_disposition_header. |
| 236 | * |
| 237 | * @since 5.9.0 |
| 238 | * |
| 239 | * @return array |
| 240 | */ |
| 241 | public function filter_content_disposition_header_with_filename_with_form_data_context( $response, $args, $url ) { |
| 242 | return array( |
| 243 | 'response' => array( |
| 244 | 'code' => 200, |
| 245 | ), |
| 246 | 'headers' => array( |
| 247 | 'content-disposition' => 'form-data; name="file"; filename="filename-from-content-disposition-header.txt"', |
| 248 | ), |
| 249 | ); |
| 250 | } |
| 251 | |