AyeCode /
invoicing
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php |
||||
| 2 | /** |
||||
| 3 | * Contains the class that downloads a single report. |
||||
| 4 | * |
||||
| 5 | * |
||||
| 6 | */ |
||||
| 7 | |||||
| 8 | defined( 'ABSPATH' ) || exit; |
||||
| 9 | |||||
| 10 | /** |
||||
| 11 | * GetPaid_Graph_Downloader Class. |
||||
| 12 | */ |
||||
| 13 | class GetPaid_Graph_Downloader { |
||||
| 14 | |||||
| 15 | /** |
||||
| 16 | * @var GetPaid_Reports_Report |
||||
| 17 | */ |
||||
| 18 | public $handler; |
||||
| 19 | |||||
| 20 | /** |
||||
| 21 | * Class constructor. |
||||
| 22 | * |
||||
| 23 | */ |
||||
| 24 | public function __construct() { |
||||
| 25 | $this->handler = new GetPaid_Reports_Report(); |
||||
| 26 | } |
||||
| 27 | |||||
| 28 | /** |
||||
| 29 | * Prepares the datastore handler. |
||||
| 30 | * |
||||
| 31 | * @return GetPaid_Reports_Report_Items|GetPaid_Reports_Report_Gateways|GetPaid_Reports_Report_Discounts |
||||
| 32 | */ |
||||
| 33 | public function prepare_handler( $graph ) { |
||||
| 34 | |||||
| 35 | if ( empty( $this->handler->views[ $graph ] ) ) { |
||||
| 36 | wp_die( esc_html__( 'Invalid Graph', 'invoicing' ), 400 ); |
||||
| 37 | } |
||||
| 38 | |||||
| 39 | return new $this->handler->views[ $graph ]['class'](); |
||||
| 40 | |||||
| 41 | } |
||||
| 42 | |||||
| 43 | /** |
||||
| 44 | * Prepares the output stream. |
||||
| 45 | * |
||||
| 46 | * @return resource |
||||
| 47 | */ |
||||
| 48 | public function prepare_output() { |
||||
| 49 | |||||
| 50 | $output = fopen( 'php://output', 'w' ); |
||||
| 51 | |||||
| 52 | if ( false === $output ) { |
||||
| 53 | wp_die( esc_html__( 'Unsupported server', 'invoicing' ), 500 ); |
||||
| 54 | } |
||||
| 55 | |||||
| 56 | return $output; |
||||
| 57 | } |
||||
| 58 | |||||
| 59 | /** |
||||
| 60 | * Prepares the file type. |
||||
| 61 | * |
||||
| 62 | * @return string |
||||
| 63 | */ |
||||
| 64 | public function prepare_file_type( $graph ) { |
||||
| 65 | |||||
| 66 | $file_type = empty( $_REQUEST['file_type'] ) ? 'csv' : sanitize_text_field( $_REQUEST['file_type'] ); |
||||
| 67 | $file_name = wpinv_sanitize_key( "getpaid-$graph-" . current_time( 'Y-m-d' ) ); |
||||
| 68 | |||||
| 69 | header( "Content-Type:application/$file_type" ); |
||||
| 70 | header( "Content-Disposition:attachment;filename=$file_name.$file_type" ); |
||||
| 71 | |||||
| 72 | return $file_type; |
||||
| 73 | } |
||||
| 74 | |||||
| 75 | /** |
||||
| 76 | * Handles the actual download. |
||||
| 77 | * |
||||
| 78 | */ |
||||
| 79 | public function download( $graph ) { |
||||
| 80 | global $wpdb; |
||||
| 81 | |||||
| 82 | $handler = $this->prepare_handler( $graph ); |
||||
| 83 | $stream = $this->prepare_output(); |
||||
| 84 | $stats = $wpdb->get_results( $handler->get_sql( $handler->get_range() ) ); |
||||
| 85 | $headers = array( $handler->field, 'total', 'total_raw' ); |
||||
| 86 | $file_type = $this->prepare_file_type( $graph ); |
||||
| 87 | |||||
| 88 | if ( 'csv' == $file_type ) { |
||||
| 89 | $this->download_csv( $stats, $stream, $headers ); |
||||
| 90 | } elseif ( 'xml' == $file_type ) { |
||||
| 91 | $this->download_xml( $stats, $stream, $headers ); |
||||
| 92 | } else { |
||||
| 93 | $this->download_json( $stats, $stream, $headers ); |
||||
| 94 | } |
||||
| 95 | |||||
| 96 | fclose( $stream ); |
||||
| 97 | exit; |
||||
|
0 ignored issues
–
show
|
|||||
| 98 | } |
||||
| 99 | |||||
| 100 | /** |
||||
| 101 | * Downloads graph as csv |
||||
| 102 | * |
||||
| 103 | * @param array $stats The stats being downloaded. |
||||
| 104 | * @param resource $stream The stream to output to. |
||||
| 105 | * @param array $headers The fields to stream. |
||||
| 106 | * @since 1.0.19 |
||||
| 107 | */ |
||||
| 108 | public function download_csv( $stats, $stream, $headers ) { |
||||
| 109 | |||||
| 110 | // Output the csv column headers. |
||||
| 111 | fputcsv( $stream, $headers ); |
||||
| 112 | |||||
| 113 | // Loop through |
||||
| 114 | foreach ( $stats as $stat ) { |
||||
| 115 | $row = array_values( $this->prepare_row( $stat, $headers ) ); |
||||
| 116 | $row = array_map( 'maybe_serialize', $row ); |
||||
| 117 | fputcsv( $stream, $row ); |
||||
| 118 | } |
||||
| 119 | |||||
| 120 | } |
||||
| 121 | |||||
| 122 | /** |
||||
| 123 | * Downloads graph as json |
||||
| 124 | * |
||||
| 125 | * @param array $stats The stats being downloaded. |
||||
| 126 | * @param resource $stream The stream to output to. |
||||
| 127 | * @param array $headers The fields to stream. |
||||
| 128 | * @since 1.0.19 |
||||
| 129 | */ |
||||
| 130 | public function download_json( $stats, $stream, $headers ) { |
||||
| 131 | |||||
| 132 | $prepared = array(); |
||||
| 133 | |||||
| 134 | // Loop through |
||||
| 135 | foreach ( $stats as $stat ) { |
||||
| 136 | $prepared[] = $this->prepare_row( $stat, $headers ); |
||||
| 137 | } |
||||
| 138 | |||||
| 139 | fwrite( $stream, wp_json_encode( $prepared ) ); |
||||
|
0 ignored issues
–
show
It seems like
wp_json_encode($prepared) can also be of type false; however, parameter $data of fwrite() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 140 | |||||
| 141 | } |
||||
| 142 | |||||
| 143 | /** |
||||
| 144 | * Downloads graph as xml |
||||
| 145 | * |
||||
| 146 | * @param array $stats The stats being downloaded. |
||||
| 147 | * @param resource $stream The stream to output to. |
||||
| 148 | * @param array $headers The fields to stream. |
||||
| 149 | * @since 1.0.19 |
||||
| 150 | */ |
||||
| 151 | public function download_xml( $stats, $stream, $headers ) { |
||||
| 152 | |||||
| 153 | $prepared = array(); |
||||
| 154 | |||||
| 155 | // Loop through |
||||
| 156 | foreach ( $stats as $stat ) { |
||||
| 157 | $prepared[] = $this->prepare_row( $stat, $headers ); |
||||
| 158 | } |
||||
| 159 | |||||
| 160 | $xml = new SimpleXMLElement( '<?xml version="1.0"?><data></data>' ); |
||||
| 161 | $this->convert_array_xml( $prepared, $xml ); |
||||
| 162 | |||||
| 163 | fwrite( $stream, $xml->asXML() ); |
||||
|
0 ignored issues
–
show
It seems like
$xml->asXML() can also be of type true; however, parameter $data of fwrite() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 164 | |||||
| 165 | } |
||||
| 166 | |||||
| 167 | /** |
||||
| 168 | * Converts stats array to xml |
||||
| 169 | * |
||||
| 170 | * @access public |
||||
| 171 | * @since 1.0.19 |
||||
| 172 | */ |
||||
| 173 | public function convert_array_xml( $data, $xml ) { |
||||
| 174 | |||||
| 175 | // Loop through |
||||
| 176 | foreach ( $data as $key => $value ) { |
||||
| 177 | |||||
| 178 | $key = preg_replace( '/[^A-Za-z0-9_\-]/', '', $key ); |
||||
| 179 | |||||
| 180 | if ( is_array( $value ) ) { |
||||
| 181 | |||||
| 182 | if ( is_numeric( $key ) ) { |
||||
| 183 | $key = 'item' . $key; //dealing with <0/>..<n/> issues |
||||
| 184 | } |
||||
| 185 | |||||
| 186 | $subnode = $xml->addChild( $key ); |
||||
| 187 | $this->convert_array_xml( $value, $subnode ); |
||||
| 188 | |||||
| 189 | } else { |
||||
| 190 | $xml->addChild( $key, $value ? htmlspecialchars( $value ) : $value ); |
||||
| 191 | } |
||||
| 192 | } |
||||
| 193 | |||||
| 194 | } |
||||
| 195 | |||||
| 196 | /** |
||||
| 197 | * Prepares a single row for download. |
||||
| 198 | * |
||||
| 199 | * @param stdClass|array $row The row to prepare.. |
||||
| 200 | * @param array $fields The fields to stream. |
||||
| 201 | * @since 1.0.19 |
||||
| 202 | * @return array |
||||
| 203 | */ |
||||
| 204 | public function prepare_row( $row, $fields ) { |
||||
| 205 | |||||
| 206 | $prepared = array(); |
||||
| 207 | $row = (array) $row; |
||||
| 208 | |||||
| 209 | foreach ( $fields as $field ) { |
||||
| 210 | |||||
| 211 | if ( $field === 'total' ) { |
||||
| 212 | $prepared[ $field ] = html_entity_decode( strip_tags( wpinv_price( $row['total'] ) ), ENT_QUOTES ); |
||||
| 213 | continue; |
||||
| 214 | } |
||||
| 215 | |||||
| 216 | if ( $field === 'total_raw' ) { |
||||
| 217 | $prepared[ $field ] = wpinv_round_amount( wpinv_sanitize_amount( $row['total'] ) ); |
||||
| 218 | continue; |
||||
| 219 | } |
||||
| 220 | |||||
| 221 | $prepared[ $field ] = strip_tags( $row[ $field ] ); |
||||
| 222 | |||||
| 223 | } |
||||
| 224 | |||||
| 225 | return $prepared; |
||||
| 226 | } |
||||
| 227 | |||||
| 228 | |||||
| 229 | } |
||||
| 230 |
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.