-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathembed.php
More file actions
155 lines (130 loc) · 5.39 KB
/
embed.php
File metadata and controls
155 lines (130 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<?php
require_once __DIR__ . '/func.php';
use PhpProxyHunter\Server;
$forbidden = false;
// Check if the X-User-Token and X-Serial-Number headers are set
$userToken = isset($_SERVER['HTTP_X_USER_TOKEN']) ? $_SERVER['HTTP_X_USER_TOKEN'] : null;
$serialNumber = isset($_SERVER['HTTP_X_SERIAL_NUMBER']) ? $_SERVER['HTTP_X_SERIAL_NUMBER'] : null;
Server::allowCors();
if ($userToken && $serialNumber) {
$userFile = __DIR__ . '/data/' . $userToken . '.json';
if (file_exists($userFile)) {
$read = read_file($userFile);
if ($read !== false) {
$json = safe_json_decode($read);
if ($json) {
$validUntilStr = isset($json['valid_until']) ? $json['valid_until'] : '';
$validUntil = DateTime::createFromFormat(DateTime::ATOM, $validUntilStr);
if ($validUntil === false) {
// Invalid date format
$forbidden = true;
} else {
// Get current date and time
$now = new DateTime();
$forbidden = $now > $validUntil;
if (!$forbidden) {
// Extract devices array
$devices = isset($json['devices']) ? $json['devices'] : [];
// If serial number exists in devices array, allow access; otherwise, return 403
$forbidden = !in_array($serialNumber, $devices, true);
}
}
}
}
}
} else {
// Check if the captcha session is empty or false
if (empty($_SESSION['captcha']) || !$_SESSION['captcha']) {
// Return 403 forbidden when captcha is not resolved
$forbidden = true;
} else {
$last_check_captcha = isset($_SESSION['last_captcha_check']) ? $_SESSION['last_captcha_check'] : '';
// Convert RFC 3339 date string to a Unix timestamp
$last_check_timestamp = strtotime($last_check_captcha) ?: 0;
// Get the current Unix timestamp
$current_timestamp = time();
// Calculate the Unix timestamp for 1 hour ago
$one_hour_ago = $current_timestamp - 3600;
// Compare if the last check captcha was more than 1 hour ago
if ($last_check_timestamp <= $one_hour_ago) {
// The last check captcha was more than 1 hour ago
$forbidden = true;
unset($_SESSION['captcha']);
}
}
}
if ($forbidden) {
header('Content-Type: application/json; charset=utf-8');
http_response_code(403);
exit(json_encode(['error' => true, 'message' => 'Access forbidden']));
}
$request = parseQueryOrPostBody();
$file = isset($request['file']) ? rawurldecode(trim($request['file'])) : null;
if (empty($file)) {
header('Content-Type: application/json; charset=utf-8');
http_response_code(400);
exit(json_encode(['error' => true, 'message' => 'Missing file parameter']));
}
// Exclude/ignore files matching these glob patterns (denylist)
$excludedGlobs = ['*.env', '*.env.*', '*.php', '*.sh', '*.bat', '*.exe', '*.bak', '*.key', '*.pem', '*.crt', '*.htaccess', 'composer.*', 'package*.json', 'node_modules*', 'vendor*', '.git*', '*.bak', '*.lock', '*.zip', '*.tar*', '*.gz', '*.7z', '*.rar', '*.db', '*.sqlite', '*.py', '*.ts', '*.js', '*.cjs', '*.mjs', '*.md', '*.yml', '*.yaml', '*.log.bak'];
$baseFile = basename($file);
$denied = false;
foreach ($excludedGlobs as $pattern) {
if (fnmatch($pattern, $baseFile, FNM_CASEFOLD)) {
$denied = true;
break;
}
}
if ($denied) {
http_response_code(403);
header('Content-Type: application/json; charset=utf-8');
exit(json_encode(['error' => 'Access denied.']));
}
$real_file = false;
// If the incoming path starts with a slash, interpret it as project-root
// relative (do not treat it as a system absolute path). This lets the
// frontend use "/tmp/..." to refer to the project's tmp folder, and also
// allows other project-root paths like "/logs/..." to be resolved safely.
if (strlen($file) > 0 && $file[0] === '/') {
// Remove leading slash and resolve under project dir
$mapped = __DIR__ . '/' . ltrim($file, '/\\');
$real_file = realpath($mapped);
} else {
// If no leading slash, try resolving as given (relative path) via basename fallback.
$real_file = realpath(__DIR__ . '/' . $baseFile);
}
if ($real_file && file_exists($real_file)) {
// Determine the file extension
$fileExtension = pathinfo($real_file, PATHINFO_EXTENSION);
// Check if the file extension is allowed (json, txt, or log)
if (in_array($fileExtension, ['json', 'txt', 'log'], true)) {
// Explicitly set the Content-Type based on the file extension
$contentType = ($fileExtension === 'json') ? 'application/json' : 'text/plain';
// Set the appropriate Content-Type header
header("Content-Type: $contentType");
// Cached filename map with their expiration times in minutes
$cachedFilenameMap = [
'working.json' => 5,
];
// Set caching headers if the file is in the cached filename map
if (array_key_exists($baseFile, $cachedFilenameMap)) {
$expirationMinutes = $cachedFilenameMap[$baseFile];
header('Cache-Control: public, max-age=' . ($expirationMinutes * 60));
} else {
// No caching for other files
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
}
// Read the file and echo its contents
readfile($real_file);
} else {
// Invalid file type
http_response_code(400);
echo 'Invalid file type. Only JSON, text, and log files are allowed.';
}
} else {
// File not found or inaccessible
http_response_code(404);
echo "$file not found";
}