PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.12
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.12
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Services / Integrations / Slack / Slack.php

Slack.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.12, at app/Services/Integrations/Slack/Slack.php

178 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Services\Integrations\Slack;
4
5 use FluentForm\App\Helpers\Helper;
6 use FluentForm\App\Modules\Form\FormDataParser;
7 use FluentForm\App\Modules\Form\FormFieldsParser;
8 use FluentForm\App\Services\Integrations\LogResponseTrait;
9 use FluentForm\Framework\Helpers\ArrayHelper;
10
11 class Slack
12 {
13 use LogResponseTrait;
14
15 /**
16 * The slack integration settings of the form.
17 *
18 * @var array $settings
19 */
20 protected $settings = [];
21
22 /**
23 * Handle slack notifier.
24 *
25 * @param $submissionId
26 * @param $formData
27 * @param $form
28 */
29 public static function handle($feed, $formData, $form, $entry)
30 {
31 $settings = $feed['processedValues'];
32
33 $inputs = FormFieldsParser::getEntryInputs($form);
34
35 $labels = FormFieldsParser::getAdminLabels($form, $inputs);
36
37 $labels = apply_filters_deprecated(
38 'fluentform_slack_field_label_selection',
39 [
40 $labels,
41 $settings,
42 $form,
43 ],
44 FLUENTFORM_FRAMEWORK_UPGRADE,
45 'fluentform/slack_field_label_selection',
46 'Use fluentform/slack_field_label_selection instead of fluentform_slack_field_label_selection.'
47 );
48
49 $labels = apply_filters('fluentform/slack_field_label_selection', $labels, $settings, $form);
50
51 foreach ($inputs as $name => $input) {
52 if (empty($formData[$name])) {
53 continue;
54 }
55 if ('tabular_grid' == ArrayHelper::get($input, 'element', '')) {
56 $formData[$name] = Helper::getTabularGridFormatValue($formData[$name], $input, '<br />', ', ', 'markdown');
57 }
58 }
59 $formData = FormDataParser::parseData((object) $formData, $inputs, $form->id);
60
61 $slackTitle = ArrayHelper::get($settings, 'textTitle');
62
63 if ('' === $slackTitle) {
64 $title = 'New submission on ' . $form->title;
65 } else {
66 $title = $slackTitle;
67 }
68
69 $footerText = ArrayHelper::get($settings, 'footerText');
70 if ($footerText === '') {
71 $footerText = 'fluentform';
72 }
73
74 $fields = [];
75
76 foreach ($formData as $attribute => $value) {
77 $value = str_replace('<br />', "\n", $value);
78 $value = str_replace('&', '&amp;', $value);
79 $value = str_replace('<', '&lt;', $value);
80 $value = str_replace('>', '&gt;', $value);
81 if (! isset($labels[$attribute]) || empty($value)) {
82 continue;
83 }
84 $fields[] = [
85 'title' => $labels[$attribute],
86 'value' => $value,
87 'short' => false,
88 ];
89 }
90 $slackHook = ArrayHelper::get($settings, 'webhook');
91
92 $titleLink = admin_url(
93 'admin.php?page=fluent_forms&form_id='
94 . $form->id
95 . '&route=entries#/entries/'
96 . $entry->id
97 );
98
99 $body = [
100 'payload' => json_encode([
101 'attachments' => [
102 [
103 'color' => '#0078ff',
104 'fallback' => $title,
105 'title' => $title,
106 'title_link' => $titleLink,
107 'fields' => $fields,
108 'footer' => $footerText,
109 'ts' => round(microtime(true) * 1000),
110 ],
111 ],
112 ]),
113 ];
114
115 // SECURITY (FINDING-15): the webhook URL is admin-supplied and was fetched with no egress
116 // restriction — an SSRF with a logged status/error oracle (the response is written into the
117 // feed log). wp_safe_remote_post (below) blocks private/loopback ranges; additionally pin
118 // the host, since Slack incoming webhooks are always https://hooks.slack.com/... , so an
119 // arbitrary external host cannot be used as the oracle target either.
120 // COMPAT: filterable so a site using a Slack-compatible endpoint (Mattermost, Rocket.Chat,
121 // an internal relay) can keep an existing feed working without patching. Site PHP only —
122 // a form manager cannot reach it, so the default-deny posture is preserved.
123 $defaultHookHosts = ['hooks.slack.com'];
124 $allowedHookHosts = (array) apply_filters('fluentform/slack_allowed_webhook_hosts', $defaultHookHosts, $feed);
125 $allowedHookHosts = array_map('strtolower', array_filter($allowedHookHosts, 'is_string'));
126 // A filter returning nothing usable must not silently break every Slack feed — fall back
127 // to the official host so the default keeps working no matter what the filter returns.
128 if (!$allowedHookHosts) {
129 $allowedHookHosts = $defaultHookHosts;
130 }
131
132 $parsedHook = wp_parse_url($slackHook);
133 $hookHost = isset($parsedHook['host']) ? strtolower($parsedHook['host']) : '';
134 $hookScheme = isset($parsedHook['scheme']) ? strtolower($parsedHook['scheme']) : '';
135 if ('https' !== $hookScheme || !in_array($hookHost, $allowedHookHosts, true)) {
136 $message = sprintf(
137 // translators: %s is a comma-separated list of allowed webhook hosts.
138 __('Invalid Slack webhook URL. It must be an https:// URL on: %s', 'fluentform'),
139 implode(', ', $allowedHookHosts)
140 );
141 do_action('fluentform/integration_action_result', $feed, 'failed', $message);
142 return [
143 'status' => 'failed',
144 'message' => $message,
145 ];
146 }
147
148 $result = wp_safe_remote_post($slackHook, [
149 'method' => 'POST',
150 'timeout' => 30,
151 'redirection' => 5,
152 'httpversion' => '1.0',
153 'headers' => [],
154 'body' => $body,
155 'cookies' => [],
156 ]);
157
158 if (is_wp_error($result)) {
159 $status = 'failed';
160 $message = $result->get_error_message();
161 } else {
162 $message = $result['response'];
163 $status = 200 == $result['response']['code'] ? 'success' : 'failed';
164 }
165
166 if ('failed' == $status) {
167 do_action('fluentform/integration_action_result', $feed, 'failed', $message);
168 } else {
169 do_action('fluentform/integration_action_result', $feed, 'success', 'Submission notification has been successfully delivered to slack channel');
170 }
171
172 return [
173 'status' => $status,
174 'message' => $message,
175 ];
176 }
177 }
178