Skip to content

Conversation

@depfu
Copy link
Contributor

@depfu depfu bot commented Jan 30, 2026


🚨 Your current dependencies have known security vulnerabilities 🚨

This dependency update fixes known security vulnerabilities. Please see the details below and assess their impact carefully. We recommend to merge and deploy this as soon as possible!


Here is everything you need to know about this update. Please take a good look at what changed and the test results before merging this pull request.

What changed?

↗️ psy/psysh (indirect, 0.12.8 → 0.12.19) · Repo

Security Advisories 🚨

🚨 PsySH has Local Privilege Escalation via CWD .psysh.php auto-load

Summary

PsySH automatically loads and executes a .psysh.php file from the Current Working Directory (CWD) on startup. If an attacker can write to a directory that a victim later uses as their CWD when launching PsySH, the attacker can trigger arbitrary code execution in the victim's context. When the victim runs PsySH with elevated privileges (e.g., root), this results in local privilege escalation.

Details

PsySH supports per-directory configuration via a .psysh.php file located in the process CWD. This file is executed implicitly when PsySH starts, without requiring explicit opt-in and without validating that the file and directory are safe (e.g., owned by the current user and not group/world-writable).

This enables a CWD poisoning scenario: a low-privileged user can plant a malicious .psysh.php in any directory they can write to, then wait for a higher-privileged user to start PsySH while their shell is in that directory.

PoC

  1. As a low-privileged user, create a malicious .psysh.php in an attacker-writable directory (example: /tmp):
bob@localhost:/tmp$ echo "<?php system('id > poc.txt'); ?>" > .psysh.php
bob@localhost:/tmp# ls -lah .psysh.php
-rw-r--r-- 1 bob bob 33 Jan 28 11:17 .psysh.php
  1. As the victim user, start PsySH with CWD set to that directory and exit:
root@localhost:/tmp# cd /tmp
root@localhost:/tmp# ./psysh
Psy Shell v0.12.18 (PHP 8.1.2-1ubuntu2.23 — cli) by Justin Hileman
New PHP manual is available (latest: 3.0.1). Update with `doc --update-manual`
> exit

INFO Goodbye.

  1. Verify code execution triggered in the victim context:
bob@localhost:/tmp$ ls -lah poc.txt
-rw-r--r-- 1 root root 39 Jan 28 11:19 poc.txt
bob@localhost:/tmp$ cat poc.txt
uid=0(root) gid=0(root) groups=0(root)

Impact

This is a CWD configuration poisoning issue leading to arbitrary code execution in the victim user’s context. If a privileged user (e.g., root, a CI runner, or an ops/debug account) launches PsySH with CWD set to an attacker-writable directory containing a malicious .psysh.php, the attacker can execute commands with that privileged user’s permissions, resulting in local privilege escalation.

Downstream consumers that embed PsySH inherit this risk. For example, Laravel Tinker (php artisan tinker) uses PsySH. If a privileged user runs Tinker while their shell is in an attacker-writable directory, the .psysh.php auto-load behavior can be abused in the same way to execute attacker-controlled code under the victim’s privileges.

Release Notes

0.12.18

  • Fix exit() not working when uopz extension is loaded
  • Don't reopen pager if user closes it early
  • Ensure stty state is restored before exiting PsySH (fixes an issue where Ctrl-C might be incorrectly handled after exiting)

0.12.17

Hot code reloading!!?!?1?

Install the uopz extension (5.0+) and PsySH will automatically reload modified files during your session. Edit code, switch back to PsySH, and your changes are live—no restart needed!

What gets reloaded

  • Method bodies (including private/protected)
  • Function implementations (and new functions!)
  • Class and global constants

What can't be reloaded

  • New class methods
  • Class properties, inheritance, or interfaces
  • Method signatures

PsySH skips "risky" reloads by default (conditional definitions, static variables). Use the new yolo command to bypass safety checks:

>>> my_helper()
Warning: Skipped conditional: if (...) { function my_helper() ... }

>>> yolo !!
=> "result"

See the documentation for more details.

Bug fixes

  • Fix "array offset on null" warning on Ctrl-C — plays nicer with Laravel + PHP 8.5
  • Work around O(n²) performance in Symfony OutputFormatter

0.12.16

A quick release adding support for Symfony Console v7.4+ and v8.x.

0.12.15

Abbreviated output reverted

The abbreviated return value output introduced in v0.12.13 has been reverted. This feature attempted to show shorter output for statements that looked like they were trying to take an action (like assignments and method calls with side effects) while preserving full output for inspection-like statements. Unfortunately, this version of the feature just ... wasn't it.

Thanks to everyone who provided feedback. If you've got thoughts on approaches that could make this better, please share them in #512!

In-shell manual updates

You can now update the PHP manual directly from inside the shell! Run doc --update-manual to fetch the latest manual version.

More robust manual handling

  • Log a warning (and continue) when trying to read from an invalid manual file
  • Show invalid manual info in --info and \Psy\info()
  • Prompt to clean up invalid manual files when running --update-manual
  • Prompt to upgrade to v3 manual (preserving language selection!) when running --update-manual with an existing sqlite manual
  • Preserve legacy manuals when updating to v3, supporting systems with multiple PsySH versions installed

