Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/phpcs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: PHPCS

on: pull_request

jobs:
phpcs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
- run: |
composer global config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer global require wp-coding-standards/wpcs
- run: phpcs
6 changes: 0 additions & 6 deletions phpcs-security.xml.dist

This file was deleted.

23 changes: 23 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<ruleset name="do_action">
<file>.</file>
<arg name="extensions" value="php"/>

<!-- Show sniff codes in all reports -->
<arg value="ps"/>
<arg name="colors"/>

<!-- Bundled third-party library. -->
<exclude-pattern>*/akismet.fuspam.php</exclude-pattern>

<rule ref="WordPress"/>

<rule ref="WordPress.WP.Capabilities">
<properties>
<property name="custom_capabilities" type="array">
<element value="organiser"/>
<element value="use_do_action_tools"/>
</property>
</properties>
</rule>
</ruleset>
2 changes: 1 addition & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ phpunit -c /absolute/path/to/doaction.org/phpunit.xml.dist

The WordPress test bootstrap recreates database tables. Never point the configuration at a live database. The tests load the plugin automatically and cover recipient authorization, nonprofit associations, native custom fields, and metadata escaping.

Run the repository-wide request and output checks with `phpcs --standard=/absolute/path/to/doaction.org/phpcs-security.xml.dist`. New and changed PHP code must also pass the full `WordPress` standard. Narrow inline exceptions cover read-only navigation and form markup whose individual values are already escaped.
Run the WordPress coding standards checks (configured in `phpcs.xml.dist`) with `phpcs` from the repository root; CI runs the same check on every pull request. Narrow inline exceptions cover read-only navigation and form markup whose individual values are already escaped.

The security fix records validated nonprofit selections in protected metadata. Existing cross-owner associations remain accessible to administrators, but an administrator must re-save the event's nonprofit selection to approve those associations for public signup. Historical metadata cannot establish whether an administrator or an unauthorized organiser created them.
7 changes: 3 additions & 4 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,15 @@
require_once $do_action_tests_dir . '/includes/functions.php';

tests_add_filter(
'init',
'muplugins_loaded',
/**
* Load the plugin before its init callbacks, after translations are available.
* Load the plugin before WordPress fires its init callbacks, as it would in production.
*
* @return void
*/
static function (): void {
require dirname( __DIR__ ) . '/wp-content/plugins/do-action/do-action.php';
},
0
}
);

