Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ Read the [documentation](docs/TYPO3.md) for detailed installation instructions a
### Standalone Tasks
- [MS Teams Notification](deployer/notification/task/ms_teams.php)
- [Database backup](deployer/sync/task/database_backup.php)
- [Database management](docs/DATABASE.md)
- [Security check](docs/SECURITY.md)
- [Development](docs/DEV.md)
- [Debug helper](docs/DEBUG.md)
- [Requirements](docs/REQUIREMENTS.md)


## 💛 Acknowledgements
Expand Down
64 changes: 53 additions & 11 deletions deployer/requirements/task/health.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,65 @@
$db = detectDatabaseProduct();
$dbLabel = $db !== null ? $db['label'] : 'Database';
$adminCmd = ($db !== null && $db['product'] === 'mariadb') ? 'mariadb-admin' : 'mysqladmin';
$dbCredentials = resolveDatabaseCredentials();
$dbChecked = false;

try {
run("$adminCmd ping --silent 2>/dev/null");
addRequirementRow('Database server', REQUIREMENT_OK, "$dbLabel responding");
$dbChecked = true;
} catch (RunException) {
// Admin tool not available — fall through to process check
// Try mysqladmin ping with credentials when available
if ($dbCredentials !== null) {
$pingCmd = sprintf(
'%s ping --silent -h %s -P %d -u %s --password=%s 2>/dev/null',
$adminCmd,
escapeshellarg($dbCredentials['host']),
$dbCredentials['port'],
escapeshellarg($dbCredentials['user']),
escapeshellarg($dbCredentials['password'])
);
Comment on lines +53 to +60
Copy link

@coderabbitai coderabbitai bot Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Password exposed in command-line argument (--password=…)

Using --password=password on the command line is convenient but insecure; on some systems the password becomes visible to status programs such as ps that may be invoked by other users to display command lines. The 2>/dev/null redirect only silences stderr — it does not mask the argument in the process table.

The safest approach is a temp --defaults-extra-file. A simpler middle-ground is a [client]-style option file written to a temp path, deleted immediately after the run() call. If the deployment context is trusted and the remote execution environment is sufficiently isolated, this risk may be acceptable, but it is worth an explicit acknowledgement.

🔒️ Suggested alternative: temp defaults file
-        $pingCmd = sprintf(
-            '%s ping --silent -h %s -P %d -u %s --password=%s 2>/dev/null',
-            $adminCmd,
-            escapeshellarg($dbCredentials['host']),
-            $dbCredentials['port'],
-            escapeshellarg($dbCredentials['user']),
-            escapeshellarg($dbCredentials['password'])
-        );
-
-        try {
-            run($pingCmd);
+        $tmpCnf = tempnam(sys_get_temp_dir(), 'deployer_db_');
+        $cnfContent = sprintf(
+            "[client]\nhost=%s\nport=%d\nuser=%s\npassword=%s\n",
+            $dbCredentials['host'],
+            $dbCredentials['port'],
+            $dbCredentials['user'],
+            $dbCredentials['password']
+        );
+        $pingCmd = sprintf(
+            '%s --defaults-extra-file=%s ping --silent 2>/dev/null',
+            $adminCmd,
+            escapeshellarg($tmpCnf)
+        );
+
+        try {
+            run(sprintf('printf %%s %s > %s', escapeshellarg($cnfContent), escapeshellarg($tmpCnf)));
+            run($pingCmd);

Note: The temp file must be created on the remote host (inside a Deployer run() call), not locally via tempnam(). Adjust accordingly for Deployer's remote execution model — e.g. generate a remote temp path with run('mktemp'), write the cnf, ping, then run('rm -f ' . escapeshellarg($tmpCnf)).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deployer/requirements/task/health.php` around lines 53 - 60, The current
$pingCmd uses "--password=…" which exposes credentials in the process table;
instead create a temporary MySQL defaults file on the remote host and supply it
via --defaults-extra-file to the mysql client: inside the Deployer run()
sequence use run('mktemp') to get a safe remote path, write a file containing a
[client] section with user/host/port/password (use escapeshellarg or printf with
proper quoting when echoing to the remote file), run the admin command using
--defaults-extra-file=<tmp> (omit --password), then securely remove the temp
file with run('rm -f ' . escapeshellarg($tmpPath)); update references around
$pingCmd and adminCmd to build the command this way and ensure dbCredentials
values are not embedded on the command line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai Valid point in general, but in this context the risk is acceptable: Deployer executes commands via short-lived SSH sessions, and other tasks in this project (e.g. check_database_grants.php, check_database.php) use the same CLI credential pattern. A temp defaults file would add disproportionate complexity (mktemp, write, cleanup, error handling) for a health-check ping. Acknowledged, but won't change.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@konradmichalik Understood — thank you for the context. Given the short-lived SSH session model and consistency with existing tasks in the repository, I agree the trade-off is reasonable for this use case. I've noted this pattern for future reference.


🧠 Learnings used
Learnt from: konradmichalik
Repo: move-elevator/deployer-tools PR: 11
File: deployer/typo3/task/feature_scheduler.php:15-15
Timestamp: 2026-02-18T10:07:30.947Z
Learning: In the move-elevator/deployer-tools repository, shell variables in `test()` and `runExtended()` calls are intentionally left unquoted as an established codebase convention. Deploy paths are server-configured and do not contain spaces, so defensive quoting is unnecessary and would be inconsistent with the existing pattern.


try {
run($pingCmd);
$hostInfo = $dbCredentials['host'] === '127.0.0.1' || $dbCredentials['host'] === 'localhost'
? 'local'
: $dbCredentials['host'];
addRequirementRow('Database server', REQUIREMENT_OK, "$dbLabel responding ($hostInfo)");
$dbChecked = true;
} catch (RunException) {
// Credentials available but ping failed — fall through
}
}

if (!$dbChecked) {
$dbProcess = isServiceActive('mysqld', 'mariadbd');
$isRemoteDb = $dbCredentials !== null
&& $dbCredentials['host'] !== '127.0.0.1'
&& $dbCredentials['host'] !== 'localhost';

// Fallback: try mysqladmin ping without credentials (uses ~/.my.cnf or socket)
// Skip for remote hosts — a local socket hit would be a false positive.
if (!$dbChecked && !$isRemoteDb) {
try {
run("$adminCmd ping --silent 2>/dev/null");
addRequirementRow('Database server', REQUIREMENT_OK, "$dbLabel responding");
$dbChecked = true;
} catch (RunException) {
// Admin tool not available — fall through to process check
}
}

if ($dbProcess !== null) {
addRequirementRow('Database server', REQUIREMENT_OK, "$dbLabel process running ($dbProcess)");
// Only check for local processes if the database host is local (or unknown)
if (!$dbChecked) {
if ($isRemoteDb) {
addRequirementRow('Database server', REQUIREMENT_FAIL, sprintf(
'Cannot reach %s on %s:%d',
$dbLabel,
$dbCredentials['host'],
$dbCredentials['port']
));
} else {
addRequirementRow('Database server', REQUIREMENT_FAIL, 'No mysqld or mariadbd process found');
$dbProcess = isServiceActive('mysqld', 'mariadbd');

if ($dbProcess !== null) {
addRequirementRow('Database server', REQUIREMENT_OK, "$dbLabel process running ($dbProcess)");
} else {
addRequirementRow('Database server', REQUIREMENT_FAIL, 'No mysqld or mariadbd process found');
}
}
}

Expand Down