diff --git a/api.wordpress.org/public_html/dotorg/slack/announce.php b/api.wordpress.org/public_html/dotorg/slack/announce.php
index 04a7c8918b..3f87ea45ea 100644
--- a/api.wordpress.org/public_html/dotorg/slack/announce.php
+++ b/api.wordpress.org/public_html/dotorg/slack/announce.php
@@ -1,13 +1,21 @@
'wordpressdotorg',
'link_names' => 1,
- 'text' => sprintf( '@%s: Use the `/committers` command.', $_POST['user_name'] ),
+ 'text' => sprintf( '@%s: Use the `/committers` command.', $user_name ),
) );
exit;
diff --git a/api.wordpress.org/public_html/dotorg/slack/community-deputies-calendly-webhook.php b/api.wordpress.org/public_html/dotorg/slack/community-deputies-calendly-webhook.php
index cb4a7b7e38..c02d71a57a 100644
--- a/api.wordpress.org/public_html/dotorg/slack/community-deputies-calendly-webhook.php
+++ b/api.wordpress.org/public_html/dotorg/slack/community-deputies-calendly-webhook.php
@@ -1,4 +1,15 @@
getMessage(), E_USER_WARNING );
} finally {
@@ -59,6 +61,7 @@
* request, so this should still be 200 even if something goes wrong on our end.
*/
http_response_code( 200 );
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- plain-text response body for Slack.
die( $result );
}
}
diff --git a/api.wordpress.org/public_html/dotorg/slack/security-team.php b/api.wordpress.org/public_html/dotorg/slack/security-team.php
index 65ce43cf57..e94096d080 100644
--- a/api.wordpress.org/public_html/dotorg/slack/security-team.php
+++ b/api.wordpress.org/public_html/dotorg/slack/security-team.php
@@ -1,14 +1,23 @@
get_col( "SELECT user_id FROM slack_users WHERE slack_id IN ($slack_user_ids_for_sql)" );
@@ -62,7 +71,7 @@ function api_call() {
header( 'Content-type: text/plain' );
// Confirm it came from the Trac server.
- if ( ! hash_equals( API_TOKEN, $_GET['token'] ?? '' ) ) {
+ if ( ! is_string( $_GET['token'] ?? null ) || ! hash_equals( API_TOKEN, $_GET['token'] ) ) {
exit;
}
@@ -76,12 +85,12 @@ function api_call() {
exit;
}
- echo implode( "\n", $team ) . "\n"; // Trailing newline critical.
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- plain-text list; trailing newline critical.
+ echo implode( "\n", $team ) . "\n";
exit;
}
+// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- substring comparison only.
if ( isset( $_SERVER['REQUEST_URI'] ) && false !== strpos( $_SERVER['REQUEST_URI'], '/security-team.php?token=' ) ) {
api_call();
}
-
-}
diff --git a/api.wordpress.org/public_html/dotorg/slack/subgroup.php b/api.wordpress.org/public_html/dotorg/slack/subgroup.php
index 61ab079ae3..cc335732af 100644
--- a/api.wordpress.org/public_html/dotorg/slack/subgroup.php
+++ b/api.wordpress.org/public_html/dotorg/slack/subgroup.php
@@ -1,4 +1,16 @@
[
+ 'regexp' => '/^\d+\z/',
+ 'default' => '',
+ ],
+ ]
+ );
+ $signature = filter_var(
+ $_SERVER['HTTP_X_SLACK_SIGNATURE'] ?? '',
+ FILTER_VALIDATE_REGEXP,
+ [
+ 'options' => [
+ 'regexp' => '/^v0=[0-9a-f]{64}\z/',
+ 'default' => '',
+ ],
+ ]
+ );
if ( ! $timestamp || ! $signature ) {
return false;
}
@@ -104,6 +135,7 @@ function verify_slack_signature( $body ) {
// Dispatch: slash command vs. interactivity callback.
if ( isset( $_POST['payload'] ) ) {
+ // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON document covered by the verified signature above; fields are validated where read.
$payload = json_decode( $_POST['payload'], true );
handle_interaction( $payload );
exit;
@@ -111,9 +143,28 @@ function verify_slack_signature( $body ) {
handle_slash_command();
function handle_slash_command() {
- $channel_id = $_POST['channel_id'] ?? '';
- $user_id = $_POST['user_id'] ?? '';
- $trigger_id = $_POST['trigger_id'] ?? '';
+ // Slack channel and user IDs are uppercase alphanumeric, eg. `C0123ABCD` and `U0123ABCD`.
+ $id_options = [
+ 'options' => [
+ 'regexp' => '/^[A-Z0-9]+\z/',
+ 'default' => '',
+ ],
+ ];
+
+ $channel_id = (string) filter_var( $_POST['channel_id'] ?? '', FILTER_VALIDATE_REGEXP, $id_options );
+ $user_id = (string) filter_var( $_POST['user_id'] ?? '', FILTER_VALIDATE_REGEXP, $id_options );
+
+ // A trigger ID, eg. `13345224609.738474920.8088930838d88f008e0`.
+ $trigger_id = (string) filter_var(
+ $_POST['trigger_id'] ?? '',
+ FILTER_VALIDATE_REGEXP,
+ [
+ 'options' => [
+ 'regexp' => '/^[A-Za-z0-9.]+\z/',
+ 'default' => '',
+ ],
+ ]
+ );
// trigger_id is only valid for 3s, and listing every subgroup's membership will take
// longer than that. Open a loading view now, then views.update after we have the data.
@@ -692,7 +743,7 @@ function finalize_create( $new_id, $name, $creator, $parent_id, $parent_name, $u
// conversations.invite fails the entire batch on one malformed ID. Filter
// defensively so a stray character in a config value can't sink everyone.
$valid_invitees = array_values( array_filter( $invitees, function ( $id ) {
- return preg_match( '/^[UWB][A-Z0-9]+$/', $id );
+ return preg_match( '/^[UWB][A-Z0-9]+\z/', $id );
} ) );
$invitees = $valid_invitees;
diff --git a/api.wordpress.org/public_html/dotorg/slack/trac-bot.php b/api.wordpress.org/public_html/dotorg/slack/trac-bot.php
index 8fda47cef2..345b082c61 100644
--- a/api.wordpress.org/public_html/dotorg/slack/trac-bot.php
+++ b/api.wordpress.org/public_html/dotorg/slack/trac-bot.php
@@ -1,4 +1,14 @@
send( $parser->get_channel(), $parser->get_thread() );
- if ( $_POST['channel_name'] === 'test' ) {
+ if ( 'test' === $channel_name ) {
// Don't post to Trac if we're coming from #test.
continue;
}
@@ -111,7 +129,7 @@
$trac_xmlrpc = new \Trac( 'slackbot', SLACKBOT_WPORG_PASSWORD, "https://$trac.trac.wordpress.org/login/xmlrpc" );
- $comment = sprintf( $comment_template, $_POST['channel_name'], $_POST['user_name'], str_replace( '.', '', $_POST['timestamp'] ) );
+ $trac_comment = sprintf( $comment_template, $channel_name, $user_name, str_replace( '.', '', $msg_timestamp ) );
foreach ( $results['ticket'] as $ticket ) {
$ticket_id = is_array( $ticket ) ? $ticket['id'] : $ticket;
@@ -135,7 +153,7 @@
$parser->set_redundancy( 'trac', $trac, 'ticket', $ticket_id );
- $trac_xmlrpc->ticket_update( $ticket_id, $comment );
+ $trac_xmlrpc->ticket_update( $ticket_id, $trac_comment );
}
}
}