Bug fixes

  • Fix namespace and use statement edge cases where aliases weren't properly tracked across REPL inputs
  • Fix history command filtering and --head/--tail interaction to apply filters first
  • Fix E_STRICT warning in PHP 8.4
  • Fix ParseCommand parsing (you had one job ಠ_ಠ)

Other improvements

  • Add a hint about doc foo when help foo doesn't match a known command
  • Don't call deprecated curl_close() on PHP >= 8.0.0 (thanks @mpesari!)
  • Lock phar build dependencies for reproducible builds
  • Improve PHP 8.5 support
  • Improve test coverage

0.12.14

Logging support

Log user input, command invocations, and executed code to a PSR-3 logger or callback.

// Simple callback
$config->setLogging(function ($kind, $data) {
    file_put_contents('/tmp/psysh.log', "[$kind] $data\n", FILE_APPEND);
});

// PSR-3 logger with granular control
$config->setLogging([
'logger' => $psrLogger,
'level' => [
'input' => 'info',
'command' => false, // disable
'execute' => 'debug',
],
]);

This has been one of our longest-requested features (🙈) and it fixes #821, #651, and #565.

Huge documentation improvements!

This release adds a new PHP-based manual data format (v3), replacing the previously used sqlite db. The new format:

  • Allows runtime formatting (especially wrapping) rather than pre-rendering at build time.
  • Adds OSC 8 links to php.net!
  • Is smaller, faster, and more flexible than the sqlite format.
  • ... and no longer requires sqlite support to use!

The phar builds now bundle the PHP manual, so these changes work out of the box. There are also automatic update notifications, an an --update-manual command to keep docs current.

If you've already got a manual file installed, remove it then run psysh --update-manual (or psysh --update-manual=LANG) after upgrading!

Fixes #595 and #406.

Other improvements

  • Prettier formatting for --help output
  • Standardized user-facing path pretty printing

0.12.13

... pushing the limits of what we can plausibly put in a point release.

Autoload warming

Added opt-in autoload warming to improve tab completion and command support. When enabled, PsySH pre-loads classes at startup, making them available to ls, doc, show, and tab completion.

// Enable with defaults (project classes only)
'warmAutoload' => true,

// Advanced configuration
'warmAutoload' => [
'includeTests' => true,
'excludeNamespaces' => ['App\Tests'],
'includeVendorNamespaces' => ['Symfony', 'Doctrine'],
],

Custom warmers can be implemented via AutoloadWarmerInterface. Fixes #650

Implicit use statements

Auto-adds use statements when you reference a class by its short name (and there's exactly one match in configured namespaces).

'implicitUse' => [
    'includeNamespaces' => ['App\'],
    'excludeNamespaces' => ['App\Legacy\'],
],

Works great with autoload warming to make class references feel natural.

Namespace-aware commands

doc, show, and ls commands now resolve class names using the current namespace and use statements, just like code execution does.

SIGINT handling

Hitting ctrl-c during long-running code now interrupts execution and returns to the prompt instead of exiting the shell entirely. Works with or without process forking (requires pcntl and posix support). Fixes #154

Exit status support

PsySH now properly handles exit status codes! Use exit(42) for non-zero status codes or exit('message') to print before exiting. Also exits with non-zero status on unrecoverable errors.

Clickable documentation links

Class names, functions, interfaces, constants, and more in ls, doc, and show commands are now clickable links to php.net (requires Symfony 4.3+, PsySH manual installed, and OSC 8 compatible terminal).

Other improvements

  • Added --info CLI option
  • Added --warm-autoload CLI option
  • Included traits in ClassNamesMatcher tab completion
  • Print shorter return values for actions than inspection
  • Improved PHPStan coverage (levels 2 and 3)
  • More robust smoketests

0.12.12

Fixing unintentional PHP support version regression due to editor autoformatting :(

Includes the following from v0.12.11:

  • Fix ob_start deprecation in PHP 8.5 (thanks @Ayesh!)

0.12.11

  • Fix ob_start deprecation in PHP 8.5 (thanks @Ayesh!)

0.12.10

  • Remove deprecated setAccessible() calls in reflection (Thanks @W0rma!)
  • Update composer urls to https (Thanks @bebehr!)
  • Stop autocompleting function names after -> (Thanks @igorsantos07!)

0.12.9

  • Stop freaking out if the output pager is closed without writing everything.
  • Prevent deprecation notice when parsing some anonymous classes.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.


Depfu Status

Depfu will automatically keep this PR conflict-free, as long as you don't add any commits to this branch yourself. You can also trigger a rebase manually by commenting with @depfu rebase.

All Depfu comment commands
@​depfu rebase
Rebases against your default branch and redoes this update
@​depfu recreate
Recreates this PR, overwriting any edits that you've made to it
@​depfu merge
Merges this PR once your tests are passing and conflicts are resolved
@​depfu cancel merge
Cancels automatic merging of this PR
@​depfu close
Closes this PR and deletes the branch
@​depfu reopen
Restores the branch and reopens this PR (if it's closed)
@​depfu pause
Ignores all future updates for this dependency and closes this PR
@​depfu pause [minor|major]
Ignores all future minor/major updates for this dependency and closes this PR
@​depfu resume
Future versions of this dependency will create PRs again (leaves this PR as is)

@depfu depfu bot added the depfu label Jan 30, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants