Skip to content

Commit bb83f4e

Browse files
authored
Add Arch Linux server support and fix package sanitization (#7531)
2 parents a088a41 + 0890079 commit bb83f4e

File tree

4 files changed

+100
-3
lines changed

4 files changed

+100
-3
lines changed

app/Actions/Server/CheckUpdates.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ class CheckUpdates
1313

1414
public function handle(Server $server)
1515
{
16+
$osId = 'unknown';
17+
$packageManager = null;
18+
1619
try {
1720
if ($server->serverStatus() === false) {
1821
return [
@@ -93,6 +96,16 @@ public function handle(Server $server)
9396
$out['osId'] = $osId;
9497
$out['package_manager'] = $packageManager;
9598

99+
return $out;
100+
case 'pacman':
101+
// Sync database first, then check for updates
102+
// Using -Sy to refresh package database before querying available updates
103+
instant_remote_process(['pacman -Sy'], $server);
104+
$output = instant_remote_process(['pacman -Qu 2>/dev/null'], $server);
105+
$out = $this->parsePacmanOutput($output);
106+
$out['osId'] = $osId;
107+
$out['package_manager'] = $packageManager;
108+
96109
return $out;
97110
default:
98111
return [
@@ -219,4 +232,45 @@ private function parseAptOutput(string $output): array
219232
'updates' => $updates,
220233
];
221234
}
235+
236+
private function parsePacmanOutput(string $output): array
237+
{
238+
$updates = [];
239+
$unparsedLines = [];
240+
$lines = explode("\n", $output);
241+
242+
foreach ($lines as $line) {
243+
if (empty($line)) {
244+
continue;
245+
}
246+
// Format: package current_version -> new_version
247+
if (preg_match('/^(\S+)\s+(\S+)\s+->\s+(\S+)$/', $line, $matches)) {
248+
$updates[] = [
249+
'package' => $matches[1],
250+
'current_version' => $matches[2],
251+
'new_version' => $matches[3],
252+
'architecture' => 'unknown',
253+
'repository' => 'unknown',
254+
];
255+
} else {
256+
// Log unmatched lines for debugging purposes
257+
$unparsedLines[] = $line;
258+
}
259+
}
260+
261+
$result = [
262+
'total_updates' => count($updates),
263+
'updates' => $updates,
264+
];
265+
266+
// Include unparsed lines in the result for debugging if any exist
267+
if (! empty($unparsedLines)) {
268+
$result['unparsed_lines'] = $unparsedLines;
269+
\Illuminate\Support\Facades\Log::debug('Pacman output contained unparsed lines', [
270+
'unparsed_lines' => $unparsedLines,
271+
]);
272+
}
273+
274+
return $result;
275+
}
222276
}

app/Actions/Server/InstallDocker.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,15 @@ private function getGenericDockerInstallCommand(): string
160160
{
161161
return "curl https://releases.rancher.com/install-docker/{$this->dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$this->dockerVersion}";
162162
}
163+
164+
private function getArchDockerInstallCommand(): string
165+
{
166+
// Use -Syu to perform full system upgrade before installing Docker
167+
// Partial upgrades (-Sy without -u) are discouraged on Arch Linux
168+
// as they can lead to broken dependencies and system instability
169+
// Use --needed to skip reinstalling packages that are already up-to-date (idempotent)
170+
return 'pacman -Syu --noconfirm --needed docker docker-compose && '.
171+
'systemctl enable docker.service && '.
172+
'systemctl start docker.service';
173+
}
163174
}

app/Actions/Server/InstallPrerequisites.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ public function handle(Server $server)
4646
'command -v git >/dev/null || zypper install -y git',
4747
'command -v jq >/dev/null || zypper install -y jq',
4848
]);
49+
} elseif ($supported_os_type->contains('arch')) {
50+
// Use -Syu for full system upgrade to avoid partial upgrade issues on Arch Linux
51+
// --needed flag skips packages that are already installed and up-to-date
52+
$command = $command->merge([
53+
"echo 'Installing Prerequisites for Arch Linux...'",
54+
'pacman -Syu --noconfirm --needed curl wget git jq',
55+
]);
4956
} else {
5057
throw new \Exception('Unsupported OS type for prerequisites installation');
5158
}

app/Actions/Server/UpdatePackage.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,43 @@ public function handle(Server $server, string $osId, ?string $package = null, ?s
2020
'error' => 'Server is not reachable or not ready.',
2121
];
2222
}
23+
24+
// Validate that package name is provided when not updating all packages
25+
if (! $all && ($package === null || $package === '')) {
26+
return [
27+
'error' => "Package name required when 'all' is false.",
28+
];
29+
}
30+
31+
// Sanitize package name to prevent command injection
32+
// Only allow alphanumeric characters, hyphens, underscores, periods, plus signs, and colons
33+
// These are valid characters in package names across most package managers
34+
$sanitizedPackage = '';
35+
if ($package !== null && ! $all) {
36+
if (! preg_match('/^[a-zA-Z0-9._+:-]+$/', $package)) {
37+
return [
38+
'error' => 'Invalid package name. Package names can only contain alphanumeric characters, hyphens, underscores, periods, plus signs, and colons.',
39+
];
40+
}
41+
$sanitizedPackage = escapeshellarg($package);
42+
}
43+
2344
switch ($packageManager) {
2445
case 'zypper':
2546
$commandAll = 'zypper update -y';
26-
$commandInstall = 'zypper install -y '.$package;
47+
$commandInstall = 'zypper install -y '.$sanitizedPackage;
2748
break;
2849
case 'dnf':
2950
$commandAll = 'dnf update -y';
30-
$commandInstall = 'dnf update -y '.$package;
51+
$commandInstall = 'dnf update -y '.$sanitizedPackage;
3152
break;
3253
case 'apt':
3354
$commandAll = 'apt update && apt upgrade -y';
34-
$commandInstall = 'apt install -y '.$package;
55+
$commandInstall = 'apt install -y '.$sanitizedPackage;
56+
break;
57+
case 'pacman':
58+
$commandAll = 'pacman -Syu --noconfirm';
59+
$commandInstall = 'pacman -S --noconfirm '.$sanitizedPackage;
3560
break;
3661
default:
3762
return [

0 commit comments

Comments
 (0)