PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 1.7.0.9 All 211 releases
charitable / includes / logger / class-charitable-log-export.php

class-charitable-log-export.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at includes/logger/class-charitable-log-export.php

163 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Charitable Log Export class.
4 *
5 * Handles CSV export of log records.
6 *
7 * @package Charitable/Classes/Charitable_Log_Export
8 * @author David Bisset
9 * @copyright Copyright (c) 2023, WP Charitable LLC
10 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
11 * @since 1.8.11
12 */
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 if ( ! class_exists( 'Charitable_Log_Export' ) ) :
20
21 /**
22 * Charitable_Log_Export
23 *
24 * @since 1.8.11
25 */
26 class Charitable_Log_Export {
27
28 /**
29 * Run the export.
30 *
31 * @since 1.8.11
32 */
33 public function run() {
34 $export_all = isset( $_GET['export_all'] ) && '1' === $_GET['export_all']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
35
36 $args = array(
37 'limit' => 999999,
38 );
39
40 // Apply filters unless exporting all.
41 if ( ! $export_all ) {
42 if ( ! empty( $_GET['log_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
43 $args['type'] = sanitize_key( $_GET['log_type'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
44 }
45 if ( ! empty( $_GET['log_level'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
46 $args['level'] = sanitize_key( $_GET['log_level'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
47 }
48 if ( ! empty( $_GET['log_source'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
49 $args['source'] = sanitize_key( $_GET['log_source'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
50 }
51 if ( ! empty( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
52 $args['search'] = sanitize_text_field( wp_unslash( $_GET['s'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
53 }
54 }
55
56 $query = Charitable_Log::get_instance()->get_query();
57 $result = $query->get( $args );
58
59 $filename = 'charitable-logs-' . gmdate( 'Y-m-d' ) . '.csv';
60
61 // Discard any prior output so the CSV stream isn't corrupted by stray notices/echoes.
62 while ( ob_get_level() ) {
63 ob_end_clean();
64 }
65
66 // Headers.
67 nocache_headers();
68 header( 'Content-Type: text/csv; charset=utf-8' );
69 header( 'Content-Disposition: attachment; filename=' . $filename );
70
71 $output = fopen( 'php://output', 'w' );
72
73 // Column headers.
74 fputcsv(
75 $output,
76 array(
77 'ID',
78 'Title',
79 'Message',
80 'Types',
81 'Level',
82 'Source',
83 'Date',
84 'Campaign ID',
85 'Donation ID',
86 'Donor ID',
87 'User ID',
88 'Object ID',
89 'Object Type',
90 ),
91 ',',
92 '"',
93 '\\'
94 );
95
96 // Data rows.
97 foreach ( $result['records'] as $record ) {
98 fputcsv(
99 $output,
100 array_map(
101 array( $this, 'csv_safe' ),
102 array(
103 $record->id,
104 $record->title,
105 wp_strip_all_tags( $record->message ),
106 $record->types,
107 $record->level,
108 $record->source,
109 $record->get_date( 'full' ),
110 $record->campaign_id,
111 $record->donation_id,
112 $record->donor_id,
113 $record->user_id,
114 $record->object_id,
115 $record->object_type,
116 )
117 ),
118 ',',
119 '"',
120 '\\'
121 );
122 }
123
124 fclose( $output );
125 exit;
126 }
127
128 /**
129 * Neutralize CSV-formula-injection payloads before writing a cell.
130 *
131 * Donor-supplied strings (e.g. payment_method from the unauthenticated
132 * PayPal Commerce AJAX handlers) flow into log records and surface here
133 * unchanged. Excel/Numbers/Sheets interpret cells beginning with `=`,
134 * `+`, `-`, `@`, tab, or carriage return as formulas — a value like
135 * `=HYPERLINK("…", "Click me")` renders as a clickable phishing link
136 * in the opened spreadsheet, and `=WEBSERVICE(…)` can exfiltrate
137 * adjacent cells without user interaction in older Excel builds.
138 *
139 * The standard mitigation is to prefix any such cell with a single
140 * quote, which spreadsheet apps consume as a text marker and strip
141 * from the rendered value. Non-string values are returned untouched
142 * so numeric IDs aren't quoted into strings unnecessarily.
143 *
144 * @since 1.8.11
145 *
146 * @param mixed $value Cell value.
147 * @return mixed
148 */
149 protected function csv_safe( $value ) {
150 if ( ! is_string( $value ) || '' === $value ) {
151 return $value;
152 }
153
154 if ( false !== strpbrk( $value[0], "=+-@\t\r" ) ) {
155 return "'" . $value;
156 }
157
158 return $value;
159 }
160 }
161
162 endif;
163