php2shell-scanner.git

php2shell-scanner

~ Check PHP2Shell (CVE-2026-109618, CVE-2026-109619) from inside your server: distribution backports, missed restarts and interim mitigations, verdicts computed in the browser.
git clone https://github.com/debuman/php2shell-scanner.git

site/lib/messages.js (164881B)

      1 // User-facing text of the verdict engine (judge.js, parse.js), in every supported language.
      2 // A message is either a string with {name} placeholders or a function (vars) => string
      3 // for plurals and conditional wording. Both languages must have the same keys.
      4 
      5 export const LANGS = ['en', 'ko'];
      6 
      7 // "1 file", "3 files"
      8 function count(n, one, many) {
      9   return `${n} ${n === 1 ? one : many}`;
     10 }
     11 
     12 const en = {
     13   // Verdicts
     14   'verdict.title.patched': 'This PHP has the fix',
     15   'verdict.title.vulnerable': 'This PHP is vulnerable',
     16   'verdict.title.pending': 'Vulnerable, and your distribution has no fix yet',
     17   'verdict.title.restart': 'Fixed on disk, still vulnerable in memory',
     18   'verdict.title.unknown': 'Not enough to decide',
     19   'verdict.label.patched': 'Patched',
     20   'verdict.label.vulnerable': 'Vulnerable',
     21   'verdict.label.pending': 'Fix pending',
     22   'verdict.label.restart': 'Restart needed',
     23   'verdict.label.unknown': 'Undetermined',
     24 
     25   // Facts
     26   'fact.phpVersion': 'PHP version',
     27   'fact.installedVersion': 'Installed package version',
     28   'fact.sapi': 'SAPI',
     29   'fact.sapiInferred': '{sapi} (inferred from running processes)',
     30   'fact.distribution': 'Distribution',
     31   'fact.webServer': 'Web server',
     32   'fact.packageChecked': 'Package checked',
     33   'fact.otherBuilds': 'Other installed PHP builds',
     34 
     35   // Distribution and SAPI labels
     36   'distro.rhel': 'RHEL family',
     37   'distro.rhelRelease': 'RHEL family {release}',
     38   'distro.upstream': 'Upstream build (self-compiled or official Docker image)',
     39   'distro.other': 'Other',
     40   'distro.unknown': 'Unknown',
     41   'distro.inferred': '{label} (inferred from the version string)',
     42   'distro.kernel': '{label} (inferred from the kernel version)',
     43   'distro.serverHeader': '{label} (inferred from the Server header)',
     44   'distro.derived': '{label} (based on {parent})',
     45   'distro.manual': '{label} (chosen manually)',
     46   'sapi.cli': 'Command line (CLI)',
     47 
     48   // One of several separate outputs in the input
     49   'part.probe': 'PHP probe (PHP {version})',
     50   'part.probeNoVersion': 'PHP probe output',
     51   'part.header': 'Header X-Powered-By: PHP/{version}',
     52   'part.cli': '-v output (PHP {version}, {sapi})',
     53   'part.server': 'Server script output ({system})',
     54   'part.version': 'Version string {version}',
     55 
     56   // Reasons: upstream
     57   'reason.noVersion': 'No PHP version was found.',
     58   'reason.preRelease': '{version} is a pre-release (alpha, beta, RC or a -dev snapshot), so it is not covered by the fixed releases. Check its release notes or commits to see whether both CVE fixes are included.',
     59   'reason.newerBranch': 'PHP {branch} is newer than every branch in the table of fixed releases ({branches}). Check its release notes to see whether both CVE fixes are included.',
     60   'reason.upstreamPatched': 'PHP {version} is at or above the upstream fixed release {fixed}.',
     61   'reason.upstreamVulnerable': 'PHP {version} is older than the upstream fixed release {fixed}.',
     62   'reason.predates54': 'PHP {version} predates the published attack path (session upload progress, PHP 5.4 and later). However, the official advisory does not state a lower bound for the affected versions, and this version reached end of life long ago and has many other vulnerabilities.',
     63   'reason.eol': 'PHP {branch} has reached end of life (EOL) and has no official fix release, so an upstream build of it is vulnerable. Only a distribution package with the fixes backported can have them.',
     64   'reason.osBlocked': 'open_basedir kept the probe from reading the OS release and the installed package list, so it cannot tell whether PHP {version} is a distribution package with the fixes backported under the same version number. Run the server script, or pick your distribution in the Distribution list and press Run check again.',
     65   'reason.osUnread': 'The probe could not read the OS release, so it cannot tell whether PHP {version} is a distribution package with the fixes backported under the same version number, as the RHEL family and others do. Run the server script, or pick your distribution in the Distribution list and press Run check again.',
     66   'reason.couldBeDistro': 'PHP {version} may also be a distribution package ({releases}) with the fixes backported under the same version number, so the version alone cannot settle it. Run the server script, which reads the installed packages and their changelogs. On Debian and Ubuntu the PHP probe can read the package version as well.',
     67 
     68   // Reasons: Debian and Ubuntu
     69   'reason.noDataChangelogFixed': "{reason} However, the installed package's changelog records fixes for both CVEs, so it is treated as fixed.",
     70   'reason.debReleaseUnknown': 'The {family} release is unknown, so the backport could not be checked. Pick it in the Distribution list and press Run check again.',
     71   'reason.debNoRelease': 'The distribution data has no record for {family} {release}. It may have reached end of life, or no advisory may have been published yet. Check the {family} security advisories yourself.',
     72   'reason.debNoPackage': 'The distribution data has no record for the {src} package in {family} {release}. The package may come from another repository, or the advisory may not be out yet.',
     73   'reason.debBackported': '{family} has backported the fixes for both CVEs to {src} {version}. The fix is included even though the version number ({upstream}) is lower.',
     74   'reason.debOlder': '{src} {version} is older than the {family} fixed version {fixed}.',
     75   'reason.debOlderUpstream': 'PHP {version} is older than the upstream version of the {family} {release} fixed package for {src} ({fixed}), so it does not have the fix.',
     76   'reason.debNeedPackage': 'PHP_VERSION ({version}) is the upstream version of the {src} package in {family} {release}, and it stays the same when the fix is installed, so the version alone cannot show whether this server has it. Run the server script, or the PHP probe where it can read the installed package list (/var/lib/dpkg/status).',
     77   'reason.debPending': "There is no fixed package for {src} in {family} {release} yet. Watch your distribution's security advisories and update as soon as the fix is published.",
     78   'reason.debNoCveData': 'The distribution data has no record for at least one of the two CVEs.',
     79   'reason.restartInstalledFixed': 'The installed package ({installed}) contains the fixes for both CVEs, but the running PHP is still the old version ({running}). It looks like the service was not restarted after the update.',
     80   'reason.installedNewer': 'The installed package ({installed}) is newer than the running PHP ({running}). It looks like the service was not restarted after the update.',
     81   'reason.installedOlderUnfixed': 'The running PHP ({running}) has the fix, but the installed package ({installed}) is older and does not: the package was downgraded, or replaced by another build, after the service started. The next restart of the service loads the vulnerable build (for PHP-FPM, so does a reload). Install the fixed package again before the service restarts.',
     82   'reason.foreignRevision': "{version} has the same upstream version as the {src} package of {family} {release}, but its revision is not one that {family} builds, so it comes from another repository (a PPA or another vendor). Sorting above the fixed version does not mean it has the backported fixes. Check the advisories of whoever supplies this package, or install the {family} package.",
     83 
     84   // Reasons: RHEL family
     85   'reason.rhelChangelogFixed': 'The changelog of {pkg}, the package of this PHP, records the fix for {cves}.',
     86   'reason.rhelChangelogMissing': 'The changelog of {pkg}, the package of this PHP, has no record of a fix for {cves}. Most likely the package has not been updated yet.',
     87   'reason.rhelNotAffected': 'Red Hat lists {cves} as not affected for RHEL {release}.',
     88   'reason.rhelNoMatchingPackage': 'The server script found no installed PHP package of version {version}, the PHP being checked. It found: {packages}. This PHP probably comes from another repository, a control panel or a container, so those changelogs do not apply. Check the advisories of whoever supplies this PHP, or run the server script where it runs.',
     89   'reason.rhelChangelogNotChecked': 'The changelog of {pkg}, the package of this PHP, was not checked: the server script output has no changelog lines for it (an older version of the script, or the changelog could not be read). Run the current server script, which reads the changelog of every PHP package.',
     90   'reason.rhelChangelogUnread': 'The changelog of {pkg} was not checked for {cves}: the server script output has no readable count for it, so it is unknown whether this PHP has those fixes. Copy the whole server script output again, or run the current server script.',
     91   'reason.rhelNeedServerScript': 'On the RHEL family, the version number alone does not show whether a fix was backported. Run the server script to check the package changelogs.',
     92   'reason.remiChangelogFixed': 'This PHP comes from the Remi repository. The changelog of {pkg} records the fix for {cves}, backported without changing the version number.',
     93 
     94   // Reasons: processes and the running PHP
     95   'reason.staleProcesses': 'Running processes ({names}) are still using PHP files from before the update.',
     96   'reason.staleOtherProcesses': 'The PHP checked here has the fix, but other PHP processes ({names}) are still using files from before an update, and they may be running an older, vulnerable build. Restart them.',
     97   'reason.runningUnchecked': 'The installed package has the fix, but the running PHP could not be checked: the server script could not read some PHP processes. Run the server script as root, or run the PHP probe, to confirm that the running PHP was restarted after the update.',
     98   'reason.noRunningPhpSeen': 'No running PHP process of this build was found, so this rests on the installed package alone. If PHP runs in a container or is started some other way, check it with the PHP probe.',
     99   'reason.loadedBuildUnconfirmed': 'PHP_VERSION ({version}) is the same for the old and the fixed build, so this rests on the installed package. The probe cannot see whether PHP was restarted after the update: run the server script as root as well, which checks the running processes.',
    100   'reason.runningHidden': "The installed package has the fix, but the server script did not run as root and found no PHP process it could check. Without root, /proc can hide other accounts' processes completely (hidepid), so a PHP that was not restarted after the update would not show up. Run the server script again with sudo.",
    101   'reason.processesNotCheckedNoService': 'The server script did not run as root, so running processes were not checked. This build has no PHP-FPM, CGI or Apache module package, so no web server process can keep an old build of it loaded, and this rests on the installed package.',
    102   'reason.probeProcessNotFound': 'PHP_VERSION ({version}) is the same for the old and the fixed build, so this rests on the installed package, and the server script found no running process of this PHP to check for a missed restart. If it runs in a container or under another name, restart it after the update to be sure.',
    103   'reason.serverConflict': 'The PHP probe and the server script list different PHP packages (see the notes), so neither the installed package nor the restart check can be relied on. Run the server script again right after the probe and paste both outputs together.',
    104   'reason.binaryOnDisk': 'This version comes from -v output of the {sapi} binary, which shows the binary installed on disk, not the PHP the running service loaded. If the service was not restarted after the update, it still runs the old build. Run the PHP probe, or the server script as root, to confirm.',
    105   'reason.staleUnaccounted': 'PHP processes that none of the PHP builds checked here accounts for ({names}) are still using files from before an update. They may run an older, vulnerable build. Restart them, and check that PHP with the probe.',
    106   'reason.unaccountedProcesses': 'Running PHP processes ({names}) belong to none of the PHP builds checked here, so the PHP they run was not checked. They may come from a container, a control panel or a self-built PHP. Check that PHP with the PHP probe (inside the container if it runs in one).',
    107   'reason.serverTruncated': 'The server script output was cut off before its END line, so its later lines (running processes and signs of compromise) are missing, and Patched cannot be confirmed. Copy the whole output again.',
    108   // One installed PHP build, when several are judged
    109   'reason.forBuild': '{build} ({verdict}): {reasons}',
    110   // The running PHP, when installed builds are judged next to it
    111   'reason.forRunning': 'Running PHP {version} ({verdict}): {reasons}',
    112   // One of several separate outputs in the input
    113   'reason.forPart': '{part} ({verdict}): {reasons}',
    114 
    115   // Notes
    116   'note.sury': 'This PHP ({version}) is a deb.sury.org build. That repository follows upstream versions directly, so the verdict uses the upstream fixed releases.',
    117   'note.upstreamBuild': 'This looks like an upstream build (self-compiled, or an official Docker image), not a distribution package. The verdict uses the upstream fixed releases.',
    118   'note.configOutsideEtc': 'PHP reads its configuration from {path}, outside /etc, so it is not a distribution package: it was built from source, or comes from a Docker image or a control panel. Installed distribution packages do not describe it, and the verdict uses the upstream fixed releases.',
    119   'note.packageNotRunning': 'The installed package {pkg} is older than the running PHP ({version}), so it is not the PHP that runs. It was left out, and the running PHP is judged on its own.',
    120   'note.notDistroBuild': "{version} is newer than the {src} package that {family} {release} ships ({shipped}), so it is not that release's package. It probably comes from another repository or was built from source, and the verdict uses the upstream fixed releases.",
    121   'note.alpine': 'Alpine packages usually follow upstream versions, so the verdict uses the upstream fixed releases. A package revision (-rN) may carry a separate patch, so check the Alpine security advisories as well.',
    122   'note.otherDistro': 'Your distribution ({vendor}) may backport fixes without changing the version number. The verdict uses the upstream fixed releases, so check its security advisories as well.',
    123   'note.backportBranch': 'The PHP security team has published a minimal backport branch ({branch}) in the php-src repository. You can use it if you build PHP yourself, but moving to a supported version is the real fix. This check cannot recognise a build from that branch by its version number.',
    124   'note.ubuntuPro': 'This fix is published only in the Ubuntu Pro (ESM) repository. You need to attach an Ubuntu Pro subscription (free for personal use) to receive it.',
    125   'note.ubuntuProLegacy': 'This fix is published only through the Ubuntu Pro Legacy Support add-on, a paid add-on that the free personal Ubuntu Pro subscription does not include.',
    126   'note.rhelCompatible': 'RHEL-compatible distributions publish their own security advisories (Rocky RLSA, AlmaLinux ALSA, Amazon Linux ALAS and others) based on the Red Hat advisories (RHSA). Release timing can differ slightly.',
    127   'note.remi': 'This PHP ({pkg}) comes from the Remi repository, which follows upstream versions, so the verdict uses the upstream fixed releases.',
    128   'note.noRpmPackage': 'The server script output lists no PHP package installed with rpm, so this PHP was probably built from source, or comes from a control panel or a container. The verdict uses the upstream fixed releases. If PHP was installed with dnf or yum, run the current server script: an older copy of it did not list packages named like php8.3-common.',
    129   'note.newerPackageWaiting': 'A newer package ({installed}) is installed than the running PHP ({running}). The running PHP already has the fix; restart the service when convenient to load the update.',
    130   'note.staleFixed': 'Processes ({names}) are still using files from before a later update. The PHP that ran the probe already has the fix; restart the service when convenient to load the update.',
    131   'note.severalOutputs': 'The input holds {n} separate outputs. Each was checked on its own, the reasons list each one, and the worst result is shown.',
    132   'note.manualConflictOs': 'The Distribution list is set to {chosen}, but the pasted output shows {detected}, so {detected} was used. If the output comes from the server you meant, set the list back to Auto-detect.',
    133   'note.manualConflictVersion': 'The Distribution list is set to {chosen}, but the version string {version} is a build of {detected}, so {detected} was used.',
    134 
    135   // Distribution advisories
    136   'advisory.fixedIn': 'fixed in {version}',
    137   'advisory.pending': 'fix pending',
    138   'advisory.noData': 'no data',
    139   // Red Hat fix_state values, shown as they are in English
    140   'rhelState.affected': 'Affected',
    141   'rhelState.underInvestigation': 'Under investigation',
    142   'rhelState.fixDeferred': 'Fix deferred',
    143   'rhelState.willNotFix': 'Will not fix',
    144   'rhelState.notAffected': 'Not affected',
    145   'rhelState.outOfSupportScope': 'Out of support scope',
    146   'list.separator': ', ',
    147   'list.and': ' and ',
    148 
    149   // Remediation steps (r.actions). Each is a sentence or two; the commands go in the step's code block.
    150   // Restart
    151   'action.restartNow': 'The running PHP loaded the old build before the update and keeps it in memory until its processes stop. Restart the service that runs PHP with the command below, so that it loads the new build.{extra}',
    152   'action.restartAfterUpdate': 'After the update, restart the service that runs PHP with the command below, so that it loads the new build.{extra}',
    153   'action.restartAfterUpdateGeneric': 'After the update, restart the service that runs PHP (the web server or PHP-FPM), so that it loads the new build. With Apache mod_php, only a full restart of Apache, not a reload or a graceful restart, loads it.',
    154   'action.restartGeneric': 'Restart the service that runs PHP (the web server or PHP-FPM), so that it loads the new build. With Apache mod_php, only a full restart of Apache, not a reload or a graceful restart, loads it.',
    155   'action.restartProcesses': 'Restart whatever runs these PHP processes ({names}), a service or a container, so that they load the new build. The command below shows what started them: in the CGROUP column, a name ending in .service is a systemd service (restart it with systemctl), and docker-<id>.scope is a container (restart that container).',
    156   'restart.modPhp': ' With mod_php, PHP runs inside the Apache processes, so only a full restart of Apache, not a reload or a graceful restart, loads the new build.',
    157   'restart.cgi': ' PHP runs here as CGI/FastCGI: the web server (Apache with mod_fcgid, as in the first command) or a separate FastCGI process manager starts the php-cgi processes, so restart whichever starts them. The second command lists the php-cgi processes with their start times: none should be older than the restart.',
    158   'restart.choose': ' Run the line for the way PHP runs on this server: Apache mod_php or PHP-FPM.',
    159   'step.restart.ifUnsure': 'The installed package already has the fix. If you are not sure that PHP was restarted after that update, restart it now with the command below.{extra}',
    160   'step.restart.confirm': 'Then run the PHP probe again through the web server and press Run check: it should no longer say Restart needed.',
    161   'step.restart.confirmServer': 'Then run the server script again as root and press Run check: it should list no PHP process as still using files from before the update. If you used the PHP probe, run it again as well.',
    162   'step.recheck': 'Then run the PHP probe again through the web server and press Run check: the result should be Patched.',
    163   'step.recheckServer': 'Then run the server script again as root (and the PHP probe, if you used it) and press Run check: the result should be Patched.',
    164   'step.recheckServerLater': 'Once the update is installed, run the server script again as root (and the PHP probe, if you used it) and press Run check: the result should be Patched.',
    165   'step.recheckChangelog': 'Then run the server script again as root and press Run check: it reads the changelog of the updated package, which shows whether both fixes are in it.',
    166   'step.recheckServerDistro': 'Then run the server script again as root, paste its output here with the release picked in the Distribution list, and press Run check: the result should be Patched.',
    167 
    168   // What to collect when the verdict is open
    169   'step.collect.server': 'Run the server script as root on the server where this PHP runs. The first two commands download it and print its hash: compare the hash with the one on the Server script tab of this page, and read the script before you run it with the last command. Paste its output here (with the PHP probe output, if you have it) and press Run check. The script reads the OS release, the installed PHP packages and their changelogs, and the running PHP processes.',
    170   'step.collect.serverInContainer': 'This PHP runs in a container, so run the server script inside that container. The first two commands download it and print its hash (compare it with the one on the Server script tab of this page); the last two copy it into the container and run it there as root (-u 0). Paste that output here on its own and press Run check.',
    171   'step.collect.root': 'Run the server script again as root (with sudo), paste the new output here and press Run check. Without root it cannot read the PHP processes of other accounts, so a PHP that was not restarted after an update stays hidden.',
    172   'step.collect.distro': 'Or, if you know the distribution, pick it and its release in the Distribution list next to Run check, and press Run check again.',
    173   'step.collect.distroOnly': 'The server script output does not name the release of this system. Pick the distribution and its release in the Distribution list next to Run check, and press Run check again.',
    174   'step.collect.probe': 'Run the PHP probe through the web server. Download it (the first command, or the PHP probe tab of this page), set $TOKEN at the top of the file to 12 or more random letters and digits (openssl rand -hex 16 makes one), upload it to the web root of the site under a name that is hard to guess, and open it once, in a browser or with the second command. Paste everything it prints here and press Run check.{next}',
    175   'collect.probeNext': ' To check the installed packages and running processes as well, also run the server script as in the next step.',
    176   'step.collect.whole': 'Copy the whole server script output again, from its BEGIN line to its END line, paste it here and press Run check.',
    177   'step.collect.together': 'Run the PHP probe and, right after it, the server script as root, then paste both outputs here together and press Run check, so that both describe the same moment.',
    178   'step.collect.elsewhere': 'Check the PHP these processes run where it runs. If it is in a container, copy the server script into the container and run it there as root, as below (-u 0 runs it as root inside the container), and paste that output on its own. Otherwise, run the PHP probe through a site that this PHP serves.',
    179   'step.collect.advisories': 'The distribution data has no record for this case, so check the {family} pages for both CVEs yourself (below). They list the fixed {src} version for each release. In the meantime, update and restart as in the next steps.',
    180   'step.collect.supplier': 'Find out which repository this package comes from: apt-cache policy marks the installed version with *** and lists its source below it. Then check that supplier\'s security advisories for both CVEs, or install the {family} package instead.',
    181 
    182   // Debian and Ubuntu packages
    183   'fix.deb': '{pkg} is installed, and the fixed version for {family} {release} is {src} {fixed}.',
    184   'fix.debRunning': 'This server runs PHP {version}, and the fixed version for {family} {release} is {src} {fixed}.',
    185   'fix.debOpen': 'Even while the result is Undetermined, install the latest {family} updates for {src} now.',
    186   'fix.sury': '{pkg} comes from deb.sury.org, which ships upstream releases, and the {branch} branch is fixed in PHP {fixed}.',
    187   'step.ubuntuPro': 'The fixed package for Ubuntu {release} is published only in Ubuntu Pro (ESM). Ubuntu Pro is free for personal use on up to five machines: get a token at https://ubuntu.com/pro and attach this server with the command below, then update as in the next step.',
    188   'step.ubuntuProLegacy': 'Ubuntu {release} is past its ten-year ESM window: its fixed package is published only through the Ubuntu Pro Legacy Support add-on, which is paid and not part of the free personal subscription. If you have it, attach this server with the command below and update as in the next step; otherwise plan a move to a supported Ubuntu release.',
    189   'step.apt.upgrade': '{fix} The commands below update the packages this check found ({names}); --only-upgrade installs nothing new, and apt updates the PHP extension packages that must keep the same version along with them. To install every pending update instead, run sudo apt full-upgrade and read its list of changes before you confirm.',
    190   'step.apt.upgradeFind': '{fix} The commands below look up the installed {src} packages with dpkg -l and update only those (--only-upgrade installs nothing new). To install every pending update instead, run sudo apt full-upgrade and read its list of changes before you confirm.',
    191   'step.apt.switchToDistro': 'If you would rather use the {family} package, first turn off the repository that apt-cache policy names for this one (its file in /etc/apt/sources.list.d/). Then run the commands below: the second reinstalls every {src} package that is now at {foreign} at the {family} version {fixed}, and --allow-downgrades lets apt replace the higher-numbered build.',
    192   'step.apt.confirmExact': 'Afterwards each package below must show exactly {fixed}, the {family} revision: a higher version from another repository does not count.',
    193   'step.apt.confirmFixed': 'Check that the update is installed: each package below should now show {fixed} or later.',
    194   'step.apt.confirmUpstream': 'Check that the update is installed: each package version below should now start with {fixed} or a later {branch} release.',
    195   'step.apt.confirm': 'Check which versions are installed now. Once the release is picked in the Distribution list, Run check compares them with the fixed version for that release.',
    196   'step.apt.confirmCompare': 'Compare the installed versions (command below) with the fixed version for your release on the {family} pages in the first step: the data of this check has no record for this case, so it cannot compare them for you.',
    197   'step.pending.watch': '{family} {release} has no fixed {src} package yet: the CVEs are known, but the fixed package has not been published. Watch the {family} pages for both CVEs (below), which show the state for each release, and install the fix as soon as {family} {release} has one.',
    198   'step.pending.check': 'To see whether the fix has arrived, refresh the package lists and look for {src} updates with the first two commands. When {src} packages are listed, the last command installs them.',
    199 
    200   // RHEL family
    201   'rhel.context.advisory': 'Red Hat fixed both CVEs for PHP {branch} on RHEL {release} in {advisories}.',
    202   'rhel.context.state': 'The distribution data has no Red Hat fix for PHP {branch} on RHEL {release} yet (Red Hat lists {states}).',
    203   'rhel.context.none': 'The distribution data has no Red Hat advisory for PHP {branch} on RHEL {release}.',
    204   'rhel.context.noRelease': 'The RHEL release of this server is not known. Red Hat has fixed PHP {branch} {fixes}; the server script shows which one applies.',
    205   'rhel.context.noReleaseItem': 'on RHEL {release} in {advisories}',
    206   'rhel.context.noReleaseNone': 'The RHEL release of this server is not known, and the distribution data has no Red Hat advisory for PHP {branch} on any release.',
    207   'rhel.context.amazon': 'Amazon Linux {release} publishes its PHP fixes in ALAS advisories, not in Red Hat ones.',
    208   'rhel.rebuild': '{name} ships the same fix in its own {prefix} advisory.',
    209   'rhel.what.names': 'the packages of this PHP ({names})',
    210   'rhel.what.all': 'every installed PHP package',
    211   'step.rhel.upgrade': '{context} The first command lists the updates that fix these CVEs; the second updates {what}. --refresh makes dnf download the latest repository metadata first.',
    212   'step.rhel.upgradeYum': '{context} The first command lists the updates that fix these CVEs; the second updates {what}.',
    213   'step.rhel.update': '{context} Update {what} with the command below.',
    214   'step.rhel.upgradeNone': 'There is nothing to install for this yet. The first command shows whether an update for these CVEs has been published; as soon as it lists one, the second updates {what} to it, and the steps after it confirm the fix.',
    215   'step.rhel.upgradeNoneStay': 'If you stay on php:{branch}, there is nothing to install for it yet. The first command shows whether an update for these CVEs has been published; as soon as it lists one, the second updates {what} to it, and the steps after it confirm the fix.',
    216   'step.rhel.updateNone': 'There is nothing to install for this yet. Once an update is published, the command below updates {what} to it, and the steps after it confirm the fix.',
    217   'step.rhel.switchStream': 'Red Hat fixed these CVEs on RHEL {release} in other module streams, {streams}, but not in php:{branch}. Test your application with php:{target}, then switch to that stream with the command below: dnf replaces the PHP packages with its fixed build ({build}).',
    218   'step.rhel.watch': '{context} Watch the Red Hat pages for both CVEs (below), which show the state for each RHEL release, and update as soon as a fix is listed for RHEL {release}.',
    219   'step.rhel.watchAny': 'Watch the Red Hat pages for both CVEs (below), which show the state for each RHEL release, and update as soon as a fix is listed for yours.',
    220   'step.rhel.module': 'Before you update, check with the command below that the php:{branch} stream is enabled (marked [e]). The fix comes as a newer build of that stream ({build}), which the upgrade in the next step installs; you do not need to switch streams.',
    221   'step.rhel.moduleNoBuild': 'This PHP comes from the php:{branch} module stream; the command below shows the enabled stream (marked [e]). A fix for it will come as a newer build of that stream, which the upgrade above installs while the stream stays enabled.',
    222   'step.rhel.confirm': 'Confirm the update: rpm -q shows the installed versions, and the changelog of {pkg} should now have a line for each CVE (the second command prints them).',
    223   'step.rhel.confirmFixed': 'Confirm the update: rpm -q should show {fixed} or later, and the changelog of {pkg} should now have a line for each CVE (the second command prints them).',
    224   'step.rhel.confirmSome': 'Confirm the update: rpm -q shows the installed versions, and the changelog of {pkg} should now have a line for {cves} (the second command prints it). Red Hat lists {notAffected} as not affected for RHEL {release}, so no line is expected for it.',
    225   'step.rhel.old': '{name} {release} is past the end of its regular support: fixes come only through a paid extended support subscription, and not for every CVE. Plan a move to a supported release (RHEL 8 or later, or a rebuild of it).',
    226   'step.rhel.centosEol': 'CentOS {release} has reached end of life, and its own repositories get no more updates: the update commands below can bring this fix only with a third-party extended support subscription. Plan a move to a supported release, for example RHEL 9, Rocky Linux 9 or AlmaLinux 9, and until then apply the mitigation below.',
    227   'step.amazon.al2Eol': 'Amazon Linux 2 reached the end of its support on 30 June 2026 and gets no more fixes: plan a move to Amazon Linux 2023.',
    228   'step.amazon.amiEol': 'Amazon Linux AMI {release} is past the end of its support and gets no more fixes: plan a move to Amazon Linux 2023.',
    229   'step.amazon.watch': '{context} Watch the Amazon Linux pages for both CVEs (below) and update as soon as a fix is listed for Amazon Linux {release}.',
    230   'step.rpm.confirmUpstream': 'Check that the update is installed: each package version below should now start with {fixed} or a later {branch} release.',
    231 
    232   // Remi, Alpine and other distributions
    233   'fix.remi': '{pkg} comes from the Remi repository, which ships upstream releases, and the {branch} branch is fixed in PHP {fixed}.',
    234   'step.remi.upgrade': '{fix} Update {what} with the command below.',
    235   'step.remi.eol': 'This PHP ({pkg}) comes from the Remi repository. PHP {branch} has reached end of life upstream, but Remi backports security fixes to its PHP {branch} packages without changing the version number. Update {what} with the command below; the changelog should then list both CVEs.',
    236   'fix.alpine': 'This PHP comes from Alpine packages ({names}), which follow upstream releases, and the {branch} branch is fixed in PHP {fixed}.',
    237   'step.apk.upgrade': '{fix} Update every installed {name} package as root with the commands below (apk info lists them). Alpine updates PHP in its community repository only until the next stable Alpine release, so on an older release you may need to move to a newer Alpine release to get PHP {fixed}.',
    238   'step.apk.confirm': 'Check the installed versions: they should now start with {fixed} or a later {branch} release.',
    239   'step.other.update': 'This PHP comes from {vendor}. Install its latest PHP update with its package manager, as below, and check the {vendor} security advisories for both CVEs: the fix may be backported without changing the version number, or come as PHP {fixed} or later.',
    240   'step.other.updateNoCode': 'This PHP comes from {vendor}. Install its latest PHP update with its package manager, and check the {vendor} security advisories for both CVEs: the fix may be backported without changing the version number, or come as PHP {fixed} or later.',
    241 
    242   // Upstream builds and end-of-life branches
    243   'step.upstream.get': 'PHP {version} does not match a distribution package in the data of this check, so it is treated as an upstream build: built from source, or installed by a control panel or a repository such as deb.sury.org or Remi. The {branch} branch is fixed in PHP {fixed}.{extra} If a control panel or a repository installed it, update it there. If you build PHP yourself, download {fixed} from php.net (or set your build pipeline to {fixed}) and compare its checksum with the one listed on php.net (https://www.php.net/downloads).',
    244   'step.upstream.getUnknown': 'Nothing in this input shows where PHP {version} comes from. If it is a distribution package, the PHP probe in the previous step gives the exact update steps. If it was built from source, or a control panel or another repository installed it, update it to PHP {fixed}, the fixed {branch} release: in the panel or repository, or by downloading {fixed} from php.net (or setting your build pipeline to it) and comparing its checksum with the one listed on php.net (https://www.php.net/downloads).{extra}',
    245   'step.upstream.build': 'Build and install PHP {fixed} with the same configure options as the current build, so that the same extensions and SAPIs are built. The first command prints the current options: put them in place of <current-options>.',
    246   'step.upstream.confirmBinary': 'Check the version of the binary that serves the site, not the command-line php, which can be another build: it should print PHP {fixed}. If {binary} is not on your PATH, use the full path that the service starts. Then run the PHP probe again through the web server and press Run check.',
    247   'step.upstream.confirmProbe': 'Run the PHP probe again through the web server and press Run check: it should show PHP {fixed}. Do not rely on php -v on the command line, which can be another build than the one the web server loads.',
    248   'step.eol.move': 'PHP {branch} has reached end of life, so php.net will not publish a fixed {branch} release. The real fix is to move to a supported branch: PHP {target} (fixed in {targetFixed}) or PHP {alt} (fixed in {altFixed}). Code written for {branch} may need changes on a newer branch, so test your application on a copy of the site or a staging server before you switch.',
    249   'step.eol.distro': 'If you cannot move yet, PHP {branch} with both fixes backported is available as a distribution package in {carriers}. A commercial long-term support vendor for PHP {branch} is another option; check that its build lists both CVEs.',
    250   'step.eol.vendor': 'If you cannot move yet, use PHP {branch} from a distribution or a commercial long-term support vendor that backports security fixes, and check that its package lists both CVEs.',
    251   'step.eol.host': 'On this {system} server, the closest fixed PHP is the distribution\'s own {src} package ({fixed}), which gets {family} security updates: install {pkg} with the commands below, switch the site over to it and test the application. A commercial long-term support vendor for PHP {branch} is another option.{extra}',
    252   'step.eol.hostNoCode': 'On this {system} server, the closest fixed PHP is the distribution\'s own {src} package ({fixed}), which gets {family} security updates: turn off the Apache PHP module loaded now (two PHP modules cannot be loaded together), install {pkg} and test the application. A commercial long-term support vendor for PHP {branch} is another option.{extra}',
    253   'eol.others': 'Other distributions that carry the {branch} backport: {carriers}.',
    254   'eol.hostPro': 'This package is published only in Ubuntu Pro (ESM), so attach an Ubuntu Pro subscription first.',
    255   'branch.eolSoon': 'PHP {branch} reaches end of life on {date}, so plan the move to {low} or {high} as well.',
    256   'branch.eolPast': 'PHP {branch} reached end of life on {date}, so plan the move to {low} or {high} as well.',
    257   'step.eol.backport': 'As a last resort, build PHP {branch} yourself from {name}, a php-src branch in which the PHP security team published only these two fixes, with the configure options of your current build (the first command prints them: put them in place of <current-options>). A build from git also needs autoconf, bison and re2c. It gets no other fixes, and this check cannot recognise such a build: its version stays {version}, so the result will still say Vulnerable.',
    258   'step.prereleaseFixed': 'Check the release notes or commits of PHP {version} for both CVE fixes. For production, use a stable release that has them: PHP {fixed} or a later {branch} release.',
    259   'action.checkReleaseNotes': 'Check the release notes or commits of PHP {version} for both CVE fixes. For production, use a stable release that has them.',
    260 
    261   // Containers
    262   'step.docker.tag': 'This container runs PHP {version} from the official PHP image; the {branch} branch is fixed in PHP {fixed}. Change the php image tag in your Dockerfile (FROM), compose.yaml (image:) or docker run command to the one below, which keeps your variant ({variant}). A tag such as php:{floating} also gets {fixed} when you pull it again, but it does not pin the version, so a later rebuild can change PHP without notice.{extra} If you use an image built on PHP instead, such as wordpress, drupal or nextcloud, keep that image and move to a newer tag of it that ships PHP {fixed} (docker run --rm <your-image> php -v shows its PHP).',
    263   'step.docker.tagNoVariant': 'This container runs PHP {version} from the official PHP image; the {branch} branch is fixed in PHP {fixed}. Change the php image tag in your Dockerfile (FROM), compose.yaml (image:) or docker run command to PHP {fixed}, with the variant of your current tag (apache, fpm or cli, for example) in place of <variant>. A tag such as php:{branch} follows the branch but does not pin the version.{extra} If you use an image built on PHP instead, such as wordpress, drupal or nextcloud, keep that image and move to a newer tag of it that ships PHP {fixed}.',
    264   'step.docker.frankenphp': 'This container runs FrankenPHP with PHP {version}. The {branch} branch is fixed in PHP {fixed}. FrankenPHP images (dunglas/frankenphp) are tagged by FrankenPHP version and PHP branch, not by PHP release, so pull your tag again once it ships PHP {fixed}, or build FrankenPHP with PHP {fixed}.',
    265   'step.docker.eol': 'This container runs the official PHP image with PHP {version}. PHP {branch} has reached end of life, so there will be no fixed {branch} image. Move to a supported branch: the tag below uses PHP {targetFixed}, and PHP {altFixed} is the other choice. Code written for {branch} may need changes on a newer branch, so test your application with the new image before you deploy it.{extra}',
    266   'docker.oldDebian': 'Your image is based on Debian {from} ({fromName}), for which no new PHP images are published, so the tag below moves it to Debian {to} ({toName}), the nearest release that still gets them. Packages your Dockerfile installs with apt may have other names or versions there, so test the build.',
    267   'docker.otherDebian': 'No PHP images are published for Debian {codename}, so the tag uses the default Debian release of the image: test your Dockerfile with it.',
    268   'docker.alpineFloating': 'The -alpine tag follows the newest Alpine release, which is newer than the Alpine {release} your image uses now: test your Dockerfile with it, or pin an Alpine release with a tag that Docker Hub lists, such as {example}.',
    269   'step.docker.rebuild': 'Rebuild with --pull, so that Docker fetches the new base image instead of using its cached copy, and replace the running containers: restarting an existing container keeps the old image. With plain Docker, build as below and start a new container from <your-image> in place of the old one; if you run the php image directly, docker pull the new tag instead. With Docker Compose, the last three commands rebuild the services that have a build: section, pull the images named in image: lines, and recreate the containers whose image changed.',
    270   'step.docker.confirm': 'Check the PHP version in a new container: the command below should print {fixed}. Then run the PHP probe again through the web server and press Run check.',
    271   'step.container.update': 'PHP in this container comes from packages, not from the official PHP image. {fix} Add the line below to your Dockerfile after the line that installs PHP (or move to a newer base image that already has the fix), then rebuild the image and replace the containers as in the next step.',
    272   'step.container.updatePro': 'PHP in this container comes from Ubuntu packages, not from the official PHP image. {fix} That version is published only in Ubuntu Pro, which an image build can use only while it is attached: add the lines below to your Dockerfile after the line that installs PHP, put the line token: <token> in a file named pro-attach-config.yaml, and build with the --secret option as in the next step, so that the token never ends up in the image. With Docker Compose, declare that file as a build secret in compose.yaml.',
    273   'step.container.upstream': 'This container runs PHP {version}, which is neither a distribution package nor the official PHP image. The {branch} branch is fixed in PHP {fixed}.{extra} Update the base image or the build that provides PHP to {fixed}, then rebuild the image and replace the containers as in the next step.',
    274   'step.container.other': 'PHP in this container comes from {vendor} packages. Update them in your Dockerfile with its package manager (or move to a newer base image), and check the {vendor} security advisories for both CVEs: the fix may be backported without changing the version number, or come as PHP {fixed} or later. Then rebuild the image and replace the containers as in the next step.',
    275   'step.container.eol': 'This container runs PHP {version}. PHP {branch} has reached end of life, so there will be no fixed {branch} release. Move the image to a supported branch, PHP {target} (fixed in {targetFixed}) or PHP {alt} (fixed in {altFixed}), and test your application with it before you deploy it.',
    276   'step.container.confirmFixed': 'Check the installed versions in a new container: they should show {fixed} or later. Then run the PHP probe again through the web server and press Run check.',
    277   'step.container.confirm': 'Check the installed versions in a new container, then run the PHP probe again through the web server and press Run check.',
    278   'step.container.confirmChangelog': 'Check in a new container that the changelog of {pkg} lists {cves} (the second command prints them), then run the PHP probe again through the web server and press Run check.',
    279   'step.container.confirmChangelogFixed': 'Check in a new container that rpm -q shows {fixed} or later and that the changelog of {pkg} lists {cves} (the second command prints them), then run the PHP probe again through the web server and press Run check.',
    280   'step.container.restart': 'The PHP packages in this container were updated, but its PHP processes still run the old build, which they loaded before the update. Restart the container to load the new build, then run the PHP probe again and press Run check.',
    281   'step.container.persist': 'The update was made inside the running container, so a new container from the same image goes back to the old build. Put the update into the image as well: add the line below to your Dockerfile after the line that installs PHP, and rebuild.',
    282 
    283   // Interim mitigations and hardening
    284   'when.cannotUpdate': 'If you cannot update right away',
    285   'when.pending': 'Until the fixed package is published',
    286   'when.eol': 'Until this site runs on a supported branch',
    287   'when.eolRelease': 'Until this server runs a supported release',
    288   'step.mitigate.apache': '{when}, turn off session upload progress, the main interim mitigation in the official advisory: the commands below set it for every site that Apache mod_php serves, test the configuration and reload Apache (a reload is enough for a configuration change).{extra} Upload progress bars stop updating, but uploads keep working. Enable the commented memory_limit -1 only with a memory cap outside PHP, such as MemoryMax= on the Apache service, never with ulimit -v. This lowers the risk but does not replace the patch.',
    289   'step.mitigate.fpm': '{when}, turn off session upload progress, the main interim mitigation in the official advisory: the commands below set it for the www pool of PHP-FPM in a separate file, test the configuration and reload PHP-FPM (a reload is enough for a configuration change).{extra} Upload progress bars stop updating, but uploads keep working. If the test says "[pool www] user has not been defined", this server has no www pool: put your pool\'s name in the brackets, and add a section like it for each other pool. Enable the commented memory_limit -1 only with a memory cap outside PHP, such as MemoryMax= on the service, never with ulimit -v. This lowers the risk but does not replace the patch.',
    290   'step.mitigate.fpmAlso': 'PHP-FPM runs this PHP as well: the commands below set the same for its www pool in a separate file, test the configuration and reload PHP-FPM (a reload is enough for a configuration change).{extra} If the test says "[pool www] user has not been defined", put your pool\'s name in the brackets. This interim mitigation lowers the risk but does not replace the patch.',
    291   'step.mitigate.docker': '{when}, turn off session upload progress, the main interim mitigation in the official advisory. The first two commands add the setting to the running container and restart it; the RUN line keeps it in your image, so add it to your Dockerfile as well.{extra} Upload progress bars stop updating, but uploads keep working. This lowers the risk but does not replace the patch.',
    292   'step.mitigate.ini': '{when}, turn off session upload progress, the main interim mitigation in the official advisory: the commands below add the setting to {file}, which this PHP reads, and restart PHP so that it takes effect.{extra} Upload progress bars stop updating, but uploads keep working. This lowers the risk but does not replace the patch.',
    293   'step.mitigate.iniNoRestart': '{when}, turn off session upload progress, the main interim mitigation in the official advisory: the command below adds the setting to {file}, which this PHP reads; then restart the service that runs PHP so that it takes effect.{extra} Upload progress bars stop updating, but uploads keep working. This lowers the risk but does not replace the patch.',
    294   'mitigate.fatalBacktraces': ' On PHP 8.5 they also turn off fatal_error_backtraces, which blocks the confirmed RCE path.',
    295   'action.applyMitigations': '{when}, apply the interim mitigations listed in this result. They do not replace the patch.',
    296   'action.applyMitigationsAdvisory': '{when}, apply the interim mitigations from the official advisory: session.upload_progress.enabled=0, and memory_limit=-1 only together with a memory cap outside PHP (a cgroup limit such as MemoryMax= on the service), never with ulimit -v. Run the PHP probe to see their current values. They do not replace the patch.',
    297   'action.lookForCompromise': 'If the server was exposed before you patched, look for signs of compromise: logs, recently changed PHP files, and unusual processes running as the web server user. Run the server script with --logs and with --webroot pointing at the site\'s directory (for example /var/www), as below, and paste its output here.',
    298   'action.checkOtherSites': 'Check every other site (vhost) and PHP-FPM pool on this server as well: each one can run a different PHP. Run the PHP probe once for each.',
    299   'action.checkOtherContainers': 'Check every other container and image that runs PHP as well: each can have its own PHP version. Run the PHP probe once in each.',
    300   'step.hardening.expose': 'Optional hardening: turn off expose_php, so that responses stop showing the PHP version in an X-Powered-By header. It gives attackers less information but does not replace updates. The commands below add the setting for this PHP and restart its service so that it takes effect.',
    301   'step.hardening.exposeDocker': 'Optional hardening: turn off expose_php, so that responses stop showing the PHP version in an X-Powered-By header. Add the line below to your Dockerfile and rebuild the image. It gives attackers less information but does not replace updates.',
    302   'step.hardening.exposePath': 'Optional hardening: set expose_php = Off in the php.ini of this PHP (in {path}) and restart the service that runs it, so that responses stop showing the PHP version in an X-Powered-By header. It gives attackers less information but does not replace updates.',
    303   'step.hardening.exposeManual': 'Optional hardening: set expose_php = Off in the php.ini of the PHP that serves this site and restart the service that runs it, so that responses stop showing the PHP version in an X-Powered-By header. It gives attackers less information but does not replace updates.',
    304   'step.hardening.uploadOff': 'session.upload_progress.enabled is off. If nothing uses the upload progress feature, keep it off now that PHP is patched: the official advisory recommends this, because attackers are looking for other flaws in this feature.',
    305   'step.hardening.uploadOn': 'If nothing on this server uses the upload progress feature, you can turn off session.upload_progress.enabled (set it to 0) even now that PHP is patched: the official advisory recommends this, because attackers are looking for other flaws in this feature.',
    306 
    307   // Interim mitigations
    308   'mit.value.notSet': '(not set)',
    309   'mit.value.emptyOff': '(empty = Off)',
    310   'mit.noChangeNeeded': 'No change is needed on the fixed version.',
    311   'mit.memoryLimit.patchedUnlimited': 'The fixed version does not need -1. With no PHP memory limit there is a denial-of-service (DoS) risk, so consider reverting it to the original value.',
    312   'mit.memoryLimit.unlimited': 'The main configuration mitigation for CVE-2026-109618 is in place. PHP itself now has no memory limit, so set a limit elsewhere, for example with cgroup (memory.max), pm.max_children or MaxRequestWorkers. Do not use RLIMIT_AS or RLIMIT_DATA: they trigger the vulnerable path instead.',
    313   'mit.memoryLimit.suggest': 'Setting it to -1 removes the predictable memory limit that CVE-2026-109618 attacks rely on (the first configuration mitigation in the official advisory, and not a complete one). It creates a denial-of-service (DoS) risk, so cap memory elsewhere, with a cgroup limit (memory.max), pm.max_children or MaxRequestWorkers. Do not use RLIMIT_AS or RLIMIT_DATA (ulimit -v): they trigger the vulnerable path.',
    314   'mit.uploadProgress.noSession': 'The session extension is not loaded, so the session path does not apply.',
    315   'mit.uploadProgress.off': 'The confirmed session path for CVE-2026-109619 is blocked. The official advisory recommends keeping it off after patching if you do not use it.',
    316   'mit.uploadProgress.patchedOn': 'If you do not use the upload progress feature, the official advisory recommends keeping it off even after patching, because attackers are looking for other flaws in this feature.',
    317   'mit.uploadProgress.suggest': 'Setting it to Off (0) blocks the session path confirmed for CVE-2026-109619. Check first whether you use the upload progress feature.',
    318   'mit.autoStart.off': 'It is off.',
    319   'mit.autoStart.suggest': 'According to the Xint reproduction, PHP is vulnerable while this is on, even with upload_progress off. Before you turn it off, check where session_start() is called and test your login flow.',
    320   'mit.fatalBacktraces.off': 'The confirmed CVE-2026-109619 RCE path in PHP 8.5 is blocked.',
    321   'mit.fatalBacktraces.suggest': 'On PHP 8.5, setting it to 0 blocks the confirmed RCE path of CVE-2026-109619 (official advisory).',
    322   'mit.nestingLevel': 'The default is 64. The attack uses deeply nested field names, so the lower you set it within what your application allows, the lower the risk (official advisory).',
    323   'mit.postMaxSize': 'Keep it conservative, and limit the request size in the web server as well (Apache LimitRequestBody, Nginx client_max_body_size).',
    324   'mit.exposePhp.off': 'The PHP version is not exposed in response headers.',
    325   'mit.exposePhp.on': 'The PHP version is exposed in a response header (X-Powered-By). Off is recommended after patching.',
    326 
    327   // SAPI exposure
    328   'exposure.apache2handler': 'With Apache mod_php, an attack can succeed while the request body is being parsed, whatever your application code does. The official advisory verified RCE with default settings on this setup, which makes it the most dangerous one.',
    329   'exposure.frankenphp': 'FrankenPHP always runs request shutdown handling, as mod_php does, so it is likely vulnerable to attacks that do not depend on the application (official advisory).',
    330   'exposure.fpm': 'PHP-FPM ends the worker when request startup fails, which blocks some paths, but attacks through application paths are still possible. The session-path RCE (CVE-2026-109619) was also verified on FPM.',
    331   'exposure.litespeed': 'LiteSpeed (LSAPI) behaves much like FPM, so attacks through application paths are expected to be possible (official advisory, not verified).',
    332   'exposure.cli': 'This result comes from the command line (CLI). It may differ from the PHP your web service uses, so upload the PHP probe to your web root and run it from a browser.',
    333   'exposure.other': 'The official advisory does not mention {sapi} separately. Treat it as exposed until it is patched.',
    334 
    335   // Compromise indicators from the server script (--logs, --webroot)
    336   'finding.oom': ({ n }) => `The logs contain ${count(n, 'out-of-memory error', 'out-of-memory errors')} raised before any script ran ("in Unknown on line 0"). This may be an attack attempt using malformed multipart requests.`,
    337   'finding.crash': ({ n }) => `The logs contain ${count(n, 'web server or PHP crash (segfault)', 'web server or PHP crashes (segfaults)')}. This may be an attempted memory corruption attack.`,
    338   'finding.bigSession': ({ n }) => `Found ${count(n, 'session file', 'session files')} larger than 1 MB. This may be a trace of an attack on the session upload progress path.`,
    339   'finding.webUserProc': ({ n }) => `Found ${count(n, 'other process', 'other processes')} running as a web server account (www-data, apache, nginx, nobody or similar). Check the server script output to see what they are: a shell, a download tool or a coin miner there suggests a compromise.`,
    340   'finding.recentPhp': ({ n }) => `Found ${count(n, 'PHP file', 'PHP files')} changed in the last 3 days. Treat any file you did not deploy as a possible web shell.`,
    341   'finding.unread': 'Some sources could not be read ({items}), so they were not checked. Run the server script as root. Zero counts from the other sources do not cover these.',
    342   'finding.notRoot': 'The server script did not run as root, so logs, session files and processes owned by other accounts may not have been read. Run it again with sudo before you rely on these counts.',
    343   'finding.source.logs': 'logs',
    344   'finding.source.sessions': 'session files',
    345   'finding.source.processes': 'web server account processes',
    346   'finding.source.webroot': 'web root',
    347   'finding.none': 'Nothing suspicious was found in the items checked ({items}). The absence of traces does not mean there was no compromise.',
    348   'finding.incident': 'If you suspect a compromise, isolate the server from the network and preserve the logs. Then rotate all database passwords, API keys, and session and encryption keys. You can also report the incident to your national CERT/CSIRT.',
    349 
    350   // Notes from reading the pasted input (parse.js)
    351   'parse.badJson': 'Could not read the JSON part of the PHP probe output. Make sure you copied the whole output and nothing was cut off.',
    352   'parse.notSelfcheck': 'This is not in the php2shell-selfcheck output format.',
    353   'parse.dpkgUnreadable': 'Could not read the installed package list (/var/lib/dpkg/status), for example because of open_basedir. The distribution backport check may be inaccurate, so run the server script as well.',
    354   'parse.openBasedir': 'open_basedir is set for this site, so the probe could not read the OS release or the installed package list. Without them the probe alone cannot check distribution backports: run the server script as well, or pick your distribution in the Distribution list and press Run check again.',
    355   'parse.osReleaseUnread': 'The probe could not read the OS release (/etc/os-release or the older release files), so the distribution and its installed packages are unknown. Without them the probe alone cannot check distribution backports: run the server script as well, or pick your distribution in the Distribution list and press Run check again.',
    356   'parse.serverOtherSystem': "The PHP probe ran inside a container, but the server script output comes from another system ({os}), probably the host. Host packages, changelogs and processes do not describe the PHP in the container, so they were not used. To check the container's packages, run the server script inside it (docker exec).",
    357   'parse.serverOtherOs': 'The server script output comes from another system ({os}) than the PHP probe ({probe}), so its packages, changelogs and processes do not describe this PHP and were not used. Run both on the server where this PHP runs.',
    358   'parse.serverUnlinked': "The PHP probe ran inside a container, and nothing in its output shows that the server script ran in the same container: the probe could not read its own OS release or package list, or its package list differs from the server script's. The server script may have run on the host, so its packages, changelogs and processes were not used. To check the container's packages, run the server script inside the container (docker exec) and paste that output on its own.",
    359   'parse.serverConflict': "The PHP probe and the server script list different PHP packages, so they were taken at different times (for example before and after an update) or on different systems. The server script's packages, changelogs and processes were not used.",
    360   'parse.serverTruncated': 'The server script output has no END line, so part of it was lost when it was copied. Copy everything from the BEGIN line to the END line.',
    361   'parse.procBadStatus': 'The restart status of {names} in the server script output is not readable (the line may have been cut off), so those processes were not checked. Copy the whole output again.',
    362   'parse.probeNotDeleted': 'The PHP probe was not deleted from the server automatically. Delete it yourself now.',
    363   // n is the count exactly as the server script printed it (a string)
    364   'parse.procUnreadable': ({ n }) => `Could not check ${n} PHP ${n === '1' ? 'process' : 'processes'} for a missed restart because of missing permissions. Run the server script again as root.`,
    365   'parse.headerOnly': 'A response header shows only the PHP that handled that URL. Check other sites and FPM pools on the same server separately.',
    366   'parse.cliVersion': 'php -v shows the version of the command-line PHP. It may differ from the PHP your web service uses, so check again with the PHP probe.',
    367   'parse.noVersion': 'No PHP version was found. Paste the full PHP probe output, an X-Powered-By header, or a version string such as 8.2.26.',
    368   // Input that could not be read, or was cut
    369   'reason.notRead': 'The pasted text could not be read as any of the supported outputs, so nothing could be checked. Paste the PHP probe output or the server script output again, from its BEGIN line to its END line.',
    370   'reason.inputCut': 'Part of the input was not read (it is too long, or holds too many separate outputs), and what was left out may be a PHP without the fix, so Patched cannot be confirmed.',
    371   'step.collect.shorter': 'Paste less at a time: the PHP probe output and the server script output of one server, each from its BEGIN line to its END line, and press Run check.',
    372   'parse.inputTooLong': 'The input is longer than 2 million characters, so only its beginning was read. Paste the outputs of one server at a time.',
    373   'parse.tooManyOutputs': 'The input holds more than {n} separate outputs, so only the first {n} were checked. Paste fewer outputs at a time.',
    374   'parse.serverNoBegin': 'The server script output has no BEGIN line, so it was read from its first data line to its END line. Next time, copy it from the BEGIN line.',
    375   'parse.serverHeadCut': 'The server script output has no BEGIN line and does not start with its first line, so its first lines are missing. Copy everything from the BEGIN line to the END line.',
    376   'reason.serverHeadCut': 'The server script output has no BEGIN line and starts below its first line, so its first lines (whether it ran as root, the OS release) may be missing, and Patched cannot be confirmed. Copy the whole output again.',
    377   'parse.serverUnlinkedHidden': "open_basedir kept the PHP probe from seeing whether it runs in a container, and nothing in its output (its OS release or its package list) ties it to the server script output, which may come from the host of a container. The server script's packages, changelogs and processes were not used for this PHP, and its output was checked on its own. If this PHP runs in a container, run the server script inside it (docker exec); otherwise run the PHP probe on a site of the same PHP without open_basedir.",
    378 
    379   // Downgrades, missing SAPI packages and the revision in PHP_VERSION (Debian and Ubuntu)
    380   'reason.sapiPackageMissing': 'PHP runs here as {sapi}, but its package ({pkg}) is not in the list of installed packages. An interrupted update can leave it half-installed, and dpkg lists only packages that are fully installed; otherwise this PHP comes from somewhere else. Debian and Ubuntu build the PHP engine into the binary of each SAPI, so the other {src} packages do not show which build runs here.',
    381   'reason.revisionMissing': 'PHP_VERSION ({version}) has no {family} revision, but on {family} {release} an updated PHP build reports it: the installed package ({installed}) would show {expected}. Only the original build of the release reports the plain version, so the running PHP is an older build that was loaded before the update, and it does not have the fix.',
    382   'reason.sapiPackageMissingProc': 'A PHP process of this branch runs ({comm}), but its package ({pkg}) is not in the list of installed packages. An interrupted update can leave it half-installed, and dpkg lists only packages that are fully installed. Debian and Ubuntu build the PHP engine into the binary of each SAPI, so the other {src} packages do not show which build {comm} runs.',
    383   'note.olderPackageFixed': 'The installed package {pkg} is older than the running PHP ({version}), but it has the fix too, so the next restart loads a fixed build.',
    384   'fix.debDowngraded': '{pkg} is installed, which is older than the running PHP {version} and does not have the fix: install {fixed} or later again before the service restarts.',
    385   'note.staleDowngraded': 'Processes ({names}) still run the build that was loaded before the package was downgraded, which has the fix. Do not restart or reload them before the fixed package is installed again: they would load the vulnerable build.',
    386   'reason.staleUnfixed': 'Running processes ({names}) are still using PHP files that were replaced after they started, by an update or a downgrade. The build installed now is not shown to have the fix, so a restart alone does not help: install the fixed package first, then restart.',
    387   'mitigate.staleReload': ' Processes ({names}) still use files from before the last package change, and a reload makes PHP-FPM load the build installed now: if that change was a downgrade, install the fixed package first.',
    388   'step.collect.sapiPackage': 'Check the state of {pkg} with the commands below: dpkg -l shows every {src} package with its state (ii is fully installed), and dpkg --audit lists packages that an interrupted update left unfinished. The last two commands finish such an update (apt-get install -f installs what is missing, and asks before it changes anything). Then restart PHP as in the next step and run the check again.',
    389 
    390   // deb.sury.org and Remi builds of end-of-life branches
    391   'note.suryEol': 'This PHP ({version}) is a deb.sury.org build of PHP {branch}, a branch that has reached end of life upstream. For such branches deb.sury.org ships backported security fixes under the same upstream version, so only the changelog of the installed package shows whether a build has them.',
    392   'fix.suryEol': 'deb.sury.org ships backported security fixes for PHP {branch}, an end-of-life branch, under the same version number, so its latest {src} build may have them.',
    393   'step.collect.hiddenLink': 'Nothing in the PHP probe output ties it to the server script output (see the notes). If this PHP runs in a container, copy the server script into the container and run it there as root, as below, and paste that output on its own. If it does not, the server script output pasted on its own already covers the PHP builds and processes of this server; or run the PHP probe through a site of the same PHP that has no open_basedir setting, so that the probe can read the OS release and the package list, and paste that output together with the server script output.',
    394   'reason.suryChangelogFixed': 'The changelog of {pkg}, read by the server script at the installed version, records the fixes for {cves}, backported by deb.sury.org.',
    395   'reason.suryChangelogMissing': 'The changelog of {pkg}, read by the server script at the installed version, has no record of a fix for {cves}: this build does not have the backport yet.',
    396   'reason.suryBuiltBefore': 'This build ({version}) was made on {date}, before the fixed releases of {released}, so it cannot have the backported fixes.',
    397   'reason.suryEolUnread': 'Whether this deb.sury.org build of PHP {branch} ({version}) has the backported fixes shows only in its package changelog, which was not read here. Run the server script, which reads the changelog of every installed PHP package.',
    398   'note.remiEol': 'This PHP ({pkg}) comes from the Remi repository. Its collections of end-of-life branches (php74, php80, php81) get backported security fixes under the same version number, so only the package changelog shows whether a build has them.',
    399   'reason.remiChangelogMissing': 'The changelog of {pkg} has no record of a fix for {cves}: this Remi build does not have the backport yet.',
    400   'reason.remiEolUnread': 'Whether this Remi build of PHP {branch} ({pkg}) has the backported fixes shows only in its package changelog, which was not read here. Run the server script, which reads the changelog of every installed PHP package.',
    401   'eol.hostSury': "deb.sury.org is configured on this server, and apt prefers its packages over the distribution's: turn that repository off (its file in /etc/apt/sources.list.d/) before you install, and check that apt-cache policy shows {fixed} as the candidate, or apt installs deb.sury.org's {src}, which is not this fixed package.",
    402   'step.eol.hostSwitch': "If you cannot move yet: on this {system} server, the distribution's own {src} package ({fixed}) has both fixes backported, but the {src} packages installed now ({foreign}) come from another repository. Turn that repository off first (its file in /etc/apt/sources.list.d/; apt-cache policy names it), then run the commands below: the second reinstalls every {src} package that is now at {foreign} at version {fixed}, and --allow-downgrades lets apt replace the higher-numbered build. Test the application afterwards.{extra}",
    403 
    404   // RHEL family, Amazon Linux and other distributions
    405   'reason.rhelOlderThanFixed': '{pkg} is older than {fixed}, the build that fixes both CVEs ({advisory}).',
    406   'reason.amazonOlder': '{pkg} is older than PHP {fixed}, the upstream fixed release. Amazon Linux ships its PHP fixes as upstream releases.',
    407   'step.rpm.confirmVersion': 'Check that the update is installed: rpm -q should now show {fixed} or later. This fix is a new upstream release, and its changelog entry names the version rather than the CVEs, so the version is what shows it.',
    408   'reason.vendorBackports': '{vendor} keeps the upstream version of its PHP packages and backports security fixes into them, so PHP {version} may have both fixes even though it is older than the upstream fixed release. The version alone cannot settle it: check the {vendor} security advisories, or the changelog of the installed PHP package, for both CVEs.',
    409   'step.collect.vendor': 'Check the {vendor} security advisories for both CVEs, or the changelog of the installed PHP package: the commands below list the PHP packages and print the CVE lines of the changelog. Each CVE should have a line once the fix is installed.',
    410   'step.other.updateEol': 'This PHP comes from {vendor}, which backports security fixes to its PHP {branch} packages without changing the version number. Install its latest PHP update with the commands below, then check the {vendor} security advisories, or the package changelog as in the first step, for both CVEs.',
    411 
    412   // Processes the server script could not check
    413   'reason.processesUnreadable': 'The server script could not check some PHP processes ({n}) for a missed restart: it could not read them, or their status line was cut off. They may run another PHP than the one checked here, one that was not restarted after an update, so Patched cannot be confirmed. Run the server script as root and paste the whole output.',
    414   'reason.processesUnreadableRoot': 'The server script could not check some PHP processes ({n}) for a missed restart, even as root. They may run another PHP than the one checked here, one that was not restarted after an update, so Patched cannot be confirmed. Inside a container without CAP_SYS_PTRACE, run the server script on the host instead, or check each PHP with the PHP probe.',
    415   'reason.runningUncheckedRoot': 'The installed package has the fix, but the server script could not read some PHP processes, even as root, so the running PHP could not be checked. Inside a container without CAP_SYS_PTRACE, run the server script on the host, or run the PHP probe, to confirm that the running PHP was restarted after the update.',
    416   'reason.runningPartlyHidden': "The installed package has the fix, but the server script did not run as root, so it saw only the PHP processes this account can read. With hidepid, /proc hides other accounts' processes completely, so a PHP that was not restarted after the update may not show up. Run the server script again with sudo.",
    417   'reason.serviceUnseen': 'The installed package has the fix, but the server script cannot see the processes of {names} on this system, so it could not check whether they were restarted after the update.',
    418   'step.collect.unreadableAsRoot': 'The server script ran as root but still could not read some PHP processes. That happens inside a container that lacks CAP_SYS_PTRACE, and running it again with sudo gives the same output. Run it on the host instead (the commands below; compare the hash with the one on the Server script tab of this page), or run the PHP probe through each site, and paste the new output here.',
    419   'step.collect.unseen': 'The server script cannot see the processes of {names} on this system. Check with the command below whether they run and when they started. If one started before the update, restart it as in the next step, or check that PHP with the PHP probe.',
    420   'parse.procUnreadableRoot': ({ n }) => `Could not check ${n} PHP ${n === '1' ? 'process' : 'processes'} for a missed restart, even as root. Inside a container, reading another account's process memory map needs CAP_SYS_PTRACE: run the server script on the host, or use the PHP probe.`,
    421 };
    422 
    423 const ko = {
    424   // Verdicts
    425   'verdict.title.patched': '수정이 적용된 PHP입니다',
    426   'verdict.title.vulnerable': '취약한 PHP입니다',
    427   'verdict.title.pending': '취약하며, 배포판의 수정 패키지가 아직 없습니다',
    428   'verdict.title.restart': '설치된 PHP는 수정됐지만 실행 중인 PHP는 여전히 취약합니다',
    429   'verdict.title.unknown': '판단할 근거가 부족합니다',
    430   'verdict.label.patched': '패치됨',
    431   'verdict.label.vulnerable': '취약',
    432   'verdict.label.pending': '수정 대기',
    433   'verdict.label.restart': '재시작 필요',
    434   'verdict.label.unknown': '판단 불가',
    435 
    436   // Facts
    437   'fact.phpVersion': 'PHP 버전',
    438   'fact.installedVersion': '설치된 패키지 버전',
    439   'fact.sapi': 'SAPI',
    440   'fact.sapiInferred': '{sapi} (실행 중인 프로세스로 추정)',
    441   'fact.distribution': '배포판',
    442   'fact.webServer': '웹 서버',
    443   'fact.packageChecked': '확인한 패키지',
    444   'fact.otherBuilds': '그 밖에 설치된 PHP 빌드',
    445 
    446   // Distribution and SAPI labels
    447   'distro.rhel': 'RHEL 계열',
    448   'distro.rhelRelease': 'RHEL {release} 계열',
    449   'distro.upstream': '업스트림 빌드(직접 컴파일 또는 공식 Docker 이미지)',
    450   'distro.other': '기타',
    451   'distro.unknown': '알 수 없음',
    452   'distro.inferred': '{label} (버전 문자열로 추정)',
    453   'distro.kernel': '{label} (커널 버전으로 추정)',
    454   'distro.serverHeader': '{label} (Server 헤더로 추정)',
    455   'distro.derived': '{label} ({parent} 기반)',
    456   'distro.manual': '{label} (수동 선택)',
    457   'sapi.cli': '명령줄(CLI)',
    458 
    459   // One of several separate outputs in the input
    460   'part.probe': 'PHP 프로브(PHP {version})',
    461   'part.probeNoVersion': 'PHP 프로브 출력',
    462   'part.header': 'X-Powered-By 헤더(PHP/{version})',
    463   'part.cli': '-v 출력(PHP {version}, {sapi})',
    464   'part.server': '서버 스크립트 출력({system})',
    465   'part.version': '버전 문자열 {version}',
    466 
    467   // Reasons: upstream
    468   'reason.noVersion': 'PHP 버전을 찾지 못했습니다.',
    469   'reason.preRelease': '{version} 버전은 사전 릴리스(alpha, beta, RC 또는 -dev 스냅숏)이므로 수정 릴리스 기준으로 확인할 수 없습니다. 릴리스 노트나 커밋에서 두 CVE의 수정이 모두 들어갔는지 확인하세요.',
    470   'reason.newerBranch': 'PHP {branch} 브랜치는 수정 릴리스 목록({branches})의 어느 브랜치보다도 새롭습니다. 릴리스 노트에서 두 CVE의 수정이 모두 들어갔는지 확인하세요.',
    471   'reason.upstreamPatched': 'PHP {version} 버전은 업스트림 수정 릴리스 {fixed} 이상입니다.',
    472   'reason.upstreamVulnerable': 'PHP {version} 버전은 업스트림 수정 릴리스 {fixed}보다 낮습니다.',
    473   'reason.predates54': 'PHP {version} 버전은 공개된 공격 경로(세션 업로드 진행률 기능, PHP 5.4 이상)보다 먼저 나왔습니다. 다만 공식 권고에는 영향받는 버전의 하한이 명시되어 있지 않으며, 이 버전은 오래전에 지원 종료(EOL)되어 다른 취약점도 많습니다.',
    474   'reason.eol': 'PHP {branch} 브랜치는 지원 종료(EOL)되어 공식 수정 릴리스가 없으므로, 이 브랜치의 업스트림 빌드는 취약합니다. 두 수정은 이를 백포트한 배포판 패키지에만 들어 있을 수 있습니다.',
    475   'reason.osBlocked': 'open_basedir 때문에 프로브가 OS 릴리스 정보와 설치된 패키지 목록을 읽지 못했습니다. 그래서 PHP {version} 버전이 같은 버전 번호로 수정을 백포트한 배포판 패키지인지 알 수 없습니다. 서버 스크립트를 실행하거나, 배포판 목록에서 배포판을 고른 뒤 결과확인을 다시 누르세요.',
    476   'reason.osUnread': '프로브가 OS 릴리스 정보를 읽지 못했습니다. 그래서 PHP {version} 버전이 RHEL 계열 등에서처럼 같은 버전 번호로 수정을 백포트한 배포판 패키지인지 알 수 없습니다. 서버 스크립트를 실행하거나, 배포판 목록에서 배포판을 고른 뒤 결과확인을 다시 누르세요.',
    477   'reason.couldBeDistro': 'PHP {version} 버전은 같은 버전 번호로 수정을 백포트한 배포판 패키지({releases})일 수도 있으므로 버전만으로는 확인할 수 없습니다. 설치된 패키지와 그 changelog를 읽는 서버 스크립트를 실행하세요. Debian과 Ubuntu에서는 PHP 프로브로도 패키지 버전을 읽을 수 있습니다.',
    478 
    479   // Reasons: Debian and Ubuntu
    480   'reason.noDataChangelogFixed': '{reason} 다만 설치된 패키지의 changelog에 두 CVE의 수정이 모두 기록되어 있으므로 수정된 것으로 간주합니다.',
    481   'reason.debReleaseUnknown': '{family} 릴리스를 알 수 없어 백포트 여부를 확인하지 못했습니다. 배포판 목록에서 릴리스를 고른 뒤 결과확인을 다시 누르세요.',
    482   'reason.debNoRelease': '배포판 데이터에 {family} {release}에 대한 기록이 없습니다. 지원 종료(EOL)됐거나 아직 권고가 공개되지 않았을 수 있습니다. {family} 보안 권고를 직접 확인하세요.',
    483   'reason.debNoPackage': '배포판 데이터에 {family} {release}의 {src} 패키지에 대한 기록이 없습니다. 다른 저장소에서 설치한 패키지이거나 아직 권고가 나오지 않았을 수 있습니다.',
    484   'reason.debBackported': '{family}에서 두 CVE의 수정을 {src} {version} 패키지에 백포트했습니다. 버전 번호({upstream})는 낮지만 수정이 포함되어 있습니다.',
    485   'reason.debOlder': '{src} {version} 패키지는 {family} 수정 버전 {fixed}보다 오래되었습니다.',
    486   'reason.debOlderUpstream': 'PHP {version} 버전은 {family} {release}의 {src} 수정 패키지({fixed})보다 업스트림 버전이 낮으므로 수정이 포함되어 있지 않습니다.',
    487   'reason.debNeedPackage': 'PHP_VERSION({version})은 {family} {release} {src} 패키지의 업스트림 버전으로, 수정 패키지를 설치해도 바뀌지 않습니다. 그래서 버전만으로는 이 서버에 수정이 있는지 알 수 없습니다. 서버 스크립트를 실행하거나, 설치된 패키지 목록(/var/lib/dpkg/status)을 읽을 수 있는 곳에서 PHP 프로브를 실행하세요.',
    488   'reason.debPending': '{family} {release}에는 아직 {src}의 수정 패키지가 없습니다. 배포판 보안 권고를 계속 확인하고, 수정이 나오는 즉시 업데이트하세요.',
    489   'reason.debNoCveData': '배포판 데이터에 두 CVE 중 하나 이상에 대한 기록이 없습니다.',
    490   'reason.restartInstalledFixed': '설치된 패키지({installed})에는 두 CVE의 수정이 포함되어 있지만, 실행 중인 PHP는 여전히 이전 버전({running})입니다. 업데이트 후 서비스를 재시작하지 않은 것으로 보입니다.',
    491   'reason.installedNewer': '설치된 패키지({installed})가 실행 중인 PHP({running})보다 새 버전입니다. 업데이트 후 서비스를 재시작하지 않은 것으로 보입니다.',
    492   'reason.installedOlderUnfixed': '실행 중인 PHP({running})에는 수정이 적용되어 있지만, 설치된 패키지({installed})는 그보다 오래되어 수정이 없습니다. 서비스가 시작된 뒤 패키지가 다운그레이드되었거나 다른 빌드로 바뀐 것입니다. 다음에 서비스를 재시작하면(PHP-FPM은 reload해도) 취약한 빌드가 로드됩니다. 서비스가 재시작되기 전에 수정 패키지를 다시 설치하세요.',
    493   'reason.foreignRevision': '{version} 버전은 {family} {release}의 {src} 패키지와 업스트림 버전이 같지만, {family}에서 빌드한 리비전이 아니므로 다른 저장소(PPA나 다른 공급자)에서 온 패키지입니다. 버전 비교에서 수정 버전보다 높게 나온다고 해서 백포트된 수정이 들어 있다는 뜻은 아닙니다. 이 패키지를 제공한 곳의 보안 권고를 확인하거나 {family} 패키지를 설치하세요.',
    494 
    495   // Reasons: RHEL family
    496   'reason.rhelChangelogFixed': '이 PHP를 제공하는 {pkg} 패키지의 changelog에 {cves} 수정이 기록되어 있습니다.',
    497   'reason.rhelChangelogMissing': '이 PHP를 제공하는 {pkg} 패키지의 changelog에 {cves} 수정 기록이 없습니다. 패키지가 아직 업데이트되지 않았을 가능성이 높습니다.',
    498   'reason.rhelNotAffected': 'Red Hat은 RHEL {release}에서 {cves}의 영향이 없다고 밝혔습니다.',
    499   'reason.rhelNoMatchingPackage': '서버 스크립트가 확인 대상 PHP와 같은 버전({version})의 PHP 패키지를 찾지 못했습니다. 찾은 패키지: {packages}. 이 PHP는 다른 저장소, 컨트롤 패널 또는 컨테이너에서 온 것으로 보이므로 이 changelog는 해당되지 않습니다. 이 PHP를 제공한 곳의 보안 권고를 확인하거나, 이 PHP가 실행되는 곳에서 서버 스크립트를 실행하세요.',
    500   'reason.rhelChangelogNotChecked': '이 PHP를 제공하는 {pkg} 패키지의 changelog를 확인하지 못했습니다. 서버 스크립트 출력에 이 패키지의 changelog 줄이 없습니다(이전 버전의 스크립트이거나 changelog를 읽지 못한 경우입니다). 모든 PHP 패키지의 changelog를 읽는 최신 서버 스크립트를 실행하세요.',
    501   'reason.rhelChangelogUnread': '{pkg}의 changelog에서 {cves} 항목을 확인하지 못했습니다. 서버 스크립트 출력에 이 항목의 건수가 제대로 기록되어 있지 않아 이 PHP에 해당 수정이 있는지 알 수 없습니다. 서버 스크립트 출력 전체를 다시 복사하거나 최신 서버 스크립트를 실행하세요.',
    502   'reason.rhelNeedServerScript': 'RHEL 계열에서는 버전 번호만으로 수정이 백포트됐는지 알 수 없습니다. 서버 스크립트를 실행해 패키지 changelog를 확인하세요.',
    503   'reason.remiChangelogFixed': '이 PHP는 Remi 저장소에서 설치한 것입니다. {pkg}의 changelog에 {cves}의 수정이 기록되어 있으며, 버전 번호를 바꾸지 않고 백포트되었습니다.',
    504 
    505   // Reasons: processes and the running PHP
    506   'reason.staleProcesses': '실행 중인 프로세스({names})가 아직 업데이트 이전의 PHP 파일을 사용하고 있습니다.',
    507   'reason.staleOtherProcesses': '여기서 확인한 PHP에는 수정이 적용되어 있지만, 다른 PHP 프로세스({names})가 아직 업데이트 이전의 파일을 사용하고 있으며 취약한 이전 빌드일 수 있습니다. 이 프로세스들을 재시작하세요.',
    508   'reason.runningUnchecked': '설치된 패키지에는 수정이 포함되어 있지만, 서버 스크립트가 일부 PHP 프로세스를 읽지 못해 실행 중인 PHP는 확인하지 못했습니다. 업데이트 후 실행 중인 PHP가 재시작됐는지 확인하려면 root 권한으로 서버 스크립트를 실행하거나 PHP 프로브를 실행하세요.',
    509   'reason.noRunningPhpSeen': '이 빌드의 실행 중인 PHP 프로세스를 찾지 못했으므로 이 결과는 설치된 패키지만을 근거로 합니다. PHP가 컨테이너 안이나 다른 방식으로 실행된다면 PHP 프로브로 확인하세요.',
    510   'reason.loadedBuildUnconfirmed': 'PHP_VERSION({version})은 이전 빌드와 수정된 빌드가 같으므로 이 결과는 설치된 패키지를 근거로 합니다. 업데이트 후 PHP가 재시작됐는지는 프로브로 알 수 없으니, 실행 중인 프로세스를 확인하는 서버 스크립트도 root 권한으로 실행하세요.',
    511   'reason.runningHidden': '설치된 패키지에는 수정이 포함되어 있지만, 서버 스크립트를 root 권한으로 실행하지 않아 확인할 수 있는 PHP 프로세스를 하나도 찾지 못했습니다. root가 아니면 /proc이 다른 계정의 프로세스를 완전히 숨길 수 있으므로(hidepid), 업데이트 후 재시작하지 않은 PHP가 있어도 보이지 않습니다. sudo로 서버 스크립트를 다시 실행하세요.',
    512   'reason.processesNotCheckedNoService': '서버 스크립트를 root 권한으로 실행하지 않아 실행 중인 프로세스는 확인하지 못했습니다. 이 빌드에는 PHP-FPM, CGI, Apache 모듈 패키지가 없어 웹 서버 프로세스가 이 빌드의 이전 버전을 메모리에 유지할 수 없으므로, 이 결과는 설치된 패키지를 근거로 합니다.',
    513   'reason.probeProcessNotFound': 'PHP_VERSION({version})은 이전 빌드와 수정된 빌드가 같으므로 이 결과는 설치된 패키지를 근거로 합니다. 서버 스크립트도 이 PHP의 실행 중인 프로세스를 찾지 못해 재시작 누락 여부를 확인할 수 없었습니다. 이 PHP가 컨테이너 안에서 또는 다른 이름으로 실행된다면, 확실히 해 두기 위해 업데이트 후 재시작하세요.',
    514   'reason.serverConflict': 'PHP 프로브와 서버 스크립트가 서로 다른 PHP 패키지를 보고하므로(참고 사항 참조) 설치된 패키지도, 재시작 확인도 믿을 수 없습니다. 프로브를 실행한 직후 서버 스크립트를 다시 실행하고 두 출력을 함께 붙여 넣으세요.',
    515   'reason.binaryOnDisk': '이 버전은 {sapi} 바이너리의 -v 출력에서 나온 것으로, 실행 중인 서비스가 로드한 PHP가 아니라 디스크에 설치된 바이너리의 버전입니다. 업데이트 후 서비스를 재시작하지 않았다면 여전히 이전 빌드가 실행됩니다. PHP 프로브를 실행하거나 root 권한으로 서버 스크립트를 실행해 확인하세요.',
    516   'reason.staleUnaccounted': '여기서 확인한 어느 PHP 빌드에도 속하지 않는 PHP 프로세스({names})가 아직 업데이트 이전의 파일을 사용하고 있습니다. 취약한 이전 빌드가 실행 중일 수 있습니다. 이 프로세스들을 재시작하고, 그 PHP도 프로브로 확인하세요.',
    517   'reason.unaccountedProcesses': '실행 중인 PHP 프로세스({names})가 여기서 확인한 어느 PHP 빌드에도 속하지 않으므로, 이 프로세스가 실행하는 PHP는 확인하지 못했습니다. 컨테이너, 컨트롤 패널 또는 직접 빌드한 PHP일 수 있습니다. 그 PHP를 PHP 프로브로 확인하세요(컨테이너에서 실행된다면 컨테이너 안에서).',
    518   'reason.serverTruncated': '서버 스크립트 출력이 END 줄 전에 잘려 뒷부분(실행 중인 프로세스, 침해 지표)이 빠졌으므로 패치됨으로 확인할 수 없습니다. 출력 전체를 다시 복사하세요.',
    519   'reason.forBuild': '[{verdict}] {build}: {reasons}',
    520   'reason.forRunning': '[{verdict}] 실행 중인 PHP {version}: {reasons}',
    521   'reason.forPart': '[{verdict}] {part}: {reasons}',
    522 
    523   // Notes
    524   'note.sury': '이 PHP({version})는 deb.sury.org 빌드입니다. 이 저장소는 업스트림 버전을 그대로 따르므로 업스트림 수정 릴리스를 기준으로 확인합니다.',
    525   'note.upstreamBuild': '배포판 패키지가 아닌 업스트림 빌드(직접 컴파일한 PHP 또는 공식 Docker 이미지)로 보입니다. 업스트림 수정 릴리스를 기준으로 확인합니다.',
    526   'note.configOutsideEtc': 'PHP가 /etc 밖의 {path}에서 설정을 읽으므로 배포판 패키지가 아닙니다. 소스에서 빌드했거나 Docker 이미지 또는 컨트롤 패널에서 온 PHP입니다. 설치된 배포판 패키지는 이 PHP와 관계가 없으므로 업스트림 수정 릴리스를 기준으로 확인합니다.',
    527   'note.packageNotRunning': '설치된 {pkg} 패키지는 실행 중인 PHP({version})보다 오래된 버전이므로, 실행 중인 PHP는 이 패키지에서 온 것이 아닙니다. 이 패키지는 제외하고 실행 중인 PHP만으로 확인했습니다.',
    528   'note.notDistroBuild': '{version} 버전은 {family} {release}에서 제공하는 {src} 패키지({shipped})보다 새 버전이므로 그 릴리스의 패키지가 아닙니다. 다른 저장소에서 설치했거나 소스에서 빌드한 것으로 보이며, 업스트림 수정 릴리스를 기준으로 확인합니다.',
    529   'note.alpine': 'Alpine 패키지는 대개 업스트림 버전을 따르므로 업스트림 수정 릴리스를 기준으로 확인합니다. 패키지 리비전(-rN)에 별도 패치가 들어갈 수 있으므로 Alpine 보안 권고도 함께 확인하세요.',
    530   'note.otherDistro': '사용 중인 배포판({vendor})은 버전 번호를 바꾸지 않고 수정을 백포트할 수 있습니다. 이 결과는 업스트림 수정 릴리스를 기준으로 하므로 배포판의 보안 권고도 함께 확인하세요.',
    531   'note.backportBranch': 'PHP 보안팀이 php-src 저장소에 최소한의 백포트 브랜치({branch})를 공개했습니다. PHP를 직접 빌드한다면 이 브랜치를 사용할 수 있지만, 근본적인 해결책은 지원되는 버전으로 업그레이드하는 것입니다. 이 확인 도구는 버전 번호만으로 그 브랜치에서 빌드한 PHP를 구별하지 못합니다.',
    532   'note.ubuntuPro': '이 수정은 Ubuntu Pro(ESM) 저장소에만 공개되어 있습니다. 받으려면 Ubuntu Pro 구독(개인 사용은 무료)을 연결(attach)해야 합니다.',
    533   'note.ubuntuProLegacy': '이 수정은 Ubuntu Pro Legacy Support 애드온으로만 공개됩니다. 이 애드온은 유료이며 무료 개인 Ubuntu Pro 구독에는 포함되지 않습니다.',
    534   'note.rhelCompatible': 'RHEL 호환 배포판은 Red Hat 권고(RHSA)를 바탕으로 자체 보안 권고(Rocky RLSA, AlmaLinux ALSA, Amazon Linux ALAS 등)를 발행합니다. 공개 시점은 조금씩 다를 수 있습니다.',
    535   'note.remi': '이 PHP({pkg})는 업스트림 버전을 따르는 Remi 저장소에서 설치한 것이므로 업스트림 수정 릴리스를 기준으로 확인합니다.',
    536   'note.noRpmPackage': '서버 스크립트 출력에 rpm으로 설치된 PHP 패키지가 없으므로, 이 PHP는 소스에서 빌드했거나 컨트롤 패널 또는 컨테이너에서 온 것으로 보입니다. 업스트림 수정 릴리스를 기준으로 확인합니다. PHP를 dnf나 yum으로 설치했다면 최신 서버 스크립트를 실행하세요. 이전 버전의 서버 스크립트는 php8.3-common 같은 이름의 패키지를 목록에 넣지 않았습니다.',
    537   'note.newerPackageWaiting': '실행 중인 PHP({running})보다 새 패키지({installed})가 설치되어 있습니다. 실행 중인 PHP에는 이미 수정이 적용되어 있으니, 적당한 때에 서비스를 재시작해 업데이트를 반영하세요.',
    538   'note.staleFixed': '프로세스({names})가 그 뒤에 설치된 업데이트 이전의 파일을 아직 사용하고 있습니다. 프로브를 실행한 PHP에는 이미 수정이 적용되어 있으니, 적당한 때에 서비스를 재시작해 업데이트를 반영하세요.',
    539   'note.severalOutputs': '입력에 서로 다른 출력이 {n}개 들어 있습니다. 출력마다 따로 확인해 출력별 결과를 함께 적었고, 그중 가장 나쁜 결과를 표시합니다.',
    540   'note.manualConflictOs': '배포판 목록에서 고른 배포판({chosen})이 붙여 넣은 출력의 배포판({detected})과 다르므로 {detected} 기준으로 확인했습니다. 출력이 의도한 서버의 것이 맞다면 배포판 목록을 자동 감지로 되돌리세요.',
    541   'note.manualConflictVersion': '배포판 목록에서 고른 배포판({chosen})이 버전 문자열 {version}의 배포판({detected})과 다르므로 {detected} 기준으로 확인했습니다.',
    542 
    543   // Distribution advisories
    544   'advisory.fixedIn': '{version}에서 수정됨',
    545   'advisory.pending': '수정 대기',
    546   'advisory.noData': '데이터 없음',
    547   'rhelState.affected': '영향받음',
    548   'rhelState.underInvestigation': '조사 중',
    549   'rhelState.fixDeferred': '수정 보류',
    550   'rhelState.willNotFix': '수정 계획 없음',
    551   'rhelState.notAffected': '영향 없음',
    552   'rhelState.outOfSupportScope': '지원 범위 밖',
    553   'list.separator': ', ',
    554   'list.and': ', ',
    555 
    556   // Remediation steps (r.actions)
    557   // Restart
    558   'action.restartNow': '실행 중인 PHP는 업데이트 전에 이전 빌드를 로드했고, 프로세스가 종료될 때까지 그 빌드를 메모리에 유지합니다. 아래 명령으로 PHP를 실행하는 서비스를 재시작해 새 빌드를 로드하게 하세요.{extra}',
    559   'action.restartAfterUpdate': '업데이트한 뒤에는 아래 명령으로 PHP를 실행하는 서비스를 재시작해 새 빌드를 로드하게 하세요.{extra}',
    560   'action.restartAfterUpdateGeneric': '업데이트한 뒤에는 PHP를 실행하는 서비스(웹 서버 또는 PHP-FPM)를 재시작해 새 빌드를 로드하게 하세요. Apache mod_php에서는 reload나 graceful 재시작이 아니라 Apache를 완전히 재시작해야 새 빌드가 적용됩니다.',
    561   'action.restartGeneric': 'PHP를 실행하는 서비스(웹 서버 또는 PHP-FPM)를 재시작해 새 빌드를 로드하게 하세요. Apache mod_php에서는 reload나 graceful 재시작이 아니라 Apache를 완전히 재시작해야 새 빌드가 적용됩니다.',
    562   'action.restartProcesses': '이 PHP 프로세스({names})를 실행하는 서비스나 컨테이너를 재시작해 새 빌드를 로드하게 하세요. 아래 명령은 프로세스를 시작한 주체를 보여 줍니다. CGROUP 열의 이름이 .service로 끝나면 systemd 서비스이고(systemctl로 재시작), docker-<id>.scope이면 컨테이너입니다(그 컨테이너를 재시작).',
    563   'restart.modPhp': ' mod_php에서는 PHP가 Apache 프로세스 안에서 실행되므로 reload나 graceful 재시작이 아니라 Apache를 완전히 재시작해야 새 빌드가 적용됩니다.',
    564   'restart.cgi': ' 이 서버에서는 PHP가 CGI/FastCGI로 실행됩니다. php-cgi 프로세스는 웹 서버(첫 번째 명령처럼 mod_fcgid를 쓰는 Apache)나 별도의 FastCGI 프로세스 관리자가 시작하므로, 그 프로세스를 시작하는 쪽을 재시작하세요. 두 번째 명령은 php-cgi 프로세스와 시작 시각을 보여 주며, 재시작 이전에 시작된 프로세스가 없어야 합니다.',
    565   'restart.choose': ' 이 서버에서 PHP가 실행되는 방식(Apache mod_php 또는 PHP-FPM)에 맞는 줄을 실행하세요.',
    566   'step.restart.ifUnsure': '설치된 패키지에는 이미 수정이 들어 있습니다. 이 패키지로 업데이트한 뒤 PHP를 재시작했는지 확실하지 않다면 지금 아래 명령으로 재시작하세요.{extra}',
    567   'step.restart.confirm': '그런 다음 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요. 더 이상 재시작 필요로 나오지 않아야 합니다.',
    568   'step.restart.confirmServer': '그런 다음 서버 스크립트를 root 권한으로 다시 실행하고 결과확인을 누르세요. 업데이트 이전의 파일을 아직 사용하는 PHP 프로세스가 하나도 없어야 합니다. PHP 프로브를 사용했다면 프로브도 다시 실행하세요.',
    569   'step.recheck': '그런 다음 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요. 결과가 패치됨으로 나와야 합니다.',
    570   'step.recheckServer': '그런 다음 서버 스크립트를 root 권한으로 다시 실행하고(PHP 프로브를 사용했다면 프로브도) 결과확인을 누르세요. 결과가 패치됨으로 나와야 합니다.',
    571   'step.recheckServerLater': '업데이트를 설치한 뒤 서버 스크립트를 root 권한으로 다시 실행하고(PHP 프로브를 사용했다면 프로브도) 결과확인을 누르세요. 결과가 패치됨으로 나와야 합니다.',
    572   'step.recheckChangelog': '그런 다음 서버 스크립트를 root 권한으로 다시 실행하고 결과확인을 누르세요. 서버 스크립트는 업데이트된 패키지의 changelog를 읽어 두 수정이 들어 있는지 보여 줍니다.',
    573   'step.recheckServerDistro': '그런 다음 서버 스크립트를 root 권한으로 다시 실행하고, 배포판 목록에서 릴리스를 고른 상태로 출력을 여기에 붙여 넣은 뒤 결과확인을 누르세요. 결과가 패치됨으로 나와야 합니다.',
    574 
    575   // What to collect when the verdict is open
    576   'step.collect.server': '이 PHP가 실행되는 서버에서 서버 스크립트를 root 권한으로 실행하세요. 앞의 두 명령은 스크립트를 내려받아 해시를 출력합니다. 이 해시를 이 페이지의 서버 스크립트 탭에 나온 값과 비교하고, 스크립트 내용을 읽어 본 뒤 마지막 명령으로 실행하세요. 출력을 여기에 붙여 넣고(PHP 프로브 출력이 있으면 함께) 결과확인을 누르세요. 서버 스크립트는 OS 릴리스 정보, 설치된 PHP 패키지와 그 changelog, 실행 중인 PHP 프로세스를 읽습니다.',
    577   'step.collect.serverInContainer': '이 PHP는 컨테이너 안에서 실행되므로 서버 스크립트를 그 컨테이너 안에서 실행하세요. 앞의 두 명령은 스크립트를 내려받아 해시를 출력하고(이 페이지의 서버 스크립트 탭에 나온 값과 비교하세요), 뒤의 두 명령은 스크립트를 컨테이너에 복사해 그 안에서 root로 실행합니다(-u 0). 그 출력만 여기에 붙여 넣고 결과확인을 누르세요.',
    578   'step.collect.root': '서버 스크립트를 root 권한(sudo)으로 다시 실행하고, 새 출력을 여기에 붙여 넣은 뒤 결과확인을 누르세요. root가 아니면 다른 계정의 PHP 프로세스를 읽을 수 없으므로, 업데이트 후 재시작하지 않은 PHP가 보이지 않습니다.',
    579   'step.collect.distro': '배포판을 알고 있다면, 결과확인 옆의 배포판 목록에서 배포판과 릴리스를 고른 뒤 결과확인을 다시 눌러도 됩니다.',
    580   'step.collect.distroOnly': '서버 스크립트 출력에 이 시스템의 릴리스가 나오지 않습니다. 결과확인 옆의 배포판 목록에서 배포판과 릴리스를 고른 뒤 결과확인을 다시 누르세요.',
    581   'step.collect.probe': '웹 서버를 통해 PHP 프로브를 실행하세요. 프로브를 내려받고(첫 번째 명령 또는 이 페이지의 PHP 프로브 탭), 파일 맨 위의 $TOKEN을 영문자와 숫자로 된 12자 이상의 임의 문자열로 바꾼 뒤(openssl rand -hex 16으로 만들 수 있습니다), 추측하기 어려운 이름으로 사이트의 웹 루트에 올리고 브라우저나 두 번째 명령으로 한 번 여세요. 출력된 내용 전체를 여기에 붙여 넣고 결과확인을 누르세요.{next}',
    582   'collect.probeNext': ' 설치된 패키지와 실행 중인 프로세스도 확인하려면 다음 단계처럼 서버 스크립트도 실행하세요.',
    583   'step.collect.whole': '서버 스크립트 출력 전체를 BEGIN 줄부터 END 줄까지 다시 복사해 여기에 붙여 넣고 결과확인을 누르세요.',
    584   'step.collect.together': 'PHP 프로브를 실행한 직후 서버 스크립트를 root 권한으로 실행하고, 두 출력이 같은 시점을 나타내도록 함께 붙여 넣은 뒤 결과확인을 누르세요.',
    585   'step.collect.elsewhere': '이 프로세스들이 실행하는 PHP는 그 PHP가 실행되는 곳에서 확인하세요. 컨테이너 안이라면 아래처럼 서버 스크립트를 컨테이너에 복사해 그 안에서 root로 실행하고(-u 0은 컨테이너 안에서 root로 실행합니다) 그 출력만 붙여 넣으세요. 컨테이너가 아니라면 그 PHP가 처리하는 사이트에서 PHP 프로브를 실행하세요.',
    586   'step.collect.advisories': '배포판 데이터에 이 경우에 대한 기록이 없으므로, 두 CVE에 대한 {family} 페이지(아래)를 직접 확인하세요. 각 릴리스의 {src} 수정 버전이 나와 있습니다. 그동안 다음 단계처럼 업데이트하고 재시작하세요.',
    587   'step.collect.supplier': '이 패키지가 어느 저장소에서 왔는지 확인하세요. apt-cache policy는 설치된 버전에 ***를 표시하고 그 아래에 출처를 보여 줍니다. 그런 다음 그 공급자의 보안 권고에서 두 CVE를 확인하거나, {family} 패키지를 대신 설치하세요.',
    588 
    589   // Debian and Ubuntu packages
    590   'fix.deb': '{pkg} 패키지가 설치되어 있으며, {family} {release}의 수정 버전은 {src} {fixed}입니다.',
    591   'fix.debRunning': '이 서버에서 실행 중인 PHP는 {version} 버전이며, {family} {release}의 수정 버전은 {src} {fixed}입니다.',
    592   'fix.debOpen': '결과가 판단 불가이더라도 {family}의 최신 {src} 업데이트는 지금 설치하세요.',
    593   'fix.sury': '{pkg} 패키지는 업스트림 릴리스를 그대로 제공하는 deb.sury.org에서 설치한 것이며, {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다.',
    594   'step.ubuntuPro': 'Ubuntu {release}의 수정 패키지는 Ubuntu Pro(ESM)에만 공개되어 있습니다. Ubuntu Pro는 개인 사용이면 기기 5대까지 무료입니다. https://ubuntu.com/pro 에서 토큰을 받아 아래 명령으로 이 서버를 연결(attach)한 뒤, 다음 단계처럼 업데이트하세요.',
    595   'step.ubuntuProLegacy': 'Ubuntu {release} 릴리스는 10년 ESM 기간이 끝나, 수정 패키지가 유료인 Ubuntu Pro Legacy Support 애드온으로만 공개되며 무료 개인 구독에는 포함되지 않습니다. 이 애드온이 있다면 아래 명령으로 이 서버를 연결(attach)한 뒤 다음 단계처럼 업데이트하고, 없다면 지원되는 Ubuntu 릴리스로 옮길 계획을 세우세요.',
    596   'step.apt.upgrade': '{fix} 아래 명령은 이 확인에서 찾은 패키지({names})를 업데이트합니다. --only-upgrade는 새 패키지를 설치하지 않으며, 버전이 같아야 하는 PHP 확장 패키지는 apt가 함께 업데이트합니다. 대기 중인 업데이트를 모두 설치하려면 sudo apt full-upgrade를 실행하되, 승인하기 전에 변경 목록을 읽어 보세요.',
    597   'step.apt.upgradeFind': '{fix} 아래 명령은 dpkg -l로 설치된 {src} 패키지를 찾아 그 패키지만 업데이트합니다(--only-upgrade는 새 패키지를 설치하지 않습니다). 대기 중인 업데이트를 모두 설치하려면 sudo apt full-upgrade를 실행하되, 승인하기 전에 변경 목록을 읽어 보세요.',
    598   'step.apt.switchToDistro': '{family} 패키지를 쓰려면 먼저 apt-cache policy가 이 패키지의 출처로 보여 준 저장소를 끄세요(/etc/apt/sources.list.d/ 안의 해당 파일). 그런 다음 아래 명령을 실행하세요. 두 번째 명령은 지금 {foreign} 버전인 {src} 패키지를 모두 {family} 버전({fixed})으로 다시 설치하며, --allow-downgrades는 apt가 번호가 더 높은 빌드를 바꿀 수 있게 합니다.',
    599   'step.apt.confirmExact': '설치 후 아래 패키지는 모두 정확히 {fixed} 버전, 즉 {family} 리비전으로 나와야 합니다. 다른 저장소의 버전은 번호가 더 높아도 인정되지 않습니다.',
    600   'step.apt.confirmFixed': '업데이트가 설치됐는지 확인하세요. 아래 패키지가 모두 {fixed} 이상으로 나와야 합니다.',
    601   'step.apt.confirmUpstream': '업데이트가 설치됐는지 확인하세요. 아래 패키지 버전이 모두 {fixed} 또는 그 이후의 {branch} 릴리스로 시작해야 합니다.',
    602   'step.apt.confirm': '지금 설치된 버전을 확인하세요. 배포판 목록에서 릴리스를 고르면 결과확인이 그 릴리스의 수정 버전과 비교합니다.',
    603   'step.apt.confirmCompare': '아래 명령으로 설치된 버전을 확인해, 첫 번째 단계의 {family} 페이지에서 사용 중인 릴리스의 수정 버전과 직접 비교하세요. 이 확인 도구의 데이터에는 이 경우에 대한 기록이 없어 도구가 대신 비교할 수 없습니다.',
    604   'step.pending.watch': '{family} {release}에는 아직 {src} 수정 패키지가 없습니다. CVE는 알려졌지만 수정 패키지가 아직 공개되지 않았다는 뜻입니다. 릴리스별 상태가 나오는 두 CVE의 {family} 페이지(아래)를 지켜보다가, {family} {release}에 수정 버전이 나오면 바로 설치하세요.',
    605   'step.pending.check': '수정 패키지가 나왔는지는 앞의 두 명령으로 패키지 목록을 새로 받아 {src} 업데이트가 있는지 보면 알 수 있습니다. {src} 패키지가 목록에 나오면 마지막 명령으로 설치하세요.',
    606 
    607   // RHEL family
    608   'rhel.context.advisory': 'Red Hat은 RHEL {release}의 PHP {branch}에 대해 {advisories}에서 두 CVE를 수정했습니다.',
    609   'rhel.context.state': '배포판 데이터에는 RHEL {release}의 PHP {branch}에 대한 Red Hat 수정이 아직 없습니다(Red Hat이 밝힌 상태: {states}).',
    610   'rhel.context.none': '배포판 데이터에는 RHEL {release}의 PHP {branch}에 대한 Red Hat 보안 권고가 없습니다.',
    611   'rhel.context.noRelease': '이 서버의 RHEL 릴리스를 알 수 없습니다. Red Hat은 {fixes}의 PHP {branch}를 수정했으며, 어느 쪽이 해당되는지는 서버 스크립트로 확인할 수 있습니다.',
    612   'rhel.context.noReleaseItem': 'RHEL {release}({advisories})',
    613   'rhel.context.noReleaseNone': '이 서버의 RHEL 릴리스를 알 수 없으며, 배포판 데이터에는 어느 릴리스에도 PHP {branch}에 대한 Red Hat 보안 권고가 없습니다.',
    614   'rhel.context.amazon': 'Amazon Linux {release}의 PHP 수정은 Red Hat 권고가 아니라 자체 ALAS 권고로 공개됩니다.',
    615   'rhel.rebuild': '{name}도 같은 수정을 자체 {prefix} 권고로 제공합니다.',
    616   'rhel.what.names': '이 PHP의 패키지({names})',
    617   'rhel.what.all': '설치된 모든 PHP 패키지',
    618   'step.rhel.upgrade': '{context} 첫 번째 명령은 두 CVE를 수정하는 업데이트를 보여 주고, 두 번째 명령은 {what}를 업데이트합니다. --refresh를 붙이면 dnf가 먼저 최신 저장소 메타데이터를 받습니다.',
    619   'step.rhel.upgradeYum': '{context} 첫 번째 명령은 두 CVE를 수정하는 업데이트를 보여 주고, 두 번째 명령은 {what}를 업데이트합니다.',
    620   'step.rhel.update': '{context} 아래 명령으로 {what}를 업데이트하세요.',
    621   'step.rhel.upgradeNone': '아직 설치할 수정이 없습니다. 첫 번째 명령은 두 CVE에 대한 업데이트가 공개됐는지 보여 주며, 업데이트가 나오면 두 번째 명령이 {what}를 그 버전으로 업데이트합니다. 수정 여부는 그다음 단계에서 확인합니다.',
    622   'step.rhel.upgradeNoneStay': 'php:{branch} 스트림을 계속 쓴다면 아직 설치할 수정이 없습니다. 첫 번째 명령은 두 CVE에 대한 업데이트가 공개됐는지 보여 주며, 업데이트가 나오면 두 번째 명령이 {what}를 그 버전으로 업데이트합니다. 수정 여부는 그다음 단계에서 확인합니다.',
    623   'step.rhel.updateNone': '아직 설치할 수정이 없습니다. 업데이트가 공개되면 아래 명령이 {what}를 그 버전으로 업데이트합니다. 수정 여부는 그다음 단계에서 확인합니다.',
    624   'step.rhel.switchStream': 'Red Hat은 RHEL {release}에서 두 CVE를 php:{branch} 스트림이 아니라 다른 모듈 스트림에서 수정했습니다. 수정된 스트림: {streams}. php:{target} 스트림에서 애플리케이션을 테스트한 뒤 아래 명령으로 그 스트림으로 전환하세요. 그러면 dnf가 PHP 패키지를 그 스트림의 수정 빌드({build})로 교체합니다.',
    625   'step.rhel.watch': '{context} 릴리스별 상태가 나오는 두 CVE의 Red Hat 페이지(아래)를 지켜보다가, RHEL {release}에 수정이 나오면 바로 업데이트하세요.',
    626   'step.rhel.watchAny': '릴리스별 상태가 나오는 두 CVE의 Red Hat 페이지(아래)를 지켜보다가, 사용 중인 릴리스에 수정이 나오면 바로 업데이트하세요.',
    627   'step.rhel.module': '업데이트하기 전에 아래 명령으로 php:{branch} 스트림이 활성화되어 있는지([e] 표시) 확인하세요. 수정은 그 스트림의 더 새로운 빌드({build})로 나오며 다음 단계의 업그레이드가 이를 설치하므로, 스트림을 바꿀 필요는 없습니다.',
    628   'step.rhel.moduleNoBuild': '이 PHP는 php:{branch} 모듈 스트림에서 설치한 것이며, 아래 명령은 활성화된 스트림을 보여 줍니다([e] 표시). 이 스트림의 수정은 같은 스트림의 더 새로운 빌드로 나오며, 스트림이 활성화된 상태에서 위의 업그레이드 명령이 이를 설치합니다.',
    629   'step.rhel.confirm': '업데이트를 확인하세요. rpm -q는 설치된 버전을 보여 주며, {pkg}의 changelog에는 이제 CVE마다 한 줄씩 있어야 합니다(두 번째 명령이 출력합니다).',
    630   'step.rhel.confirmFixed': '업데이트를 확인하세요. rpm -q가 {fixed} 이상을 보여 줘야 하며, {pkg}의 changelog에는 이제 CVE마다 한 줄씩 있어야 합니다(두 번째 명령이 출력합니다).',
    631   'step.rhel.confirmSome': '업데이트를 확인하세요. rpm -q는 설치된 버전을 보여 주며, {pkg}의 changelog에는 이제 {cves} 줄이 있어야 합니다(두 번째 명령이 출력합니다). Red Hat은 RHEL {release}에서 {notAffected}의 영향이 없다고 밝혔으므로 그 줄은 나오지 않습니다.',
    632   'step.rhel.old': '{name} {release} 릴리스는 정규 지원이 끝났습니다. 수정은 유료 연장 지원 구독으로만 나오며, 그마저 모든 CVE에 나오지는 않습니다. 지원되는 릴리스(RHEL 8 이상 또는 그 재빌드 배포판)로 옮길 계획을 세우세요.',
    633   'step.rhel.centosEol': 'CentOS {release} 릴리스는 지원이 종료(EOL)되어 자체 저장소에 더 이상 업데이트가 나오지 않습니다. 아래 업데이트 명령은 외부 업체의 연장 지원 구독이 있을 때만 이 수정을 가져올 수 있습니다. RHEL 9, Rocky Linux 9, AlmaLinux 9 같은 지원되는 릴리스로 옮길 계획을 세우고, 그때까지는 아래 완화 설정을 적용하세요.',
    634   'step.amazon.al2Eol': 'Amazon Linux 2는 2026년 6월 30일에 지원이 종료되어 더 이상 수정이 나오지 않습니다. Amazon Linux 2023으로 옮길 계획을 세우세요.',
    635   'step.amazon.amiEol': 'Amazon Linux AMI {release} 릴리스는 지원이 끝나 더 이상 수정이 나오지 않습니다. Amazon Linux 2023으로 옮길 계획을 세우세요.',
    636   'step.amazon.watch': '{context} 두 CVE의 Amazon Linux 페이지(아래)를 지켜보다가, Amazon Linux {release}에 수정이 나오면 바로 업데이트하세요.',
    637   'step.rpm.confirmUpstream': '업데이트가 설치됐는지 확인하세요. 아래 패키지 버전이 모두 {fixed} 또는 그 이후의 {branch} 릴리스로 시작해야 합니다.',
    638 
    639   // Remi, Alpine and other distributions
    640   'fix.remi': '{pkg} 패키지는 업스트림 릴리스를 그대로 제공하는 Remi 저장소에서 설치한 것이며, {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다.',
    641   'step.remi.upgrade': '{fix} 아래 명령으로 {what}를 업데이트하세요.',
    642   'step.remi.eol': '이 PHP({pkg})는 Remi 저장소에서 설치한 것입니다. PHP {branch} 브랜치는 업스트림 지원이 종료(EOL)되었지만, Remi는 버전 번호를 바꾸지 않고 PHP {branch} 패키지에 보안 수정을 백포트합니다. 아래 명령으로 {what}를 업데이트하세요. 그러면 changelog에 두 CVE가 나와야 합니다.',
    643   'fix.alpine': '이 PHP는 업스트림 릴리스를 따르는 Alpine 패키지({names})이며, {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다.',
    644   'step.apk.upgrade': '{fix} 아래 명령을 root 권한으로 실행해 설치된 {name} 패키지를 모두 업데이트하세요(apk info로 목록을 만듭니다). Alpine은 PHP가 들어 있는 community 저장소를 다음 안정 릴리스가 나올 때까지만 업데이트하므로, 오래된 릴리스에서는 PHP {fixed} 버전을 받으려면 더 새로운 Alpine 릴리스로 옮겨야 할 수 있습니다.',
    645   'step.apk.confirm': '설치된 버전을 확인하세요. 버전이 {fixed} 또는 그 이후의 {branch} 릴리스로 시작해야 합니다.',
    646   'step.other.update': '이 PHP는 {vendor}에서 제공한 것입니다. 아래처럼 패키지 관리자로 최신 PHP 업데이트를 설치하고, {vendor}의 보안 권고에서 두 CVE를 확인하세요. 수정은 버전 번호를 바꾸지 않은 백포트로 나오거나 PHP {fixed} 이상 버전으로 나올 수 있습니다.',
    647   'step.other.updateNoCode': '이 PHP는 {vendor}에서 제공한 것입니다. 패키지 관리자로 최신 PHP 업데이트를 설치하고, {vendor}의 보안 권고에서 두 CVE를 확인하세요. 수정은 버전 번호를 바꾸지 않은 백포트로 나오거나 PHP {fixed} 이상 버전으로 나올 수 있습니다.',
    648 
    649   // Upstream builds and end-of-life branches
    650   'step.upstream.get': 'PHP {version} 버전은 이 확인 도구의 데이터에 있는 배포판 패키지와 일치하지 않으므로 업스트림 빌드로 간주합니다. 소스에서 빌드했거나 컨트롤 패널 또는 deb.sury.org, Remi 같은 저장소에서 설치한 것입니다. {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다.{extra} 컨트롤 패널이나 저장소에서 설치했다면 그곳에서 업데이트하세요. 직접 빌드한다면 php.net에서 {fixed} 버전을 내려받고(또는 빌드 파이프라인을 {fixed} 버전으로 바꾸고), 체크섬을 php.net(https://www.php.net/downloads)에 나온 값과 비교하세요.',
    651   'step.upstream.getUnknown': '이 입력만으로는 PHP {version} 버전을 어디서 설치했는지 알 수 없습니다. 배포판 패키지라면 앞 단계의 PHP 프로브로 정확한 업데이트 단계를 확인하세요. 소스에서 빌드했거나 컨트롤 패널 또는 다른 저장소에서 설치했다면 {branch} 브랜치의 수정 릴리스인 PHP {fixed} 버전으로 업데이트하세요. 컨트롤 패널이나 저장소에서 업데이트하거나, php.net에서 {fixed} 버전을 내려받고(또는 빌드 파이프라인을 그 버전으로 바꾸고) 체크섬을 php.net(https://www.php.net/downloads)에 나온 값과 비교하세요.{extra}',
    652   'step.upstream.build': '현재 빌드와 같은 configure 옵션으로 PHP {fixed} 버전을 빌드하고 설치해야 같은 확장과 SAPI가 만들어집니다. 첫 번째 명령이 현재 옵션을 보여 주니, <current-options> 자리에 넣으세요.',
    653   'step.upstream.confirmBinary': '명령줄 php가 아니라 사이트를 처리하는 바이너리의 버전을 확인하세요. 명령줄 php는 다른 빌드일 수 있습니다. PHP {fixed} 버전이 출력되어야 합니다. {binary} 명령이 PATH에 없다면 서비스가 실행하는 전체 경로를 사용하세요. 그런 다음 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요.',
    654   'step.upstream.confirmProbe': '웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요. PHP {fixed} 버전으로 나와야 합니다. 명령줄의 php -v는 웹 서버가 로드한 빌드와 다른 빌드일 수 있으니 기준으로 삼지 마세요.',
    655   'step.eol.move': 'PHP {branch} 브랜치는 지원이 종료(EOL)되어 php.net에서 {branch} 수정 릴리스가 나오지 않습니다. 근본적인 해결책은 지원되는 브랜치로 옮기는 것입니다. PHP {target} 브랜치(수정 버전 {targetFixed})나 PHP {alt} 브랜치(수정 버전 {altFixed})로 옮기세요. {branch} 브랜치용으로 작성한 코드는 새 브랜치에서 수정이 필요할 수 있으니, 전환하기 전에 사이트 사본이나 스테이징 서버에서 애플리케이션을 테스트하세요.',
    656   'step.eol.distro': '당장 옮길 수 없다면, 두 수정이 백포트된 PHP {branch} 배포판 패키지를 쓸 수 있습니다: {carriers}. PHP {branch} 브랜치를 장기 지원하는 상용 업체를 이용하는 방법도 있으며, 이 경우 그 업체의 빌드에 두 CVE가 나와 있는지 확인하세요.',
    657   'step.eol.vendor': '당장 옮길 수 없다면, 보안 수정을 백포트하는 배포판이나 상용 장기 지원 업체의 PHP {branch} 패키지를 사용하고, 그 패키지에 두 CVE가 나와 있는지 확인하세요.',
    658   'step.eol.host': '이 {system} 서버에서 가장 가까운 수정된 PHP는 배포판 자체의 {src} 패키지({fixed})이며, {family} 보안 업데이트를 받습니다. 아래 명령으로 {pkg} 패키지를 설치하고, 사이트를 그 PHP로 옮긴 뒤 애플리케이션을 테스트하세요. PHP {branch} 브랜치를 장기 지원하는 상용 업체를 이용하는 방법도 있습니다.{extra}',
    659   'step.eol.hostNoCode': '이 {system} 서버에서 가장 가까운 수정된 PHP는 배포판 자체의 {src} 패키지({fixed})이며, {family} 보안 업데이트를 받습니다. 지금 로드된 Apache PHP 모듈을 끈 뒤(PHP 모듈 두 개를 동시에 로드할 수 없습니다) {pkg} 패키지를 설치하고 애플리케이션을 테스트하세요. PHP {branch} 브랜치를 장기 지원하는 상용 업체를 이용하는 방법도 있습니다.{extra}',
    660   'eol.others': 'PHP {branch} 백포트가 들어 있는 다른 배포판: {carriers}.',
    661   'eol.hostPro': '이 패키지는 Ubuntu Pro(ESM)에만 공개되어 있으므로 먼저 Ubuntu Pro 구독을 연결(attach)하세요.',
    662   'branch.eolSoon': 'PHP {branch} 브랜치는 {date}에 지원이 종료되므로 {low} 또는 {high} 브랜치로 옮길 계획도 세우세요.',
    663   'branch.eolPast': 'PHP {branch} 브랜치는 {date}에 지원이 종료되었으므로 {low} 또는 {high} 브랜치로 옮길 계획도 세우세요.',
    664   'step.eol.backport': '마지막 수단으로, PHP 보안팀이 이 두 수정만 넣어 공개한 php-src 브랜치 {name}에서 현재 빌드의 configure 옵션으로 PHP {branch} 브랜치를 직접 빌드할 수 있습니다(첫 번째 명령이 현재 옵션을 보여 주니 <current-options> 자리에 넣으세요). git에서 빌드하려면 autoconf, bison, re2c도 필요합니다. 이 브랜치에는 다른 수정이 없으며, 이 확인 도구는 그렇게 빌드한 PHP를 구별하지 못합니다. 버전이 {version} 그대로이므로 결과는 계속 취약으로 나옵니다.',
    665   'step.prereleaseFixed': 'PHP {version}의 릴리스 노트나 커밋에서 두 CVE의 수정을 확인하세요. 운영 환경에는 수정이 포함된 안정 릴리스인 PHP {fixed} 또는 그 이후의 {branch} 릴리스를 사용하세요.',
    666   'action.checkReleaseNotes': 'PHP {version}의 릴리스 노트나 커밋에서 두 CVE의 수정을 확인하세요. 운영 환경에는 수정이 포함된 안정 릴리스를 사용하세요.',
    667 
    668   // Containers
    669   'step.docker.tag': '이 컨테이너는 공식 PHP 이미지의 PHP {version} 버전을 실행하며, {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다. Dockerfile(FROM), compose.yaml(image:) 또는 docker run 명령에 적힌 php 이미지 태그를 아래 태그로 바꾸세요. 아래 태그는 지금 쓰는 이미지 종류({variant})를 그대로 유지합니다. php:{floating}처럼 브랜치만 지정한 태그도 다시 받으면 {fixed} 버전이 되지만, 버전을 고정하지 않으므로 나중에 다시 빌드할 때 PHP가 예고 없이 바뀔 수 있습니다.{extra} wordpress, drupal, nextcloud처럼 PHP 이미지를 바탕으로 만든 이미지를 쓴다면 이미지 이름은 그대로 두고, PHP {fixed} 버전이 들어 있는 그 이미지의 더 새로운 태그로 옮기세요(docker run --rm <your-image> php -v로 그 이미지의 PHP 버전을 볼 수 있습니다).',
    670   'step.docker.tagNoVariant': '이 컨테이너는 공식 PHP 이미지의 PHP {version} 버전을 실행하며, {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다. Dockerfile(FROM), compose.yaml(image:) 또는 docker run 명령에 적힌 php 이미지 태그를 PHP {fixed} 버전으로 바꾸고, <variant> 자리에는 지금 태그의 이미지 종류(예: apache, fpm, cli)를 넣으세요. php:{branch}처럼 브랜치만 지정한 태그는 브랜치를 따라가지만 버전을 고정하지 않습니다.{extra} wordpress, drupal, nextcloud처럼 PHP 이미지를 바탕으로 만든 이미지를 쓴다면 이미지 이름은 그대로 두고, PHP {fixed} 버전이 들어 있는 그 이미지의 더 새로운 태그로 옮기세요.',
    671   'step.docker.frankenphp': '이 컨테이너는 PHP {version} 버전의 FrankenPHP를 실행합니다. {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다. FrankenPHP 이미지(dunglas/frankenphp)는 PHP 릴리스가 아니라 FrankenPHP 버전과 PHP 브랜치로 태그가 붙으므로, 그 태그에 PHP {fixed} 버전이 들어가면 다시 받거나 PHP {fixed} 버전으로 FrankenPHP를 빌드하세요.',
    672   'step.docker.eol': '이 컨테이너는 공식 PHP 이미지의 PHP {version} 버전을 실행합니다. PHP {branch} 브랜치는 지원이 종료(EOL)되어 수정된 {branch} 이미지가 나오지 않습니다. 지원되는 브랜치로 옮기세요. 아래 태그는 PHP {targetFixed} 버전을 사용하며, PHP {altFixed} 버전도 선택할 수 있습니다. {branch} 브랜치용으로 작성한 코드는 새 브랜치에서 수정이 필요할 수 있으니, 배포하기 전에 새 이미지로 애플리케이션을 테스트하세요.{extra}',
    673   'docker.oldDebian': '이 이미지는 Debian {from}({fromName}) 기반인데, 이 릴리스용 PHP 이미지는 더 이상 나오지 않으므로 아래 태그는 아직 이미지가 나오는 가장 가까운 릴리스인 Debian {to}({toName}) 기반 이미지를 씁니다. Dockerfile에서 apt로 설치하는 패키지는 이름이나 버전이 다를 수 있으니 빌드를 테스트하세요.',
    674   'docker.otherDebian': 'Debian {codename}용 PHP 이미지는 나오지 않으므로 태그에는 이미지의 기본 Debian 릴리스를 사용했습니다. 이 태그로 Dockerfile을 테스트하세요.',
    675   'docker.alpineFloating': '-alpine 태그는 최신 Alpine 릴리스를 따르며, 이는 지금 이미지가 쓰는 Alpine {release}보다 새 릴리스입니다. 이 태그로 Dockerfile을 테스트하거나, {example}처럼 Docker Hub에 있는 태그로 Alpine 릴리스를 고정하세요.',
    676   'step.docker.rebuild': '--pull 옵션으로 다시 빌드해 Docker가 캐시된 베이스 이미지 대신 새 베이스 이미지를 받게 하고, 실행 중인 컨테이너를 교체하세요. 기존 컨테이너를 재시작하면 이전 이미지가 그대로 쓰입니다. Docker만 쓴다면 아래처럼 빌드한 뒤 <your-image>로 새 컨테이너를 시작해 이전 컨테이너와 교체하세요. 빌드 없이 php 이미지를 그대로 실행한다면 빌드 대신 새 태그를 docker pull로 받으세요. Docker Compose에서는 마지막 세 명령이 build: 항목이 있는 서비스를 다시 빌드하고, image: 줄에 적힌 이미지를 받은 뒤, 이미지가 바뀐 컨테이너를 다시 만듭니다.',
    677   'step.docker.confirm': '새 컨테이너에서 PHP 버전을 확인하세요. 아래 명령은 {fixed} 버전을 출력해야 합니다. 그런 다음 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요.',
    678   'step.container.update': '이 컨테이너의 PHP는 공식 PHP 이미지가 아니라 패키지로 설치한 것입니다. {fix} 아래 줄을 Dockerfile에서 PHP를 설치하는 줄 뒤에 넣고(또는 수정이 이미 들어 있는 더 새로운 베이스 이미지로 옮기고), 다음 단계처럼 이미지를 다시 빌드해 컨테이너를 교체하세요.',
    679   'step.container.updatePro': '이 컨테이너의 PHP는 공식 PHP 이미지가 아니라 Ubuntu 패키지로 설치한 것입니다. {fix} 이 버전은 Ubuntu Pro에만 공개되어 있어 이미지 빌드 중에 Pro를 연결(attach)해야 받을 수 있습니다. 아래 줄을 Dockerfile에서 PHP를 설치하는 줄 뒤에 넣고, pro-attach-config.yaml 파일에 token: <token> 한 줄을 적은 뒤, 토큰이 이미지에 남지 않도록 다음 단계처럼 --secret 옵션으로 빌드하세요. Docker Compose에서는 compose.yaml에서 이 파일을 빌드 secret으로 지정하세요.',
    680   'step.container.upstream': '이 컨테이너는 PHP {version} 버전을 실행하는데, 배포판 패키지도 공식 PHP 이미지도 아닙니다. {branch} 브랜치는 PHP {fixed} 버전에서 수정되었습니다.{extra} PHP를 제공하는 베이스 이미지나 빌드를 {fixed} 버전으로 올린 뒤, 다음 단계처럼 이미지를 다시 빌드해 컨테이너를 교체하세요.',
    681   'step.container.other': '이 컨테이너의 PHP는 {vendor} 패키지입니다. Dockerfile에서 패키지 관리자로 업데이트하고(또는 더 새로운 베이스 이미지로 옮기고), {vendor}의 보안 권고에서 두 CVE를 확인하세요. 수정은 버전 번호를 바꾸지 않은 백포트로 나오거나 PHP {fixed} 이상 버전으로 나올 수 있습니다. 그런 다음 이어지는 단계처럼 이미지를 다시 빌드해 컨테이너를 교체하세요.',
    682   'step.container.eol': '이 컨테이너는 PHP {version} 버전을 실행합니다. PHP {branch} 브랜치는 지원이 종료(EOL)되어 {branch} 수정 릴리스가 나오지 않습니다. 이미지를 지원되는 PHP {target} 브랜치(수정 버전 {targetFixed})나 PHP {alt} 브랜치(수정 버전 {altFixed})로 옮기고, 배포하기 전에 애플리케이션을 테스트하세요.',
    683   'step.container.confirmFixed': '새 컨테이너에서 설치된 버전을 확인하세요. {fixed} 이상으로 나와야 합니다. 그런 다음 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요.',
    684   'step.container.confirm': '새 컨테이너에서 설치된 버전을 확인한 뒤, 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요.',
    685   'step.container.confirmChangelog': '새 컨테이너에서 {pkg}의 changelog에 {cves} 항목이 나오는지 확인한 뒤(두 번째 명령이 출력합니다), 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요.',
    686   'step.container.confirmChangelogFixed': '새 컨테이너에서 rpm -q가 {fixed} 이상을 보여 주고 {pkg}의 changelog에 {cves} 항목이 나오는지 확인한 뒤(두 번째 명령이 출력합니다), 웹 서버를 통해 PHP 프로브를 다시 실행하고 결과확인을 누르세요.',
    687   'step.container.restart': '이 컨테이너의 PHP 패키지는 업데이트됐지만, PHP 프로세스는 업데이트 전에 로드한 이전 빌드를 아직 실행하고 있습니다. 컨테이너를 재시작해 새 빌드를 로드하게 한 뒤, PHP 프로브를 다시 실행하고 결과확인을 누르세요.',
    688   'step.container.persist': '이 업데이트는 실행 중인 컨테이너 안에서만 이루어졌으므로, 같은 이미지로 새 컨테이너를 만들면 이전 빌드로 돌아갑니다. 이미지에도 업데이트를 넣으세요. 아래 줄을 Dockerfile에서 PHP를 설치하는 줄 뒤에 넣고 다시 빌드합니다.',
    689 
    690   // Interim mitigations and hardening
    691   'when.cannotUpdate': '바로 업데이트할 수 없다면',
    692   'when.pending': '수정 패키지가 공개될 때까지는',
    693   'when.eol': '지원되는 브랜치로 옮길 때까지는',
    694   'when.eolRelease': '지원되는 릴리스로 옮길 때까지는',
    695   'step.mitigate.apache': '{when} 공식 권고의 주요 임시 완화 설정인 세션 업로드 진행률 기능을 끄세요. 아래 명령은 Apache mod_php가 처리하는 모든 사이트에 이 설정을 적용하고, 설정을 검사한 뒤 Apache를 reload합니다(설정 변경에는 reload로 충분합니다).{extra} 업로드 진행률 표시만 멈출 뿐 업로드 자체는 그대로 동작합니다. 주석 처리된 memory_limit -1 줄은 Apache 서비스의 MemoryMax= 같은 PHP 바깥의 메모리 제한을 함께 둘 때만 주석을 해제하고, ulimit -v와는 함께 쓰지 마세요. 이 설정은 위험을 줄일 뿐 패치를 대신하지 않습니다.',
    696   'step.mitigate.fpm': '{when} 공식 권고의 주요 임시 완화 설정인 세션 업로드 진행률 기능을 끄세요. 아래 명령은 별도 파일로 PHP-FPM의 www 풀에 이 설정을 넣고, 설정을 검사한 뒤 PHP-FPM을 reload합니다(설정 변경에는 reload로 충분합니다).{extra} 업로드 진행률 표시만 멈출 뿐 업로드 자체는 그대로 동작합니다. 설정 검사가 "[pool www] user has not been defined" 오류를 내면 이 서버에는 www 풀이 없는 것이니, 대괄호 안에 사용하는 풀의 이름을 넣고, 다른 풀마다 같은 섹션을 추가하세요. 주석 처리된 memory_limit -1 줄은 서비스의 MemoryMax= 같은 PHP 바깥의 메모리 제한을 함께 둘 때만 주석을 해제하고, ulimit -v와는 함께 쓰지 마세요. 이 설정은 위험을 줄일 뿐 패치를 대신하지 않습니다.',
    697   'step.mitigate.fpmAlso': '이 PHP는 PHP-FPM에서도 실행됩니다. 아래 명령은 별도 파일로 PHP-FPM의 www 풀에도 같은 설정을 넣고, 설정을 검사한 뒤 PHP-FPM을 reload합니다(설정 변경에는 reload로 충분합니다).{extra} 설정 검사가 "[pool www] user has not been defined" 오류를 내면 대괄호 안에 사용하는 풀의 이름을 넣으세요. 이 임시 완화 설정은 위험을 줄일 뿐 패치를 대신하지 않습니다.',
    698   'step.mitigate.docker': '{when} 공식 권고의 주요 임시 완화 설정인 세션 업로드 진행률 기능을 끄세요. 앞의 두 명령은 실행 중인 컨테이너에 이 설정을 넣고 컨테이너를 재시작하며, RUN 줄은 이미지에도 설정이 남도록 Dockerfile에 넣는 줄입니다.{extra} 업로드 진행률 표시만 멈출 뿐 업로드 자체는 그대로 동작합니다. 이 설정은 위험을 줄일 뿐 패치를 대신하지 않습니다.',
    699   'step.mitigate.ini': '{when} 공식 권고의 주요 임시 완화 설정인 세션 업로드 진행률 기능을 끄세요. 아래 명령은 이 PHP가 읽는 {file}에 설정을 추가하고, 적용되도록 PHP를 재시작합니다.{extra} 업로드 진행률 표시만 멈출 뿐 업로드 자체는 그대로 동작합니다. 이 설정은 위험을 줄일 뿐 패치를 대신하지 않습니다.',
    700   'step.mitigate.iniNoRestart': '{when} 공식 권고의 주요 임시 완화 설정인 세션 업로드 진행률 기능을 끄세요. 아래 명령은 이 PHP가 읽는 {file}에 설정을 추가합니다. 그런 다음 적용되도록 PHP를 실행하는 서비스를 재시작하세요.{extra} 업로드 진행률 표시만 멈출 뿐 업로드 자체는 그대로 동작합니다. 이 설정은 위험을 줄일 뿐 패치를 대신하지 않습니다.',
    701   'mitigate.fatalBacktraces': ' PHP 8.5에서는 확인된 RCE 경로를 막는 fatal_error_backtraces도 끕니다.',
    702   'action.applyMitigations': '{when} 이 결과에 나온 임시 완화 설정을 적용하세요. 임시 완화 설정은 패치를 대신하지 않습니다.',
    703   'action.applyMitigationsAdvisory': '{when} 공식 권고의 임시 완화 설정을 적용하세요. session.upload_progress.enabled=0으로 설정하고, memory_limit=-1은 PHP 바깥의 메모리 제한(서비스의 MemoryMax= 같은 cgroup 제한)을 함께 둘 때만 쓰고 ulimit -v와는 함께 쓰지 마세요. 현재 값은 PHP 프로브로 확인할 수 있습니다. 임시 완화 설정은 패치를 대신하지 않습니다.',
    704   'action.lookForCompromise': '패치 전에 서버가 노출되어 있었다면 로그, 최근 변경된 PHP 파일, 웹 서버 계정으로 실행 중인 비정상 프로세스 등 침해 지표를 점검하세요. 아래처럼 서버 스크립트를 --logs와 --webroot(사이트 디렉터리, 예: /var/www) 옵션으로 실행하고 출력을 여기에 붙여 넣으세요.',
    705   'action.checkOtherSites': '같은 서버의 다른 사이트(vhost)와 PHP-FPM 풀도 모두 확인하세요. 저마다 다른 PHP를 실행할 수 있으므로 각각에서 PHP 프로브를 한 번씩 실행하세요.',
    706   'action.checkOtherContainers': 'PHP를 실행하는 다른 컨테이너와 이미지도 모두 확인하세요. 저마다 PHP 버전이 다를 수 있으므로 각 컨테이너에서 PHP 프로브를 한 번씩 실행하세요.',
    707   'step.hardening.expose': '선택 사항으로 expose_php를 꺼서 응답의 X-Powered-By 헤더에 PHP 버전이 나오지 않게 하세요. 공격자에게 주는 정보는 줄지만 업데이트를 대신하지는 않습니다. 아래 명령은 이 PHP에 설정을 추가하고, 적용되도록 서비스를 재시작합니다.',
    708   'step.hardening.exposeDocker': '선택 사항으로 expose_php를 꺼서 응답의 X-Powered-By 헤더에 PHP 버전이 나오지 않게 하세요. 아래 줄을 Dockerfile에 넣고 이미지를 다시 빌드합니다. 공격자에게 주는 정보는 줄지만 업데이트를 대신하지는 않습니다.',
    709   'step.hardening.exposePath': '선택 사항으로 이 PHP의 php.ini({path} 안)에 expose_php = Off를 설정하고 PHP를 실행하는 서비스를 재시작해, 응답의 X-Powered-By 헤더에 PHP 버전이 나오지 않게 하세요. 공격자에게 주는 정보는 줄지만 업데이트를 대신하지는 않습니다.',
    710   'step.hardening.exposeManual': '선택 사항으로 이 사이트를 처리하는 PHP의 php.ini에 expose_php = Off를 설정하고 PHP를 실행하는 서비스를 재시작해, 응답의 X-Powered-By 헤더에 PHP 버전이 나오지 않게 하세요. 공격자에게 주는 정보는 줄지만 업데이트를 대신하지는 않습니다.',
    711   'step.hardening.uploadOff': 'session.upload_progress.enabled가 꺼져 있습니다. 업로드 진행률 기능을 쓰지 않는다면 패치한 지금도 꺼 두세요. 공격자들이 이 기능의 다른 결함을 찾고 있어 공식 권고도 이를 권장합니다.',
    712   'step.hardening.uploadOn': '이 서버에서 업로드 진행률 기능을 쓰지 않는다면, 패치한 지금도 session.upload_progress.enabled를 0으로 설정해 꺼 둘 수 있습니다. 공격자들이 이 기능의 다른 결함을 찾고 있어 공식 권고도 이를 권장합니다.',
    713 
    714   // Interim mitigations
    715   'mit.value.notSet': '(설정 안 됨)',
    716   'mit.value.emptyOff': '(빈 값 = Off)',
    717   'mit.noChangeNeeded': '수정된 버전에서는 변경할 필요가 없습니다.',
    718   'mit.memoryLimit.patchedUnlimited': '수정된 버전에서는 -1이 필요하지 않습니다. PHP 메모리 제한이 없으면 서비스 거부(DoS) 위험이 있으므로 원래 값으로 되돌리는 것을 검토하세요.',
    719   'mit.memoryLimit.unlimited': 'CVE-2026-109618에 대한 주요 완화 설정이 적용되어 있습니다. 이제 PHP 자체에는 메모리 제한이 없으므로 cgroup(memory.max), pm.max_children, MaxRequestWorkers 등으로 PHP 바깥에서 메모리를 제한하세요. RLIMIT_AS나 RLIMIT_DATA는 사용하지 마세요. 오히려 취약한 경로가 실행됩니다.',
    720   'mit.memoryLimit.suggest': '-1로 설정하면 CVE-2026-109618 공격이 의존하는 예측 가능한 메모리 제한이 사라집니다. 공식 권고가 첫 번째로 제시한 완화 설정이지만 완전한 대책은 아닙니다. 서비스 거부(DoS) 위험이 생기므로 cgroup(memory.max), pm.max_children, MaxRequestWorkers 등으로 PHP 바깥에서 메모리를 제한하세요. RLIMIT_AS나 RLIMIT_DATA(ulimit -v)는 사용하지 마세요. 오히려 취약한 경로가 실행됩니다.',
    721   'mit.uploadProgress.noSession': 'session 확장이 로드되지 않았으므로 세션 경로는 해당되지 않습니다.',
    722   'mit.uploadProgress.off': 'CVE-2026-109619에서 확인된 세션 경로가 차단되어 있습니다. 공식 권고는 이 기능을 사용하지 않는 경우 패치 후에도 꺼 둘 것을 권장합니다.',
    723   'mit.uploadProgress.patchedOn': '업로드 진행률 기능을 사용하지 않는 경우, 공식 권고는 패치 후에도 이 설정을 꺼 둘 것을 권장합니다. 공격자들이 이 기능의 다른 결함을 찾고 있기 때문입니다.',
    724   'mit.uploadProgress.suggest': 'Off(0)로 설정하면 CVE-2026-109619에서 확인된 세션 경로가 차단됩니다. 먼저 업로드 진행률 기능을 사용하는지 확인하세요.',
    725   'mit.autoStart.off': '꺼져 있습니다.',
    726   'mit.autoStart.suggest': 'Xint의 재현 결과에 따르면 이 설정이 켜져 있으면 upload_progress가 꺼져 있어도 PHP가 취약합니다. 끄기 전에 session_start()를 호출하는 위치를 확인하고 로그인 흐름을 테스트하세요.',
    727   'mit.fatalBacktraces.off': 'PHP 8.5에서 확인된 CVE-2026-109619 RCE 경로가 차단되어 있습니다.',
    728   'mit.fatalBacktraces.suggest': 'PHP 8.5에서는 0으로 설정하면 CVE-2026-109619에서 확인된 RCE 경로가 차단됩니다(공식 권고).',
    729   'mit.nestingLevel': '기본값은 64입니다. 공격에 깊게 중첩된 필드 이름이 사용되므로, 애플리케이션이 허용하는 범위 안에서 낮게 설정할수록 위험이 줄어듭니다(공식 권고).',
    730   'mit.postMaxSize': '보수적인 값으로 유지하고, 웹 서버에서도 요청 크기를 제한하세요(Apache LimitRequestBody, Nginx client_max_body_size).',
    731   'mit.exposePhp.off': '응답 헤더에 PHP 버전이 노출되지 않습니다.',
    732   'mit.exposePhp.on': '응답 헤더(X-Powered-By)에 PHP 버전이 노출됩니다. 패치 후에는 Off로 설정하는 것을 권장합니다.',
    733 
    734   // SAPI exposure
    735   'exposure.apache2handler': 'Apache mod_php에서는 애플리케이션 코드와 관계없이 요청 본문을 파싱하는 단계에서 공격이 성공할 수 있습니다. 공식 권고가 이 구성의 기본 설정에서 RCE를 검증했으므로 가장 위험한 구성입니다.',
    736   'exposure.frankenphp': 'FrankenPHP는 mod_php처럼 요청 종료 처리를 항상 실행하므로 애플리케이션과 무관한 공격에 취약할 가능성이 높습니다(공식 권고).',
    737   'exposure.fpm': 'PHP-FPM은 요청 시작 단계가 실패하면 워커를 종료하므로 일부 경로는 막히지만, 애플리케이션 경로를 통한 공격은 여전히 가능합니다. 세션 경로를 통한 RCE(CVE-2026-109619)도 FPM에서 검증되었습니다.',
    738   'exposure.litespeed': 'LiteSpeed(LSAPI)는 FPM과 비슷하게 동작하므로 애플리케이션 경로를 통한 공격이 가능할 것으로 예상됩니다(공식 권고, 검증되지 않음).',
    739   'exposure.cli': '이 결과는 명령줄(CLI)에서 얻은 것입니다. 웹 서비스가 사용하는 PHP와 다를 수 있으므로 PHP 프로브를 웹 루트에 올리고 브라우저에서 실행하세요.',
    740   'exposure.other': '공식 권고는 {sapi}에 대해 따로 언급하지 않습니다. 패치를 적용할 때까지는 노출된 것으로 간주해야 합니다.',
    741 
    742   // Compromise indicators from the server script (--logs, --webroot)
    743   'finding.oom': ({ n }) => `로그에 스크립트 실행 전에 발생한 메모리 부족 오류("in Unknown on line 0")가 ${n}건 있습니다. 형식이 잘못된 multipart 요청을 이용한 공격 시도일 수 있습니다.`,
    744   'finding.crash': ({ n }) => `로그에 웹 서버 또는 PHP 비정상 종료(segfault)가 ${n}건 있습니다. 메모리 손상 공격 시도일 수 있습니다.`,
    745   'finding.bigSession': ({ n }) => `1 MB보다 큰 세션 파일을 ${n}개 발견했습니다. 세션 업로드 진행률 경로를 노린 공격의 흔적일 수 있습니다.`,
    746   'finding.webUserProc': ({ n }) => `웹 서버 계정(www-data, apache, nginx, nobody 등)으로 실행 중인 다른 프로세스를 ${n}개 발견했습니다. 서버 스크립트 출력에서 무엇인지 확인하세요. 셸, 다운로드 도구, 코인 채굴기가 있다면 침해를 의심하세요.`,
    747   'finding.recentPhp': ({ n }) => `최근 3일 이내에 변경된 PHP 파일을 ${n}개 발견했습니다. 직접 배포하지 않은 파일은 모두 웹셸일 가능성이 있는 것으로 간주하세요.`,
    748   'finding.unread': '일부 항목({items})을 읽지 못해 점검하지 못했습니다. root 권한으로 서버 스크립트를 실행하세요. 다른 항목이 0건이라고 해서 이 항목들까지 이상이 없다는 뜻은 아닙니다.',
    749   'finding.notRoot': '서버 스크립트를 root 권한으로 실행하지 않아 다른 계정이 소유한 로그, 세션 파일, 프로세스를 읽지 못했을 수 있습니다. 이 수치를 믿기 전에 sudo로 다시 실행하세요.',
    750   'finding.source.logs': '로그',
    751   'finding.source.sessions': '세션 파일',
    752   'finding.source.processes': '웹 서버 계정 프로세스',
    753   'finding.source.webroot': '웹 루트',
    754   'finding.none': '확인한 항목({items})에서는 의심스러운 흔적을 찾지 못했습니다. 흔적이 없다고 해서 침해가 없었다는 뜻은 아닙니다.',
    755   'finding.incident': '침해가 의심되면 서버를 네트워크에서 격리하고 로그를 보존하세요. 그런 다음 모든 데이터베이스 비밀번호, API 키, 세션 키와 암호화 키를 교체하세요. 해당 국가의 CERT/CSIRT(한국은 KISA 인터넷침해대응센터, 국번 없이 118)에 침해 사고를 신고할 수도 있습니다.',
    756 
    757   // Notes from reading the pasted input (parse.js)
    758   'parse.badJson': 'PHP 프로브 출력의 JSON 부분을 읽지 못했습니다. 출력 전체를 잘림 없이 복사했는지 확인하세요.',
    759   'parse.notSelfcheck': 'php2shell-selfcheck 출력 형식이 아닙니다.',
    760   'parse.dpkgUnreadable': 'open_basedir 등의 이유로 설치된 패키지 목록(/var/lib/dpkg/status)을 읽지 못했습니다. 배포판 백포트 확인이 부정확할 수 있으므로 서버 스크립트도 함께 실행하세요.',
    761   'parse.openBasedir': '이 사이트에 open_basedir가 설정되어 있어 프로브가 OS 릴리스 정보와 설치된 패키지 목록을 읽지 못했습니다. 이 정보 없이는 프로브만으로 배포판 백포트를 확인할 수 없습니다. 서버 스크립트도 함께 실행하거나, 배포판 목록에서 배포판을 고른 뒤 결과확인을 다시 누르세요.',
    762   'parse.osReleaseUnread': '프로브가 OS 릴리스 정보(/etc/os-release 또는 이전 방식의 릴리스 파일)를 읽지 못해 배포판과 설치된 패키지를 알 수 없습니다. 이 정보 없이는 프로브만으로 배포판 백포트를 확인할 수 없습니다. 서버 스크립트도 함께 실행하거나, 배포판 목록에서 배포판을 고른 뒤 결과확인을 다시 누르세요.',
    763   'parse.serverOtherSystem': 'PHP 프로브는 컨테이너 안에서 실행됐지만, 서버 스크립트 출력은 다른 시스템({os}, 아마도 호스트)에서 나왔습니다. 호스트의 패키지, changelog, 프로세스는 컨테이너 안의 PHP와 관계가 없으므로 사용하지 않았습니다. 컨테이너의 패키지를 확인하려면 컨테이너 안에서 서버 스크립트를 실행하세요(docker exec).',
    764   'parse.serverOtherOs': '서버 스크립트 출력은 PHP 프로브({probe})와 다른 시스템({os})에서 나왔습니다. 그 패키지, changelog, 프로세스는 이 PHP와 관계가 없으므로 사용하지 않았습니다. 이 PHP가 실행되는 서버에서 둘 다 실행하세요.',
    765   'parse.serverUnlinked': 'PHP 프로브는 컨테이너 안에서 실행됐지만, 서버 스크립트가 같은 컨테이너에서 실행됐다는 근거가 프로브 출력에 없습니다. 프로브가 자신의 OS 릴리스 정보나 패키지 목록을 읽지 못했거나, 패키지 목록이 서버 스크립트의 목록과 다릅니다. 서버 스크립트가 호스트에서 실행됐을 수 있으므로 그 패키지, changelog, 프로세스는 사용하지 않았습니다. 컨테이너의 패키지를 확인하려면 컨테이너 안에서 서버 스크립트를 실행하고(docker exec) 그 출력만 붙여 넣으세요.',
    766   'parse.serverConflict': 'PHP 프로브와 서버 스크립트가 서로 다른 PHP 패키지를 보고하므로, 두 출력은 다른 시점(예: 업데이트 전과 후)이나 다른 시스템에서 나온 것입니다. 서버 스크립트의 패키지, changelog, 프로세스는 사용하지 않았습니다.',
    767   'parse.serverTruncated': '서버 스크립트 출력에 END 줄이 없습니다. 복사하는 과정에서 일부가 잘린 것입니다. BEGIN 줄부터 END 줄까지 모두 복사하세요.',
    768   'parse.procBadStatus': '서버 스크립트 출력에서 {names}의 재시작 상태를 읽을 수 없어(줄이 잘렸을 수 있습니다) 이 프로세스들은 확인하지 못했습니다. 출력 전체를 다시 복사하세요.',
    769   'parse.probeNotDeleted': 'PHP 프로브가 서버에서 자동으로 삭제되지 않았습니다. 지금 직접 삭제하세요.',
    770   'parse.procUnreadable': ({ n }) => `권한이 부족해 PHP 프로세스${n ? ` ${n}개` : ''}의 재시작 누락 여부를 확인하지 못했습니다. root 권한으로 서버 스크립트를 다시 실행하세요.`,
    771   'parse.headerOnly': '응답 헤더는 해당 URL을 처리한 PHP만 보여 줍니다. 같은 서버의 다른 사이트와 PHP-FPM 풀은 따로 확인하세요.',
    772   'parse.cliVersion': 'php -v는 명령줄 PHP의 버전을 보여 줍니다. 웹 서비스가 사용하는 PHP와 다를 수 있으므로 PHP 프로브로 다시 확인하세요.',
    773   'parse.noVersion': 'PHP 버전을 찾지 못했습니다. PHP 프로브 출력 전체, X-Powered-By 헤더 또는 8.2.26 같은 버전 문자열을 붙여 넣으세요.',
    774   // Input that could not be read, or was cut
    775   'reason.notRead': '붙여 넣은 내용을 지원하는 출력 형식으로 읽지 못해 아무것도 확인하지 못했습니다. PHP 프로브 출력이나 서버 스크립트 출력을 BEGIN 줄부터 END 줄까지 다시 붙여 넣으세요.',
    776   'reason.inputCut': '입력이 너무 길거나 서로 다른 출력이 너무 많아 일부를 읽지 못했습니다. 읽지 않은 부분에 수정이 없는 PHP가 있을 수 있으므로 패치됨으로 확인할 수 없습니다.',
    777   'step.collect.shorter': '한 번에 붙여 넣는 양을 줄이세요. 서버 한 대의 PHP 프로브 출력과 서버 스크립트 출력을 각각 BEGIN 줄부터 END 줄까지 붙여 넣고 결과확인을 누르세요.',
    778   'parse.inputTooLong': '입력이 200만 자보다 길어 앞부분만 읽었습니다. 서버 한 대씩 출력을 붙여 넣으세요.',
    779   'parse.tooManyOutputs': '입력에 서로 다른 출력이 {n}개보다 많아 처음 {n}개만 확인했습니다. 한 번에 붙여 넣는 출력 수를 줄이세요.',
    780   'parse.serverNoBegin': '서버 스크립트 출력에 BEGIN 줄이 없어 첫 데이터 줄부터 END 줄까지 읽었습니다. 다음에는 BEGIN 줄부터 복사하세요.',
    781   'parse.serverHeadCut': '서버 스크립트 출력에 BEGIN 줄이 없고 첫 줄부터 시작하지도 않아 앞부분이 빠졌습니다. BEGIN 줄부터 END 줄까지 모두 복사하세요.',
    782   'reason.serverHeadCut': '서버 스크립트 출력에 BEGIN 줄이 없고 첫 줄부터 시작하지도 않아 앞부분(root 권한 실행 여부, OS 릴리스 정보)이 빠졌을 수 있으므로 패치됨으로 확인할 수 없습니다. 출력 전체를 다시 복사하세요.',
    783   'parse.serverUnlinkedHidden': 'open_basedir 때문에 PHP 프로브는 자신이 컨테이너 안에서 실행되는지 알 수 없었고, 출력에도 서버 스크립트 출력과 연결할 근거(OS 릴리스 정보나 패키지 목록)가 없습니다. 서버 스크립트 출력은 컨테이너의 호스트에서 나왔을 수 있으므로 그 패키지, changelog, 프로세스는 이 PHP에 사용하지 않았고, 그 출력은 따로 확인했습니다. 이 PHP가 컨테이너 안에서 실행된다면 컨테이너 안에서 서버 스크립트를 실행하고(docker exec), 그렇지 않다면 같은 PHP를 쓰면서 open_basedir가 설정되지 않은 사이트에서 PHP 프로브를 실행하세요.',
    784 
    785   // Downgrades, missing SAPI packages and the revision in PHP_VERSION (Debian and Ubuntu)
    786   'reason.sapiPackageMissing': '이 서버에서 PHP는 {sapi}로 실행되지만, 그 패키지({pkg})가 설치된 패키지 목록에 없습니다. 업데이트가 중단되면 패키지가 반쯤 설치된 상태로 남을 수 있으며, dpkg는 완전히 설치된 패키지만 목록에 보여 줍니다. 그렇지 않다면 이 PHP는 다른 곳에서 온 것입니다. Debian과 Ubuntu는 PHP 엔진을 SAPI마다 각자의 바이너리에 넣어 빌드하므로, 다른 {src} 패키지로는 여기서 실행되는 빌드를 알 수 없습니다.',
    787   'reason.revisionMissing': 'PHP_VERSION({version})에는 {family} 리비전이 없습니다. 그런데 {family} {release}에서는 업데이트된 PHP 빌드가 리비전을 함께 보고하므로, 설치된 패키지({installed})가 실행 중이라면 {expected}로 나와야 합니다. 리비전이 없는 버전은 그 릴리스의 최초 빌드만 보고하므로, 실행 중인 PHP는 업데이트 전에 로드된 이전 빌드이며 수정이 들어 있지 않습니다.',
    788   'reason.sapiPackageMissingProc': '이 브랜치의 PHP 프로세스({comm})가 실행 중이지만, 그 패키지({pkg})가 설치된 패키지 목록에 없습니다. 업데이트가 중단되면 패키지가 반쯤 설치된 상태로 남을 수 있으며, dpkg는 완전히 설치된 패키지만 목록에 보여 줍니다. Debian과 Ubuntu는 PHP 엔진을 SAPI마다 각자의 바이너리에 넣어 빌드하므로, 다른 {src} 패키지로는 {comm} 프로세스가 실행하는 빌드를 알 수 없습니다.',
    789   'note.olderPackageFixed': '설치된 {pkg} 패키지는 실행 중인 PHP({version})보다 오래되었지만 수정이 들어 있으므로, 다음에 재시작해도 수정된 빌드가 로드됩니다.',
    790   'fix.debDowngraded': '설치된 {pkg} 패키지는 실행 중인 PHP {version}보다 오래되었고 수정이 없습니다. 서비스가 재시작되기 전에 {fixed} 이상 버전을 다시 설치하세요.',
    791   'note.staleDowngraded': '프로세스({names})는 패키지가 다운그레이드되기 전에 로드한, 수정이 들어 있는 빌드를 아직 실행하고 있습니다. 수정 패키지를 다시 설치하기 전에는 이 프로세스들을 재시작하거나 reload하지 마세요. 취약한 빌드가 로드됩니다.',
    792   'reason.staleUnfixed': '실행 중인 프로세스({names})가 시작된 뒤 업데이트나 다운그레이드로 교체된 PHP 파일을 아직 사용하고 있습니다. 지금 설치된 빌드에 수정이 있는지는 확인되지 않았으므로 재시작만으로는 해결되지 않습니다. 먼저 수정 패키지를 설치한 뒤 재시작하세요.',
    793   'mitigate.staleReload': ' 프로세스({names})가 마지막 패키지 변경 이전의 파일을 아직 사용하고 있으며, reload하면 PHP-FPM이 지금 설치된 빌드를 로드합니다. 그 변경이 다운그레이드였다면 먼저 수정 패키지를 설치하세요.',
    794   'step.collect.sapiPackage': '아래 명령으로 {pkg} 패키지의 상태를 확인하세요. dpkg -l은 {src} 패키지마다 상태를 보여 주며(ii는 완전히 설치된 상태입니다), dpkg --audit는 업데이트가 중단되어 마무리되지 않은 패키지를 보여 줍니다. 마지막 두 명령은 그런 업데이트를 마무리합니다(apt-get install -f는 빠진 것을 설치하며, 무엇이든 바꾸기 전에 확인을 받습니다). 그런 다음 다음 단계처럼 PHP를 재시작하고 다시 확인하세요.',
    795 
    796   // deb.sury.org and Remi builds of end-of-life branches
    797   'note.suryEol': '이 PHP({version})는 업스트림 지원이 종료(EOL)된 PHP {branch} 브랜치의 deb.sury.org 빌드입니다. deb.sury.org는 이런 브랜치에 업스트림 버전을 그대로 둔 채 보안 수정을 백포트하므로, 빌드에 수정이 들어 있는지는 설치된 패키지의 changelog로만 알 수 있습니다.',
    798   'fix.suryEol': 'deb.sury.org는 지원 종료(EOL)된 PHP {branch} 브랜치에도 버전 번호를 그대로 둔 채 보안 수정을 백포트하므로, 최신 {src} 빌드에는 수정이 들어 있을 수 있습니다.',
    799   'step.collect.hiddenLink': 'PHP 프로브 출력에는 서버 스크립트 출력과 연결할 근거가 없습니다(참고 사항 참조). 이 PHP가 컨테이너 안에서 실행된다면 아래처럼 서버 스크립트를 컨테이너에 복사해 그 안에서 root로 실행하고, 그 출력만 붙여 넣으세요. 컨테이너가 아니라면 서버 스크립트 출력만 붙여 넣어도 이 서버의 PHP 빌드와 프로세스를 모두 확인할 수 있습니다. 또는 같은 PHP를 쓰면서 open_basedir가 설정되지 않은 사이트에서 PHP 프로브를 실행해 OS 릴리스 정보와 패키지 목록을 읽게 한 뒤, 그 출력을 서버 스크립트 출력과 함께 붙여 넣으세요.',
    800   'reason.suryChangelogFixed': '서버 스크립트가 설치된 버전에서 읽은 {pkg}의 changelog에 deb.sury.org가 백포트한 {cves} 수정이 기록되어 있습니다.',
    801   'reason.suryChangelogMissing': '서버 스크립트가 설치된 버전에서 읽은 {pkg}의 changelog에 {cves} 수정 기록이 없습니다. 이 빌드에는 아직 백포트가 들어 있지 않습니다.',
    802   'reason.suryBuiltBefore': '이 빌드({version})는 수정 릴리스가 나온 {released}보다 앞선 {date}에 만들어졌으므로 백포트된 수정이 들어 있을 수 없습니다.',
    803   'reason.suryEolUnread': '이 PHP {branch} deb.sury.org 빌드({version})에 백포트된 수정이 있는지는 패키지 changelog로만 알 수 있는데, 여기서는 changelog를 읽지 못했습니다. 설치된 모든 PHP 패키지의 changelog를 읽는 서버 스크립트를 실행하세요.',
    804   'note.remiEol': '이 PHP({pkg})는 Remi 저장소에서 설치한 것입니다. Remi의 지원 종료(EOL) 브랜치 컬렉션(php74, php80, php81)에는 버전 번호를 바꾸지 않고 보안 수정이 백포트되므로, 빌드에 수정이 들어 있는지는 패키지 changelog로만 알 수 있습니다.',
    805   'reason.remiChangelogMissing': '{pkg}의 changelog에 {cves} 수정 기록이 없습니다. 이 Remi 빌드에는 아직 백포트가 들어 있지 않습니다.',
    806   'reason.remiEolUnread': '이 PHP {branch} Remi 빌드({pkg})에 백포트된 수정이 있는지는 패키지 changelog로만 알 수 있는데, 여기서는 changelog를 읽지 못했습니다. 설치된 모든 PHP 패키지의 changelog를 읽는 서버 스크립트를 실행하세요.',
    807   'eol.hostSury': '이 서버에는 deb.sury.org 저장소가 설정되어 있어 apt가 배포판 패키지보다 그 저장소의 패키지를 우선합니다. 설치하기 전에 그 저장소를 끄고(/etc/apt/sources.list.d/ 안의 해당 파일), apt-cache policy에서 설치 후보가 {fixed}인지 확인하세요. 그러지 않으면 apt가 이 수정 패키지가 아닌 deb.sury.org의 {src}를 설치합니다.',
    808   'step.eol.hostSwitch': '당장 옮길 수 없다면, 이 {system} 서버에서는 배포판 자체의 {src} 패키지({fixed})에 두 수정이 백포트되어 있지만, 지금 설치된 {src} 패키지({foreign})는 다른 저장소에서 온 것입니다. 먼저 그 저장소를 끄고(/etc/apt/sources.list.d/ 안의 해당 파일이며, apt-cache policy로 알 수 있습니다) 아래 명령을 실행하세요. 두 번째 명령은 지금 {foreign} 버전인 {src} 패키지를 모두 {fixed} 버전으로 다시 설치하며, --allow-downgrades는 apt가 번호가 더 높은 빌드를 교체할 수 있게 합니다. 그런 다음 애플리케이션을 테스트하세요.{extra}',
    809 
    810   // RHEL family, Amazon Linux and other distributions
    811   'reason.rhelOlderThanFixed': '{pkg} 패키지는 두 CVE를 수정한 빌드 {fixed}({advisory})보다 오래되었습니다.',
    812   'reason.amazonOlder': '{pkg} 패키지는 업스트림 수정 릴리스인 PHP {fixed} 버전보다 오래되었습니다. Amazon Linux는 PHP 수정을 업스트림 릴리스로 제공합니다.',
    813   'step.rpm.confirmVersion': '업데이트가 설치됐는지 확인하세요. 이제 rpm -q가 {fixed} 이상을 보여 줘야 합니다. 이 수정은 새 업스트림 릴리스로 나왔고 changelog 항목에는 CVE가 아니라 버전이 적혀 있으므로 버전으로 확인합니다.',
    814   'reason.vendorBackports': '{vendor} 배포판은 PHP 패키지의 업스트림 버전을 그대로 두고 보안 수정을 백포트하므로, PHP {version} 버전은 업스트림 수정 릴리스보다 낮아도 두 수정이 들어 있을 수 있습니다. 버전만으로는 확인할 수 없으니 {vendor}의 보안 권고나 설치된 PHP 패키지의 changelog에서 두 CVE를 확인하세요.',
    815   'step.collect.vendor': '{vendor}의 보안 권고나 설치된 PHP 패키지의 changelog에서 두 CVE를 확인하세요. 아래 명령은 PHP 패키지 목록과 changelog의 CVE 줄을 보여 줍니다. 수정이 설치되어 있다면 CVE마다 한 줄씩 있어야 합니다.',
    816   'step.other.updateEol': '이 PHP는 {vendor}에서 제공한 것이며, 이 배포판은 버전 번호를 바꾸지 않고 PHP {branch} 패키지에 보안 수정을 백포트합니다. 아래 명령으로 최신 PHP 업데이트를 설치한 뒤, {vendor}의 보안 권고나 첫 번째 단계처럼 패키지 changelog에서 두 CVE를 확인하세요.',
    817 
    818   // Processes the server script could not check
    819   'reason.processesUnreadable': '서버 스크립트가 일부 PHP 프로세스({n}개)의 재시작 누락 여부를 확인하지 못했습니다. 프로세스를 읽지 못했거나 상태 줄이 잘렸습니다. 이 프로세스들은 여기서 확인한 PHP가 아니라 업데이트 후 재시작하지 않은 다른 PHP일 수 있으므로 패치됨으로 확인할 수 없습니다. root 권한으로 서버 스크립트를 실행하고 출력 전체를 붙여 넣으세요.',
    820   'reason.processesUnreadableRoot': '서버 스크립트가 root 권한으로도 일부 PHP 프로세스({n}개)의 재시작 누락 여부를 확인하지 못했습니다. 이 프로세스들은 여기서 확인한 PHP가 아니라 업데이트 후 재시작하지 않은 다른 PHP일 수 있으므로 패치됨으로 확인할 수 없습니다. CAP_SYS_PTRACE 권한이 없는 컨테이너 안이라면 호스트에서 서버 스크립트를 실행하거나, PHP마다 PHP 프로브로 확인하세요.',
    821   'reason.runningUncheckedRoot': '설치된 패키지에는 수정이 포함되어 있지만, 서버 스크립트가 root 권한으로도 일부 PHP 프로세스를 읽지 못해 실행 중인 PHP는 확인하지 못했습니다. CAP_SYS_PTRACE 권한이 없는 컨테이너 안이라면 호스트에서 서버 스크립트를 실행하거나 PHP 프로브를 실행해, 업데이트 후 실행 중인 PHP가 재시작됐는지 확인하세요.',
    822   'reason.runningPartlyHidden': '설치된 패키지에는 수정이 포함되어 있지만, 서버 스크립트를 root 권한으로 실행하지 않아 이 계정이 읽을 수 있는 PHP 프로세스만 확인했습니다. hidepid가 설정되어 있으면 /proc이 다른 계정의 프로세스를 완전히 숨기므로, 업데이트 후 재시작하지 않은 PHP가 있어도 보이지 않을 수 있습니다. sudo로 서버 스크립트를 다시 실행하세요.',
    823   'reason.serviceUnseen': '설치된 패키지에는 수정이 포함되어 있지만, 이 시스템에서는 서버 스크립트가 {names}의 프로세스를 볼 수 없어 업데이트 후 재시작됐는지 확인하지 못했습니다.',
    824   'step.collect.unreadableAsRoot': '서버 스크립트를 root 권한으로 실행했지만 일부 PHP 프로세스를 읽지 못했습니다. CAP_SYS_PTRACE 권한이 없는 컨테이너 안에서 실행하면 이렇게 되며, sudo로 다시 실행해도 결과는 같습니다. 대신 호스트에서 아래 명령으로 실행하거나(해시를 이 페이지의 서버 스크립트 탭에 나온 값과 비교하세요), 각 사이트에서 PHP 프로브를 실행하고 새 출력을 여기에 붙여 넣으세요.',
    825   'step.collect.unseen': '이 시스템에서는 서버 스크립트가 {names}의 프로세스를 볼 수 없습니다. 아래 명령으로 이 프로세스가 실행 중인지, 언제 시작됐는지 확인하세요. 업데이트 전에 시작된 프로세스가 있다면 다음 단계처럼 재시작하거나, 그 PHP를 PHP 프로브로 확인하세요.',
    826   'parse.procUnreadableRoot': ({ n }) => `root 권한으로도 PHP 프로세스${n ? ` ${n}개` : ''}의 재시작 누락 여부를 확인하지 못했습니다. 컨테이너 안에서 다른 계정의 프로세스 메모리 맵을 읽으려면 CAP_SYS_PTRACE 권한이 필요하므로, 호스트에서 서버 스크립트를 실행하거나 PHP 프로브를 사용하세요.`,
    827 };
    828 
    829 export const MESSAGES = { en, ko };
    830 
    831 const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
    832 
    833 // A date from the data files (2026-12-31) as the language writes it: "31 December 2026", "2026년 12월 31일".
    834 export function formatDate(iso, lang) {
    835   const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(iso || ''));
    836   if (!m) return String(iso || '');
    837   const [year, month, day] = [Number(m[1]), Number(m[2]), Number(m[3])];
    838   return lang === 'ko' ? `${year}년 ${month}월 ${day}일` : `${day} ${MONTHS[month - 1]} ${year}`;
    839 }
    840 
    841 const has = (table, key) => Object.prototype.hasOwnProperty.call(table, key);
    842 
    843 // Returns t(key, vars) for the language. Unknown languages use English, and so does a key
    844 // missing in the language. A key missing everywhere, or a placeholder with no value, throws.
    845 export function translator(lang) {
    846   const table = LANGS.includes(lang) ? MESSAGES[lang] : MESSAGES.en;
    847   return function t(key, vars = {}) {
    848     const msg = has(table, key) ? table[key] : has(MESSAGES.en, key) ? MESSAGES.en[key] : undefined;
    849     if (msg === undefined) throw new Error('Missing message: ' + key);
    850     if (typeof msg === 'function') return msg(vars);
    851     return msg.replace(/\{(\w+)\}/g, (_, name) => {
    852       const value = vars[name];
    853       if (value === undefined || value === null) throw new Error(`Missing value {${name}} for message: ${key}`);
    854       return String(value);
    855     });
    856   };
    857 }