PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.14.0
Independent Analytics – WordPress Analytics Plugin v2.14.0
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 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 9 months ago Click_Processing_Job.php 10 months ago Config_File_Manager.php 1 year ago Link_Rule.php 1 year ago Link_Rule_Finder.php 10 months ago
Click_Processing_Job.php
128 lines
1 <?php
2
3 namespace IAWP\Click_Tracking;
4
5 use IAWP\Cron_Job;
6 use IAWP\Models\Visitor;
7 use IAWP\Payload_Validator;
8 use IAWP\Utils\Security;
9 use IAWPSCOPED\Illuminate\Support\Str;
10 use IAWPSCOPED\League\Uri\Uri;
11 /** @internal */
12 class Click_Processing_Job extends Cron_Job
13 {
14 protected $name = 'iawp_click_processing';
15 protected $interval = 'every_minute';
16 public function handle() : void
17 {
18 // Periodically recreate the config file
19 \IAWP\Click_Tracking\Config_File_Manager::recreate();
20 if (\IAWPSCOPED\iawp_is_free()) {
21 self::unschedule();
22 return;
23 }
24 $click_data_file = $this->get_click_data_file();
25 if ($click_data_file === null) {
26 return;
27 }
28 $job_file = $this->create_job_file($click_data_file);
29 if ($job_file === null) {
30 return;
31 }
32 $job_handle = \fopen($job_file, 'r');
33 if ($job_handle === \false) {
34 return;
35 }
36 // The first line for the PHP file is an exit statement to keep the contents private. This
37 // should be skipped when parsing the file.
38 if (\pathinfo($job_file, \PATHINFO_EXTENSION) === 'php') {
39 \fgets($job_handle);
40 // Skip first line
41 }
42 while (($json = \fgets($job_handle)) !== \false) {
43 $event = \json_decode($json, \true);
44 if (\is_null($event)) {
45 continue;
46 }
47 $payload_validator = Payload_Validator::new($event['payload'], $event['signature']);
48 if (!$payload_validator->is_valid() || \is_null($payload_validator->resource())) {
49 continue;
50 }
51 if (\is_string($event['href']) && !$this->is_valid_href($event['href'])) {
52 continue;
53 }
54 $event['href'] = \sanitize_url($event['href']);
55 $event['classes'] = Security::string($event['classes']);
56 $event['ids'] = Security::string($event['ids']);
57 $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' => \DateTime::createFromFormat('U', $event['created_at'])]);
58 $click->track();
59 }
60 \fclose($job_handle);
61 \unlink($job_file);
62 }
63 private function get_click_data_file() : ?string
64 {
65 $avoid_temporary_directory = \defined('IAWP_AVOID_TEMPORARY_DIRECTORY') ? \IAWP_AVOID_TEMPORARY_DIRECTORY === \true : \false;
66 if (!$avoid_temporary_directory) {
67 $text_file = Str::finish(\sys_get_temp_dir(), \DIRECTORY_SEPARATOR) . "iawp-click-data.txt";
68 if (\is_file($text_file) && \is_readable($text_file) && \is_writable($text_file)) {
69 return $text_file;
70 }
71 }
72 $php_file = \IAWPSCOPED\iawp_path_to('iawp-click-data.php');
73 if (\is_file($php_file)) {
74 return $php_file;
75 }
76 return null;
77 }
78 private function create_job_file(string $file) : ?string
79 {
80 if (!\is_readable($file) || !\is_writable($file)) {
81 return null;
82 }
83 $job_id = \rand();
84 $extension = \pathinfo($file, \PATHINFO_EXTENSION);
85 $job_file = Str::finish(\dirname($file), \DIRECTORY_SEPARATOR) . "iawp-click-data-{$job_id}.{$extension}";
86 $was_renamed = \rename($file, $job_file);
87 if (!$was_renamed || !\is_file($job_file)) {
88 return null;
89 }
90 return $job_file;
91 }
92 private function is_valid_href(string $href) : bool
93 {
94 if ($this->has_injection_attempt($href)) {
95 return \false;
96 }
97 $uri = Uri::createFromString($href);
98 $scheme = $uri->getScheme();
99 if ($scheme === null) {
100 return \false;
101 }
102 $is_http = $scheme === 'http' || $scheme === 'https';
103 if ($is_http && \filter_var($href, \FILTER_VALIDATE_URL) === \false) {
104 return \false;
105 }
106 return \true;
107 }
108 /**
109 * The goal of this method is not security. That's handled by the secure and robust functions
110 * that PHP and WordPress provide. The goal of this function is to detect when someone tried
111 * to use SQL injection so we can ignore the spam clicks.
112 *
113 * @param string $string
114 *
115 * @return bool
116 */
117 private function has_injection_attempt(string $string) : bool
118 {
119 $patterns = ['/select\\s.*\\sfrom/i', '/waitfor\\sdelay/i', '/pg_sleep/i'];
120 foreach ($patterns as $pattern) {
121 if (\preg_match($pattern, $string)) {
122 return \true;
123 }
124 }
125 return \false;
126 }
127 }
128