require $do_action_tests_dir . '/includes/bootstrap.php';
16 changes: 8 additions & 8 deletions tests/test-security.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public function tear_down(): void {
* @return array Recipient records.
*/
private function recipients( array|false $orgs = false ): array {
$method = new ReflectionMethod( do_action_tools::class, 'get_people_data' );
$method = new ReflectionMethod( Do_Action_Tools::class, 'get_people_data' );
return $method->invoke( do_action_functions()->tools, $this->ids['event'], array( 'npo', 'designer' ), $orgs );
}

Expand Down Expand Up @@ -246,7 +246,7 @@ public function test_signup_form_ignores_poisoned_relationship(): void {
public function test_metabox_save_rejects_foreign_association(): void {
$_POST['do_action_meta_nonce'] = wp_create_nonce( 'do_action_save_meta_' . $this->ids['event'] );
$_REQUEST['nonprofits'] = array( $this->ids['other'] );
$admin = new do_action_Admin_API();
$admin = new Do_Action_Admin_API();
$admin->save_meta_boxes( $this->ids['event'] );
remove_action( 'save_post', array( $admin, 'save_meta_boxes' ) );
$this->assertSame( array( $this->ids['own'] ), get_post_meta( $this->ids['event'], 'nonprofits', true ) );
Expand Down Expand Up @@ -276,7 +276,7 @@ public function test_administrator_can_approve_cross_owner_association(): void {
wp_set_current_user( $this->administrator->ID );
$_POST['do_action_meta_nonce'] = wp_create_nonce( 'do_action_save_meta_' . $this->ids['event'] );
$_REQUEST['nonprofits'] = array( $this->ids['other'] );
$admin = new do_action_Admin_API();
$admin = new Do_Action_Admin_API();
$admin->save_meta_boxes( $this->ids['event'] );
remove_action( 'save_post', array( $admin, 'save_meta_boxes' ) );
$this->assertSame( array( $this->ids['other'] ), get_post_meta( $this->ids['event'], 'nonprofits', true ) );
Expand Down Expand Up @@ -331,7 +331,7 @@ public function test_contact_metadata_is_escaped_without_requiring_login(): void
* @return void
*/
public function test_email_preview_filters_html_after_substitution(): void {
$method = new ReflectionMethod( do_action_tools::class, 'format_email' );
$method = new ReflectionMethod( Do_Action_Tools::class, 'format_email' );
$html = $method->invoke(
do_action_functions()->tools,
'<strong>Hello {{NAME}}</strong><script>alert(1)</script><a href="javascript:alert(1)">Link</a><img src=x onerror=alert(1)> C:\\team',
Expand Down Expand Up @@ -371,7 +371,7 @@ public function test_metadata_round_trip_preserves_text(): void {
$_POST['do_action_meta_nonce'] = wp_create_nonce( 'do_action_save_meta_' . $this->ids['event'] );
$_REQUEST['venue_name'] = wp_slash( 'The "Hall" C:\\venue <script>alert(1)</script>' );
$_REQUEST['nonprofits'] = array( $this->ids['own'] );
$admin = new do_action_Admin_API();
$admin = new Do_Action_Admin_API();
$admin->save_meta_boxes( $this->ids['event'] );
remove_action( 'save_post', array( $admin, 'save_meta_boxes' ) );
$this->assertSame( 'The "Hall" C:\\venue', get_post_meta( $this->ids['event'], 'venue_name', true ) );
Expand All @@ -386,7 +386,7 @@ public function test_metadata_preserves_encoded_urls(): void {
$url = 'https://example.org/a%20b/path%2Fpart?q=a%26b';
$_POST['do_action_meta_nonce'] = wp_create_nonce( 'do_action_save_meta_' . $this->ids['own'] );
$_REQUEST['url'] = wp_slash( $url );
$admin = new do_action_Admin_API();
$admin = new Do_Action_Admin_API();
$admin->save_meta_boxes( $this->ids['own'] );
remove_action( 'save_post', array( $admin, 'save_meta_boxes' ) );
$this->assertSame( $url, get_post_meta( $this->ids['own'], 'url', true ) );
Expand All @@ -401,7 +401,7 @@ public function test_metadata_preserves_email_local_parts(): void {
$email = 'team%abcd@example.org';
$_POST['do_action_meta_nonce'] = wp_create_nonce( 'do_action_save_meta_' . $this->ids['event'] );
$_REQUEST['organiser_email'] = wp_slash( $email );
$admin = new do_action_Admin_API();
$admin = new Do_Action_Admin_API();
try {
$admin->save_meta_boxes( $this->ids['event'] );
$this->assertSame( $email, get_post_meta( $this->ids['event'], 'organiser_email', true ) );
Expand All @@ -417,7 +417,7 @@ public function test_metadata_preserves_email_local_parts(): void {
*/
public function test_admin_field_retains_controls_and_escapes_values(): void {
update_post_meta( $this->ids['event'], 'venue_name', '"><script>alert(1)</script>' );
$admin = new do_action_Admin_API();
$admin = new Do_Action_Admin_API();
ob_start();
$admin->display_meta_box_field(
array(
Expand Down
14 changes: 7 additions & 7 deletions wp-content/plugins/do-action/do-action.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
/*
/**
* Plugin Name: do_action
* Version: 1.0
* Plugin URI: http://doaction.org/
Expand All @@ -21,7 +21,7 @@
exit;
}

// Load plugin localisation
// Load plugin localisation.
add_action(
'init',
function () {
Expand All @@ -30,11 +30,11 @@ function () {
0
);

// Load plugin class files
// Load plugin class files.
require_once 'includes/class-do-action.php';
require_once 'includes/class-do-action-tools.php';

// Load plugin libraries
// Load plugin libraries.
require_once 'includes/lib/class-do-action-admin-api.php';
require_once 'includes/lib/class-do-action-post-type.php';
require_once 'includes/lib/class-do-action-taxonomy.php';
Expand All @@ -43,11 +43,11 @@ function () {
* Returns the main instance of do_action to prevent the need to use globals.
*
* @since 1.0.0
* @return object do_action
* @return object Do_Action
*/
function do_action_functions() {
$instance = do_action::instance( __FILE__, '1.0.0' );
$instance->tools = do_action_tools::instance( $instance );
$instance = Do_Action::instance( __FILE__, '1.0.0' );
$instance->tools = Do_Action_Tools::instance( $instance );
return $instance;
}

Expand Down
Loading
Loading