-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
wp-sri-admin.php
225 lines (193 loc) · 8.28 KB
/
wp-sri-admin.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
<?php
/**
* WordPress Subresource Integrity Manager Admin Interface
*
* @package plugin
*/
if (!class_exists('WP_List_Table')) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* List table for managing known resource hashes.
*/
class WP_SRI_Known_Hashes_List_Table extends WP_List_Table {
/**
* Options array of excluded asset URLs.
*
* @var array
*/
protected $sri_exclude;
/**
* Constructor.
*/
public function __construct () {
$this->sri_exclude = get_option(WP_SRI_Plugin::prefix.'excluded_hashes', array()); // Get our excluded option array
parent::__construct(array(
'singular' => esc_html__('Known Hash', 'wp-sri'),
'plural' => esc_html__('Known Hashes', 'wp-sri'),
'ajax' => false,
'screen' => get_current_screen(), // https://wordpress.org/support/topic/php-notice-because-constructor-for-class-wp_list_table?replies=1
));
}
public function no_items () {
esc_html_e('No hashes known.', 'wp-sri');
}
public function get_columns () {
return array(
'cb' => '<input type="checkbox" />',
'url' => esc_html__('URL', 'wp-sri'),
'hash' => esc_html__('Hash', 'wp-sri'),
'exclude' => esc_html__( 'Exclude', 'wp-sri' ),
);
}
public function get_sortable_columns () {
return array(
'url' => array('url', false),
'hash' => array('hash', false),
'exclude' => array( 'exclude', true )
);
}
public function get_bulk_actions () {
return array(
'delete' => esc_html__('Delete', 'wp-sri'),
'exclude' => esc_html__( 'Exclude', 'wp-sri' ),
'include' => esc_html__( 'Include', 'wp-wri' )
);
}
public function column_cb ($item) {
return sprintf(
'<input type="checkbox" name="url[]" value="%s" />', rawurlencode($item['url'])
);
}
private function usort_reorder ($a, $b) {
$orderby = (!empty($_GET['orderby'])) ? $_GET['orderby'] : 'exclude';
$order = (!empty($_GET['order'])) ? $_GET['order'] : 'asc';
$result = strcmp($a[$orderby], $b[$orderby]);
return ($order === 'asc') ? $result : -$result;
}
public function search ($value) {
return (false !== strpos($value, $_POST['s']));
}
public function prepare_items () {
$this->_column_headers = $this->get_column_info();
$known_hashes = get_option(WP_SRI_Plugin::prefix.'known_hashes', array());
if (!empty($_POST['s'])) {
$known_hashes = array_flip(array_filter(array_flip($known_hashes), array($this, 'search')));
}
$total_hashes = count($known_hashes);
$show_on_page = $this->get_items_per_page('wp_sri_hashes_per_page', 20);
$this->set_pagination_args(array(
'total_items' => $total_hashes,
'total_pages' => ceil($total_hashes / $show_on_page),
'per_page' => $show_on_page
));
$items = array();
foreach ($known_hashes as $url => $hash) {
$exclude = ( false !== array_search( $url, $this->sri_exclude ) ) ? 'a' : 'b';
$items[] = array(
'url' => $url,
'hash' => $hash,
'exclude' => $exclude
);
}
usort($items, array(&$this, 'usort_reorder'));
$shown_hashes = array_slice($items, (($this->get_pagenum() - 1) * $show_on_page), $show_on_page);
return $this->items = $shown_hashes;
}
/**
* Create our output for the Excluded column.
*
* If the row's $url is in our excluded array, make sure box is checked.
* We include a loading image which is hidden using CSS by default.
* Checkboxes are disabled by default, enabled using JS if available.
*
* @param $item
*
* @return string
*/
protected function column_exclude( $item ) {
$url = esc_url( $item['url'] );
$hash = $item['hash'];
if ( false !== array_search( $url, $this->sri_exclude) ) {
$checked = 'checked="checked"';
} else {
$checked = '';
}
$loading = plugin_dir_url( __FILE__ ) . 'css/working.gif'; // image shown during AJAX request to indicate something is happening.
return sprintf('<input disabled="disabled" type="checkbox" class="sri-exclude" id="%s" %s><span class="sri-loading"><img src="%s" /> </span>', $url, $checked, $loading );
}
protected function column_url ($item) {
$actions = array(
'delete' => sprintf(
'<a href="?page=%s&action=%s&url=%s&_wp_sri_nonce=%s&orderby=%s&order=%s" title="%s">%s</a>',
$_REQUEST['page'], 'delete', rawurlencode($item['url']), wp_create_nonce('update_sri_hash'),
(!empty($_GET['orderby'])) ? $_GET['orderby'] : '', (!empty($_GET['order'])) ? $_GET['order'] : '',
esc_html__('Remove this URL and hash pair.', 'wp-sri'), esc_html__('Delete', 'wp-sri')
)
);
$this->get_exclude_actions( $item, $actions );
return sprintf('%1$s %2$s', $item['url'], $this->row_actions($actions));
}
/**
* Add proper output to $actions array depending on whether or not $item['url'] is being excluded.
*
* @param $item array Table row data
* @param $actions array ref We're adding our action directoy to the array used in the above column_url() func
*/
protected function get_exclude_actions( $item, &$actions ) {
$url = esc_url( $item['url'] );
if ( false === array_search( $url, $this->sri_exclude ) ) {
$actions['exclude'] = sprintf(
'<a href="?page=%s&action=%s&url=%s&_wp_sri_nonce=%s&orderby=%s&order=%s" title="%s">%s</a>',
$_REQUEST['page'], 'exclude', rawurlencode($item['url']), wp_create_nonce('update_sri_hash'),
(!empty($_GET['orderby'])) ? $_GET['orderby'] : '', (!empty($_GET['order'])) ? $_GET['order'] : '',
esc_html__('Exclude this URL.', 'wp-sri'), esc_html__('Exclude', 'wp-sri')
);
} else {
$actions['include'] = sprintf(
'<a href="?page=%s&action=%s&url=%s&_wp_sri_nonce=%s&orderby=%s&order=%s" title="%s">%s</a>',
$_REQUEST['page'], 'include', rawurlencode($item['url']), wp_create_nonce('update_sri_hash'),
(!empty($_GET['orderby'])) ? $_GET['orderby'] : '', (!empty($_GET['order'])) ? $_GET['order'] : '',
esc_html__('Include this URL.', 'wp-sri'), esc_html__('Include', 'wp-sri')
);
}
}
// TODO: implement hash editing interface
// protected function column_hash ($item) {
// $actions = array(
// 'edit_hash' => sprintf(
// '<a href="?page=%s&action=%s&url=%s" title="%s">%s</a>',
// $_REQUEST['page'], 'edit_hash', rawurlencode($item['url']),
// esc_html__('Edit the hash for this URL.', 'wp-sri'), esc_html__('Edit Hash', 'wp-sri')
// )
// );
// return sprintf('%1$s %2$s', $item['hash'], $this->row_actions($actions));
// }
protected function column_default ($item, $column_name) {
return $item[$column_name];
}
public static function update_sri_exclude () {
check_ajax_referer( 'sri-update-exclusion', 'security' );
$update = false;
$excluded = get_option(WP_SRI_Plugin::prefix.'excluded_hashes', array());
$url = esc_url( $_POST['url'] );
$checked = filter_var( $_POST['checked'], FILTER_VALIDATE_BOOLEAN );
if ( $checked ) {
// If checked, we add $url to our exclusion array.
if ( ! in_array( $url, $excluded ) ) {
$excluded[] = $url;
$update = true;
}
} else {
// If unchecked, we remove $url from our exclusion array.
if ( false !== ($key = array_search( $url, $excluded)) ) {
unset( $excluded[$key] );
$update = true;
}
}
if ( $update ) {
update_option( WP_SRI_Plugin::prefix.'excluded_hashes', $excluded );
}
wp_send_json_success( 'done' );
}
}