@mytechtoday/augment-extensions 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/augment-extensions/domain-rules/wordpress/README.md +163 -0
- package/augment-extensions/domain-rules/wordpress/module.json +32 -0
- package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -0
- package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -0
- package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -0
- package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -0
- package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -0
- package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -0
- package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -0
- package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -0
- package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -0
- package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -0
- package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -0
- package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -0
- package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -0
- package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -0
- package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -0
- package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -0
- package/augment-extensions/examples/rest-api-plugin/README.md +98 -0
- package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -0
- package/augment-extensions/examples/rest-api-plugin/module.json +40 -0
- package/augment-extensions/examples/woocommerce-extension/README.md +98 -0
- package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -0
- package/augment-extensions/examples/woocommerce-extension/module.json +40 -0
- package/augment-extensions/workflows/wordpress-plugin/README.md +232 -0
- package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -0
- package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -0
- package/augment-extensions/workflows/wordpress-plugin/module.json +49 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -0
- package/cli/dist/cli.js +5 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/show.d.ts.map +1 -1
- package/cli/dist/commands/show.js +41 -0
- package/cli/dist/commands/show.js.map +1 -1
- package/modules.md +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
# WordPress Security Best Practices
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document provides comprehensive security guidelines for WordPress development, covering input sanitization, output escaping, authentication, and common vulnerabilities.
|
|
6
|
+
|
|
7
|
+
## Critical Security Rules
|
|
8
|
+
|
|
9
|
+
### 🔴 NEVER
|
|
10
|
+
|
|
11
|
+
❌ Trust user input
|
|
12
|
+
❌ Use `eval()` or similar dangerous functions
|
|
13
|
+
❌ Store passwords in plain text
|
|
14
|
+
❌ Use `$_GET`, `$_POST`, `$_REQUEST` directly without sanitization
|
|
15
|
+
❌ Output data without escaping
|
|
16
|
+
❌ Use SQL queries without prepared statements
|
|
17
|
+
❌ Hardcode credentials
|
|
18
|
+
❌ Disable security features for convenience
|
|
19
|
+
❌ Use deprecated functions
|
|
20
|
+
❌ Ignore WordPress security updates
|
|
21
|
+
|
|
22
|
+
### ✅ ALWAYS
|
|
23
|
+
|
|
24
|
+
✅ Sanitize all input
|
|
25
|
+
✅ Escape all output
|
|
26
|
+
✅ Use nonces for form submissions
|
|
27
|
+
✅ Check user capabilities
|
|
28
|
+
✅ Use prepared statements for database queries
|
|
29
|
+
✅ Validate and verify data
|
|
30
|
+
✅ Use HTTPS
|
|
31
|
+
✅ Keep WordPress, themes, and plugins updated
|
|
32
|
+
✅ Use strong passwords
|
|
33
|
+
✅ Implement proper error handling
|
|
34
|
+
|
|
35
|
+
## Input Sanitization
|
|
36
|
+
|
|
37
|
+
### Text Fields
|
|
38
|
+
|
|
39
|
+
```php
|
|
40
|
+
// Sanitize text field
|
|
41
|
+
$clean_text = sanitize_text_field( $_POST['field'] );
|
|
42
|
+
|
|
43
|
+
// Sanitize textarea
|
|
44
|
+
$clean_textarea = sanitize_textarea_field( $_POST['textarea'] );
|
|
45
|
+
|
|
46
|
+
// Sanitize title
|
|
47
|
+
$clean_title = sanitize_title( $_POST['title'] );
|
|
48
|
+
|
|
49
|
+
// Sanitize key (lowercase alphanumeric with dashes and underscores)
|
|
50
|
+
$clean_key = sanitize_key( $_POST['key'] );
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Email and URL
|
|
54
|
+
|
|
55
|
+
```php
|
|
56
|
+
// Sanitize email
|
|
57
|
+
$clean_email = sanitize_email( $_POST['email'] );
|
|
58
|
+
|
|
59
|
+
// Validate email
|
|
60
|
+
if ( ! is_email( $clean_email ) ) {
|
|
61
|
+
wp_die( __( 'Invalid email address', 'my-plugin' ) );
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Sanitize URL
|
|
65
|
+
$clean_url = esc_url_raw( $_POST['url'] );
|
|
66
|
+
|
|
67
|
+
// Sanitize file name
|
|
68
|
+
$clean_filename = sanitize_file_name( $_FILES['file']['name'] );
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### HTML and Rich Content
|
|
72
|
+
|
|
73
|
+
```php
|
|
74
|
+
// Sanitize HTML (allows safe HTML tags)
|
|
75
|
+
$clean_html = wp_kses_post( $_POST['content'] );
|
|
76
|
+
|
|
77
|
+
// Sanitize with custom allowed tags
|
|
78
|
+
$allowed_tags = array(
|
|
79
|
+
'a' => array(
|
|
80
|
+
'href' => array(),
|
|
81
|
+
'title' => array(),
|
|
82
|
+
),
|
|
83
|
+
'br' => array(),
|
|
84
|
+
'em' => array(),
|
|
85
|
+
'strong' => array(),
|
|
86
|
+
);
|
|
87
|
+
$clean_html = wp_kses( $_POST['content'], $allowed_tags );
|
|
88
|
+
|
|
89
|
+
// Strip all HTML tags
|
|
90
|
+
$clean_text = wp_strip_all_tags( $_POST['content'] );
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Arrays and Complex Data
|
|
94
|
+
|
|
95
|
+
```php
|
|
96
|
+
// Sanitize array of text fields
|
|
97
|
+
$clean_array = array_map( 'sanitize_text_field', $_POST['items'] );
|
|
98
|
+
|
|
99
|
+
// Sanitize array recursively
|
|
100
|
+
function sanitize_array( $array ) {
|
|
101
|
+
foreach ( $array as $key => &$value ) {
|
|
102
|
+
if ( is_array( $value ) ) {
|
|
103
|
+
$value = sanitize_array( $value );
|
|
104
|
+
} else {
|
|
105
|
+
$value = sanitize_text_field( $value );
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return $array;
|
|
109
|
+
}
|
|
110
|
+
$clean_data = sanitize_array( $_POST['data'] );
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Numbers and Booleans
|
|
114
|
+
|
|
115
|
+
```php
|
|
116
|
+
// Sanitize integer
|
|
117
|
+
$clean_int = absint( $_POST['number'] ); // Absolute integer (always positive)
|
|
118
|
+
$clean_int = intval( $_POST['number'] ); // Can be negative
|
|
119
|
+
|
|
120
|
+
// Sanitize float
|
|
121
|
+
$clean_float = floatval( $_POST['price'] );
|
|
122
|
+
|
|
123
|
+
// Sanitize boolean
|
|
124
|
+
$clean_bool = (bool) $_POST['checkbox'];
|
|
125
|
+
$clean_bool = rest_sanitize_boolean( $_POST['checkbox'] ); // REST API
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Output Escaping
|
|
129
|
+
|
|
130
|
+
### HTML Context
|
|
131
|
+
|
|
132
|
+
```php
|
|
133
|
+
// Escape HTML
|
|
134
|
+
echo esc_html( $text );
|
|
135
|
+
|
|
136
|
+
// Escape HTML with translation
|
|
137
|
+
echo esc_html__( 'Text to translate', 'my-plugin' );
|
|
138
|
+
echo esc_html_e( 'Text to translate', 'my-plugin' ); // Echo version
|
|
139
|
+
|
|
140
|
+
// Escape and translate with variables
|
|
141
|
+
echo esc_html( sprintf( __( 'Hello %s', 'my-plugin' ), $name ) );
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Attribute Context
|
|
145
|
+
|
|
146
|
+
```php
|
|
147
|
+
// Escape attributes
|
|
148
|
+
echo '<div class="' . esc_attr( $class ) . '">';
|
|
149
|
+
echo '<input type="text" value="' . esc_attr( $value ) . '" />';
|
|
150
|
+
|
|
151
|
+
// Escape attribute with translation
|
|
152
|
+
echo '<div title="' . esc_attr__( 'Title text', 'my-plugin' ) . '">';
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### URL Context
|
|
156
|
+
|
|
157
|
+
```php
|
|
158
|
+
// Escape URL
|
|
159
|
+
echo '<a href="' . esc_url( $url ) . '">';
|
|
160
|
+
|
|
161
|
+
// Escape URL for database storage
|
|
162
|
+
$clean_url = esc_url_raw( $url );
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### JavaScript Context
|
|
166
|
+
|
|
167
|
+
```php
|
|
168
|
+
// Escape JavaScript
|
|
169
|
+
echo '<script>var data = "' . esc_js( $data ) . '";</script>';
|
|
170
|
+
|
|
171
|
+
// Better: Use wp_localize_script or wp_json_encode
|
|
172
|
+
wp_localize_script( 'my-script', 'myData', array(
|
|
173
|
+
'value' => $data,
|
|
174
|
+
) );
|
|
175
|
+
|
|
176
|
+
// Or use JSON encoding
|
|
177
|
+
echo '<script>var data = ' . wp_json_encode( $data ) . ';</script>';
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Textarea Context
|
|
181
|
+
|
|
182
|
+
```php
|
|
183
|
+
// Escape textarea
|
|
184
|
+
echo '<textarea>' . esc_textarea( $content ) . '</textarea>';
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Nonce Verification
|
|
188
|
+
|
|
189
|
+
### Creating Nonces
|
|
190
|
+
|
|
191
|
+
```php
|
|
192
|
+
// Create nonce field in form
|
|
193
|
+
wp_nonce_field( 'my_action', 'my_nonce_field' );
|
|
194
|
+
|
|
195
|
+
// Create nonce URL
|
|
196
|
+
$url = wp_nonce_url( 'admin.php?page=my-page&action=delete', 'delete_action' );
|
|
197
|
+
|
|
198
|
+
// Create nonce value
|
|
199
|
+
$nonce = wp_create_nonce( 'my_action' );
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Verifying Nonces
|
|
203
|
+
|
|
204
|
+
```php
|
|
205
|
+
// Verify nonce in form submission
|
|
206
|
+
if ( ! isset( $_POST['my_nonce_field'] ) || ! wp_verify_nonce( $_POST['my_nonce_field'], 'my_action' ) ) {
|
|
207
|
+
wp_die( __( 'Security check failed', 'my-plugin' ) );
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Verify nonce in URL
|
|
211
|
+
if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'delete_action' ) ) {
|
|
212
|
+
wp_die( __( 'Security check failed', 'my-plugin' ) );
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Verify nonce in AJAX
|
|
216
|
+
check_ajax_referer( 'my_ajax_action', 'security' );
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Nonce in AJAX
|
|
220
|
+
|
|
221
|
+
```php
|
|
222
|
+
// Localize nonce for AJAX
|
|
223
|
+
wp_localize_script( 'my-ajax-script', 'myAjax', array(
|
|
224
|
+
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
|
225
|
+
'nonce' => wp_create_nonce( 'my_ajax_action' ),
|
|
226
|
+
) );
|
|
227
|
+
|
|
228
|
+
// JavaScript
|
|
229
|
+
jQuery.ajax({
|
|
230
|
+
url: myAjax.ajaxurl,
|
|
231
|
+
type: 'POST',
|
|
232
|
+
data: {
|
|
233
|
+
action: 'my_ajax_action',
|
|
234
|
+
security: myAjax.nonce,
|
|
235
|
+
data: 'value'
|
|
236
|
+
},
|
|
237
|
+
success: function(response) {
|
|
238
|
+
console.log(response);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// PHP handler
|
|
243
|
+
function my_ajax_handler() {
|
|
244
|
+
check_ajax_referer( 'my_ajax_action', 'security' );
|
|
245
|
+
|
|
246
|
+
// Process request
|
|
247
|
+
$data = sanitize_text_field( $_POST['data'] );
|
|
248
|
+
|
|
249
|
+
wp_send_json_success( array( 'message' => 'Success' ) );
|
|
250
|
+
}
|
|
251
|
+
add_action( 'wp_ajax_my_ajax_action', 'my_ajax_handler' );
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Capability Checks
|
|
255
|
+
|
|
256
|
+
### Check User Capabilities
|
|
257
|
+
|
|
258
|
+
```php
|
|
259
|
+
// Check if user can manage options
|
|
260
|
+
if ( ! current_user_can( 'manage_options' ) ) {
|
|
261
|
+
wp_die( __( 'You do not have sufficient permissions', 'my-plugin' ) );
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check if user can edit posts
|
|
265
|
+
if ( ! current_user_can( 'edit_posts' ) ) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Check if user can edit specific post
|
|
270
|
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
|
271
|
+
wp_die( __( 'You cannot edit this post', 'my-plugin' ) );
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check if user can delete users
|
|
275
|
+
if ( ! current_user_can( 'delete_users' ) ) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Common Capabilities
|
|
281
|
+
|
|
282
|
+
- `manage_options` - Administrator
|
|
283
|
+
- `edit_posts` - Editor, Author, Contributor
|
|
284
|
+
- `publish_posts` - Editor, Author
|
|
285
|
+
- `edit_published_posts` - Editor, Author
|
|
286
|
+
- `delete_posts` - Editor, Author, Contributor
|
|
287
|
+
- `upload_files` - Editor, Author
|
|
288
|
+
- `edit_pages` - Editor
|
|
289
|
+
- `edit_users` - Administrator
|
|
290
|
+
- `delete_users` - Administrator
|
|
291
|
+
- `install_plugins` - Administrator
|
|
292
|
+
- `activate_plugins` - Administrator
|
|
293
|
+
|
|
294
|
+
## Database Security
|
|
295
|
+
|
|
296
|
+
### Prepared Statements
|
|
297
|
+
|
|
298
|
+
```php
|
|
299
|
+
global $wpdb;
|
|
300
|
+
|
|
301
|
+
// Correct: Use prepared statements
|
|
302
|
+
$user_id = 123;
|
|
303
|
+
$results = $wpdb->get_results(
|
|
304
|
+
$wpdb->prepare(
|
|
305
|
+
"SELECT * FROM {$wpdb->prefix}my_table WHERE user_id = %d",
|
|
306
|
+
$user_id
|
|
307
|
+
)
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
// Correct: Multiple placeholders
|
|
311
|
+
$name = 'John';
|
|
312
|
+
$age = 30;
|
|
313
|
+
$wpdb->query(
|
|
314
|
+
$wpdb->prepare(
|
|
315
|
+
"INSERT INTO {$wpdb->prefix}my_table (name, age) VALUES (%s, %d)",
|
|
316
|
+
$name,
|
|
317
|
+
$age
|
|
318
|
+
)
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// WRONG: Direct variable insertion (SQL injection risk)
|
|
322
|
+
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}my_table WHERE user_id = $user_id" );
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Placeholder Types
|
|
326
|
+
|
|
327
|
+
- `%s` - String
|
|
328
|
+
- `%d` - Integer
|
|
329
|
+
- `%f` - Float
|
|
330
|
+
|
|
331
|
+
### Safe Database Operations
|
|
332
|
+
|
|
333
|
+
```php
|
|
334
|
+
// Insert
|
|
335
|
+
$wpdb->insert(
|
|
336
|
+
$wpdb->prefix . 'my_table',
|
|
337
|
+
array(
|
|
338
|
+
'name' => sanitize_text_field( $_POST['name'] ),
|
|
339
|
+
'age' => absint( $_POST['age'] ),
|
|
340
|
+
),
|
|
341
|
+
array( '%s', '%d' )
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
// Update
|
|
345
|
+
$wpdb->update(
|
|
346
|
+
$wpdb->prefix . 'my_table',
|
|
347
|
+
array( 'name' => sanitize_text_field( $_POST['name'] ) ),
|
|
348
|
+
array( 'id' => absint( $_POST['id'] ) ),
|
|
349
|
+
array( '%s' ),
|
|
350
|
+
array( '%d' )
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
// Delete
|
|
354
|
+
$wpdb->delete(
|
|
355
|
+
$wpdb->prefix . 'my_table',
|
|
356
|
+
array( 'id' => absint( $_POST['id'] ) ),
|
|
357
|
+
array( '%d' )
|
|
358
|
+
);
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## File Upload Security
|
|
362
|
+
|
|
363
|
+
### Validate File Uploads
|
|
364
|
+
|
|
365
|
+
```php
|
|
366
|
+
function validate_file_upload( $file ) {
|
|
367
|
+
// Check if file was uploaded
|
|
368
|
+
if ( ! isset( $file['error'] ) || is_array( $file['error'] ) ) {
|
|
369
|
+
wp_die( __( 'Invalid file upload', 'my-plugin' ) );
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Check for upload errors
|
|
373
|
+
if ( $file['error'] !== UPLOAD_ERR_OK ) {
|
|
374
|
+
wp_die( __( 'Upload error', 'my-plugin' ) );
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Check file size (5MB max)
|
|
378
|
+
if ( $file['size'] > 5242880 ) {
|
|
379
|
+
wp_die( __( 'File too large', 'my-plugin' ) );
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Check file type
|
|
383
|
+
$allowed_types = array( 'image/jpeg', 'image/png', 'image/gif' );
|
|
384
|
+
$finfo = finfo_open( FILEINFO_MIME_TYPE );
|
|
385
|
+
$mime_type = finfo_file( $finfo, $file['tmp_name'] );
|
|
386
|
+
finfo_close( $finfo );
|
|
387
|
+
|
|
388
|
+
if ( ! in_array( $mime_type, $allowed_types, true ) ) {
|
|
389
|
+
wp_die( __( 'Invalid file type', 'my-plugin' ) );
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Sanitize filename
|
|
393
|
+
$filename = sanitize_file_name( $file['name'] );
|
|
394
|
+
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Use WordPress Upload Functions
|
|
400
|
+
|
|
401
|
+
```php
|
|
402
|
+
// Use WordPress file upload handler
|
|
403
|
+
require_once( ABSPATH . 'wp-admin/includes/file.php' );
|
|
404
|
+
|
|
405
|
+
$uploaded_file = wp_handle_upload( $_FILES['file'], array( 'test_form' => false ) );
|
|
406
|
+
|
|
407
|
+
if ( isset( $uploaded_file['error'] ) ) {
|
|
408
|
+
wp_die( $uploaded_file['error'] );
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// File uploaded successfully
|
|
412
|
+
$file_url = $uploaded_file['url'];
|
|
413
|
+
$file_path = $uploaded_file['file'];
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Authentication and Authorization
|
|
417
|
+
|
|
418
|
+
### Check if User is Logged In
|
|
419
|
+
|
|
420
|
+
```php
|
|
421
|
+
if ( ! is_user_logged_in() ) {
|
|
422
|
+
wp_redirect( wp_login_url() );
|
|
423
|
+
exit;
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Verify User Identity
|
|
428
|
+
|
|
429
|
+
```php
|
|
430
|
+
// Check if current user owns the post
|
|
431
|
+
$post = get_post( $post_id );
|
|
432
|
+
if ( $post->post_author != get_current_user_id() ) {
|
|
433
|
+
wp_die( __( 'You do not own this post', 'my-plugin' ) );
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Password Hashing
|
|
438
|
+
|
|
439
|
+
```php
|
|
440
|
+
// Hash password (WordPress handles this automatically)
|
|
441
|
+
$user_id = wp_create_user( $username, $password, $email );
|
|
442
|
+
|
|
443
|
+
// Verify password
|
|
444
|
+
$user = get_user_by( 'login', $username );
|
|
445
|
+
if ( $user && wp_check_password( $password, $user->user_pass, $user->ID ) ) {
|
|
446
|
+
// Password is correct
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Update password
|
|
450
|
+
wp_set_password( $new_password, $user_id );
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Common Vulnerabilities
|
|
454
|
+
|
|
455
|
+
### SQL Injection Prevention
|
|
456
|
+
|
|
457
|
+
```php
|
|
458
|
+
// ✅ CORRECT
|
|
459
|
+
$wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %d", $id );
|
|
460
|
+
|
|
461
|
+
// ❌ WRONG
|
|
462
|
+
$wpdb->query( "SELECT * FROM {$wpdb->posts} WHERE ID = $id" );
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Cross-Site Scripting (XSS) Prevention
|
|
466
|
+
|
|
467
|
+
```php
|
|
468
|
+
// ✅ CORRECT
|
|
469
|
+
echo esc_html( $user_input );
|
|
470
|
+
|
|
471
|
+
// ❌ WRONG
|
|
472
|
+
echo $user_input;
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Cross-Site Request Forgery (CSRF) Prevention
|
|
476
|
+
|
|
477
|
+
```php
|
|
478
|
+
// ✅ CORRECT
|
|
479
|
+
wp_nonce_field( 'my_action', 'my_nonce' );
|
|
480
|
+
wp_verify_nonce( $_POST['my_nonce'], 'my_action' );
|
|
481
|
+
|
|
482
|
+
// ❌ WRONG
|
|
483
|
+
// No nonce verification
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Directory Traversal Prevention
|
|
487
|
+
|
|
488
|
+
```php
|
|
489
|
+
// ✅ CORRECT
|
|
490
|
+
$file = basename( $_GET['file'] );
|
|
491
|
+
$path = WP_CONTENT_DIR . '/uploads/' . $file;
|
|
492
|
+
|
|
493
|
+
// Verify file is in allowed directory
|
|
494
|
+
if ( strpos( realpath( $path ), WP_CONTENT_DIR . '/uploads/' ) !== 0 ) {
|
|
495
|
+
wp_die( __( 'Invalid file path', 'my-plugin' ) );
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ❌ WRONG
|
|
499
|
+
$file = $_GET['file'];
|
|
500
|
+
include( WP_CONTENT_DIR . '/uploads/' . $file );
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Security Headers
|
|
504
|
+
|
|
505
|
+
### Add Security Headers
|
|
506
|
+
|
|
507
|
+
```php
|
|
508
|
+
/**
|
|
509
|
+
* Add security headers
|
|
510
|
+
*/
|
|
511
|
+
function add_security_headers() {
|
|
512
|
+
header( 'X-Content-Type-Options: nosniff' );
|
|
513
|
+
header( 'X-Frame-Options: SAMEORIGIN' );
|
|
514
|
+
header( 'X-XSS-Protection: 1; mode=block' );
|
|
515
|
+
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
|
|
516
|
+
}
|
|
517
|
+
add_action( 'send_headers', 'add_security_headers' );
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## Best Practices Summary
|
|
521
|
+
|
|
522
|
+
### Input Handling
|
|
523
|
+
|
|
524
|
+
✅ Sanitize all input using appropriate functions
|
|
525
|
+
✅ Validate data types and formats
|
|
526
|
+
✅ Use whitelist validation when possible
|
|
527
|
+
✅ Never trust user input
|
|
528
|
+
|
|
529
|
+
### Output Handling
|
|
530
|
+
|
|
531
|
+
✅ Escape all output based on context
|
|
532
|
+
✅ Use `esc_html()`, `esc_attr()`, `esc_url()`, `esc_js()`
|
|
533
|
+
✅ Escape late (just before output)
|
|
534
|
+
|
|
535
|
+
### Database
|
|
536
|
+
|
|
537
|
+
✅ Always use prepared statements
|
|
538
|
+
✅ Use `$wpdb->prepare()` for custom queries
|
|
539
|
+
✅ Use WordPress database functions when possible
|
|
540
|
+
|
|
541
|
+
### Authentication
|
|
542
|
+
|
|
543
|
+
✅ Use nonces for all form submissions
|
|
544
|
+
✅ Check user capabilities
|
|
545
|
+
✅ Verify user identity for sensitive operations
|
|
546
|
+
✅ Use HTTPS for login and admin areas
|
|
547
|
+
|
|
548
|
+
### Files
|
|
549
|
+
|
|
550
|
+
✅ Validate file uploads
|
|
551
|
+
✅ Check file types and sizes
|
|
552
|
+
✅ Use WordPress upload functions
|
|
553
|
+
✅ Store uploads outside web root when possible
|
|
554
|
+
|
|
555
|
+
### General
|
|
556
|
+
|
|
557
|
+
✅ Keep WordPress, themes, and plugins updated
|
|
558
|
+
✅ Use strong passwords
|
|
559
|
+
✅ Limit login attempts
|
|
560
|
+
✅ Regular security audits
|
|
561
|
+
✅ Monitor error logs
|
|
562
|
+
✅ Use security plugins
|
|
563
|
+
✅ Regular backups
|
|
564
|
+
|