PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.14.8
Independent Analytics – WordPress Analytics Plugin v2.14.8
2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / IAWP / Click_Tracking / Click_Processing_Job.php
independent-analytics / IAWP / Click_Tracking Last commit date
Click.php 8 months ago Click_Processing_Job.php 5 months ago Config_File_Manager.php 1 year ago Link_Rule.php 1 year ago Link_Rule_Finder.php 9 months ago
Click_Processing_Job.php
129 lines
1 <?php
2
3 namespace IAWP\Click_Tracking;
4
5 use IAWPSCOPED\Carbon\CarbonImmutable;
6 use IAWP\Cron_Job;
7 use IAWP\Models\Visitor;
8 use IAWP\Payload_Validator;
9 use IAWP\Utils\Security;
10 use IAWPSCOPED\Illuminate\Support\Str;
11 use IAWPSCOPED\League\Uri\Uri;
12 /** @internal */
13 class Click_Processing_Job extends Cron_Job
14 {
15 protected $name = 'iawp_click_processing';
16 protected $interval = 'every_minute';
17 public function handle() : void
18 {
19 // Periodically recreate the config file
20 \IAWP\Click_Tracking\Config_File_Manager::recreate();
21 if (\IAWPSCOPED\iawp_is_free()) {
22 self::unschedule();
23 return;
24 }
25 $click_data_file = $this->get_click_data_file();
26 if ($click_data_file === null) {
27 return;
28 }
29 $job_file = $this->create_job_file($click_data_file);
30 if ($job_file === null) {
31 return;
32 }
33 $job_handle = \fopen($job_file, 'r');
34 if ($job_handle === \false) {
35 return;
36 }
37 // The first line for the PHP file is an exit statement to keep the contents private. This
38 // should be skipped when parsing the file.
39 if (\pathinfo($job_file, \PATHINFO_EXTENSION) === 'php') {
40 \fgets($job_handle);
41 // Skip first line
42 }
43 while (($json = \fgets($job_handle)) !== \false) {
44 $event = \json_decode($json, \true);
45 if (\is_null($event)) {
46 continue;
47 }
48 $payload_validator = Payload_Validator::new($event['payload'], $event['signature']);
49 if (!$payload_validator->is_valid() || \is_null($payload_validator->resource())) {
50 continue;
51 }
52 if (\is_string($event['href']) && !$this->is_valid_href($event['href'])) {
53 continue;
54 }
55 $event['href'] = \sanitize_url($event['href']);
56 $event['classes'] = Security::string($event['classes']);
57 $event['ids'] = Security::string($event['ids']);
58 $click = \IAWP\Click_Tracking\Click::new(['href' => $event['href'], 'classes' => $event['classes'], 'ids' => $event['ids'], 'resource_id' => $payload_validator->resource()['id'], 'visitor_id' => Visitor::fetch_visitor_id_by_hash($event['visitor_token']), 'created_at' => CarbonImmutable::createFromTimestamp($event['created_at'], 'utc')]);
59 $click->track();
60 }
61 \fclose($job_handle);
62 \unlink($job_file);
63 }
64 private function get_click_data_file() : ?string
65 {
66 $avoid_temporary_directory = \defined('IAWP_AVOID_TEMPORARY_DIRECTORY') ? \IAWP_AVOID_TEMPORARY_DIRECTORY === \true : \false;
67 if (!$avoid_temporary_directory) {
68 $text_file = Str::finish(\sys_get_temp_dir(), \DIRECTORY_SEPARATOR) . "iawp-click-data.txt";
69 if (\is_file($text_file) && \is_readable($text_file) && \is_writable($text_file)) {
70 return $text_file;
71 }
72 }
73 $php_file = \IAWPSCOPED\iawp_path_to('iawp-click-data.php');
74 if (\is_file($php_file)) {
75 return $php_file;
76 }
77 return null;
78 }
79 private function create_job_file(string $file) : ?string
80 {
81 if (!\is_readable($file) || !\is_writable($file)) {
82 return null;
83 }
84 $job_id = \rand();
85 $extension = \pathinfo($file, \PATHINFO_EXTENSION);
86 $job_file = Str::finish(\dirname($file), \DIRECTORY_SEPARATOR) . "iawp-click-data-{$job_id}.{$extension}";
87 $was_renamed = \rename($file, $job_file);
88 if (!$was_renamed || !\is_file($job_file)) {
89 return null;
90 }
91 return $job_file;
92 }
93 private function is_valid_href(string $href) : bool
94 {
95 if ($this->has_injection_attempt($href)) {
96 return \false;
97 }
98 $uri = Uri::createFromString($href);
99 $scheme = $uri->getScheme();
100 if ($scheme === null) {
101 return \false;
102 }
103 $is_http = $scheme === 'http' || $scheme === 'https';
104 if ($is_http && \filter_var($href, \FILTER_VALIDATE_URL) === \false) {
105 return \false;
106 }
107 return \true;
108 }
109 /**
110 * The goal of this method is not security. That's handled by the secure and robust functions
111 * that PHP and WordPress provide. The goal of this function is to detect when someone tried
112 * to use SQL injection so we can ignore the spam clicks.
113 *
114 * @param string $string
115 *
116 * @return bool
117 */
118 private function has_injection_attempt(string $string) : bool
119 {
120 $patterns = ['/select\\s.*\\sfrom/i', '/waitfor\\sdelay/i', '/pg_sleep/i'];
121 foreach ($patterns as $pattern) {
122 if (\preg_match($pattern, $string)) {
123 return \true;
124 }
125 }
126 return \false;
127 }
128 }
129