PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.8
Booking for Appointments and Events Calendar – Amelia v2.4.8
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Infrastructure / Services / Logger / LogDirectoryManager.php
ameliabooking / src / Infrastructure / Services / Logger Last commit date
JsonLineFormatter.php 6 days ago LogDirectoryManager.php 6 days ago LogRetentionCleanupService.php 6 days ago LogSanitizer.php 6 days ago MonologChannelLogger.php 6 days ago MonologLoggerFactory.php 6 days ago RequestIdProcessor.php 6 days ago
LogDirectoryManager.php
117 lines
1 <?php
2
3 namespace AmeliaBooking\Infrastructure\Services\Logger;
4
5 /**
6 * Class LogDirectoryManager
7 *
8 * Ensures a writable log directory under wp-content/uploads/amelia-logs, with
9 * access-protection files as defense-in-depth. Log filenames include a site-specific
10 * hash so they are harder to guess via direct URL.
11 *
12 * @package AmeliaBooking\Infrastructure\Services\Logger
13 */
14 final class LogDirectoryManager
15 {
16 public const DIRECTORY_NAME = 'amelia-logs';
17
18 public static function ensureDirectory(): string
19 {
20 $directory = self::resolveDirectory();
21
22 if (!is_dir($directory)) {
23 if (function_exists('wp_mkdir_p')) {
24 wp_mkdir_p($directory);
25 } else {
26 @mkdir($directory, 0755, true);
27 }
28 }
29
30 self::ensureAccessProtection($directory);
31
32 return $directory;
33 }
34
35 /**
36 * Filename stem used by RotatingFileHandler, e.g. amelia-a1b2c3d4e5f6-payment.log
37 * which rotates to amelia-a1b2c3d4e5f6-payment-YYYY-MM-DD.log.
38 */
39 public static function getLogBasename(string $channel): string
40 {
41 return self::getFilenamePrefix() . '-' . self::sanitizeChannel($channel) . '.log';
42 }
43
44 /**
45 * Stable per-site prefix: amelia-{12-char-hash}.
46 */
47 public static function getFilenamePrefix(): string
48 {
49 $secret = defined('AUTH_KEY') && is_string(AUTH_KEY) && AUTH_KEY !== ''
50 ? AUTH_KEY
51 : (defined('DB_NAME') && is_string(DB_NAME) ? DB_NAME : 'amelia');
52
53 return 'amelia-' . substr(hash('sha256', 'amelia-logs|' . $secret), 0, 12);
54 }
55
56 /**
57 * Keep channel segment filesystem-safe (lowercase [a-z0-9_-]).
58 */
59 public static function sanitizeChannel(string $channel): string
60 {
61 $sanitized = strtolower(preg_replace('/[^a-zA-Z0-9_-]/', '', $channel) ?? '');
62
63 return $sanitized !== '' ? $sanitized : 'app';
64 }
65
66 private static function resolveDirectory(): string
67 {
68 if (defined('AMELIA_UPLOADS_PATH') && is_string(AMELIA_UPLOADS_PATH) && AMELIA_UPLOADS_PATH !== '') {
69 return rtrim(str_replace('\\', '/', AMELIA_UPLOADS_PATH), '/') . '/' . self::DIRECTORY_NAME;
70 }
71
72 if (defined('WP_CONTENT_DIR') && is_string(WP_CONTENT_DIR) && WP_CONTENT_DIR !== '') {
73 return rtrim(str_replace('\\', '/', WP_CONTENT_DIR), '/') . '/uploads/' . self::DIRECTORY_NAME;
74 }
75
76 return dirname(__DIR__, 4) . '/storage/logs';
77 }
78
79 private static function ensureAccessProtection(string $directory): void
80 {
81 if (!@is_dir($directory) || !@is_writable($directory)) {
82 return;
83 }
84
85 $htaccess = $directory . '/.htaccess';
86
87 if (!file_exists($htaccess)) {
88 file_put_contents(
89 $htaccess,
90 "Deny from all\n<IfModule mod_authz_core.c>\n Require all denied\n</IfModule>\n"
91 );
92 }
93
94 $webConfig = $directory . '/web.config';
95
96 if (!file_exists($webConfig)) {
97 file_put_contents(
98 $webConfig,
99 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
100 . "<configuration>\n"
101 . " <system.webServer>\n"
102 . " <authorization>\n"
103 . " <deny users=\"*\" />\n"
104 . " </authorization>\n"
105 . " </system.webServer>\n"
106 . "</configuration>\n"
107 );
108 }
109
110 $index = $directory . '/index.php';
111
112 if (!file_exists($index)) {
113 file_put_contents($index, "<?php\n// Silence is golden.\n");
114 }
115 }
116 }
117