security: FTP password not masked in AjaxController maskSecrets/mergeExistingSecrets #169

Open
opened 2026-06-29 14:20:05 +00:00 by jmiller · 1 comment
Owner

Summary

AjaxController::maskSecrets() and AjaxController::mergeExistingSecrets() define secret field mappings for sftp, s3, and google_drive remote types — but ftp is completely missing. When a remote destination of type ftp is loaded via the AJAX listRemotes endpoint, the FTP password is returned in cleartext to the browser.

Location

src/Controller/AjaxController.php — lines 1142-1146 and 1164-1168:

$secrets = [
    'sftp'         => ['password', 'passphrase', 'key_data'],
    's3'           => ['secret_key'],
    'google_drive' => ['client_secret', 'refresh_token'],
    // 'ftp' is missing — password returned in cleartext
];

What to do

  • Add 'ftp' => ['password'] to the $secrets array in maskSecrets()
  • Add 'ftp' => ['password'] to the $secrets array in mergeExistingSecrets()
  • Verify the API controller's profiles() endpoint also masks FTP credentials (it does via a separate $sensitiveFields list that includes ftp_password)

Why

This is a credential exposure vulnerability. Any admin user who can view the remotes list sees FTP passwords in the DOM/network response. The other three remote types correctly mask their secrets.

## Summary `AjaxController::maskSecrets()` and `AjaxController::mergeExistingSecrets()` define secret field mappings for `sftp`, `s3`, and `google_drive` remote types — but **`ftp` is completely missing**. When a remote destination of type `ftp` is loaded via the AJAX `listRemotes` endpoint, the FTP password is returned in cleartext to the browser. ## Location `src/Controller/AjaxController.php` — lines 1142-1146 and 1164-1168: ```php $secrets = [ 'sftp' => ['password', 'passphrase', 'key_data'], 's3' => ['secret_key'], 'google_drive' => ['client_secret', 'refresh_token'], // 'ftp' is missing — password returned in cleartext ]; ``` ## What to do - [ ] Add `'ftp' => ['password']` to the `$secrets` array in `maskSecrets()` - [ ] Add `'ftp' => ['password']` to the `$secrets` array in `mergeExistingSecrets()` - [ ] Verify the API controller's `profiles()` endpoint also masks FTP credentials (it does via a separate `$sensitiveFields` list that includes `ftp_password`) ## Why This is a credential exposure vulnerability. Any admin user who can view the remotes list sees FTP passwords in the DOM/network response. The other three remote types correctly mask their secrets.
jmiller added the component: remote label 2026-06-29 14:20:05 +00:00
Author
Owner

Branch created: feature/169-security-ftp-password-not-masked-in-ajax

git fetch origin
git checkout feature/169-security-ftp-password-not-masked-in-ajax
Branch created: [`feature/169-security-ftp-password-not-masked-in-ajax`](https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteBackup/src/branch/feature/169-security-ftp-password-not-masked-in-ajax) ```bash git fetch origin git checkout feature/169-security-ftp-password-not-masked-in-ajax ```
Sign in to join this conversation.