fix: keep the storage connection out of refusals - #255
Merged
Merged
Conversation
|
getDevice() receives the connection string with its access and secret keys in it, and a refusal leaked it three ways: - its message forwarded utopia-php/dsn's, which in 0.2.1 and earlier quotes the whole DSN back when parse_url() rejects it; - the chained DSN exception carried that same message, and its DSN::__construct() frame, into anything that prints the exception; - every exception raised beneath getDevice() printed the connection argument in its trace, cut to 15 bytes by default, which is the scheme and the start of the credentials. Traces carry arguments unless zend.exception_ignore_args is on, and it is off by default and in appwrite/utopia-base, which ships no php.ini. The message matters most: the build path copies an exception's message into the build output it returns, so a malformed OPR_EXECUTOR_CONNECTION_STORAGE would put the operator's keys in build logs. The refusal now only says the DSN could not be parsed, and chains nothing, so what it prints no longer depends on which dsn version a consumer resolves (utopia-php/dsn#10 fixes the parser's message at the source). $connection is #[\SensitiveParameter], as utopia-php/storage already does for the secret key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abnegate
force-pushed
the
fix-storage-dsn-trace-redaction
branch
from
September 24, 2026 08:11
66c073f to
a2373d6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What leaked
StorageFactory::getDevice()receives the connection string, access and secret keys included. A refusal leaked it in three ways:parse_url()rejects it:Unable to parse storage DSN: Unable to parse DSN: s3://KEY:SECRET@/bucket....previous:carried that same message, plus aDSN->__construct('s3://KEY:SECR...')frame, into anything that prints the exception.getDevice()printed the$connectionargument, cut to 15 bytes by default: the scheme and the start of the credentials. Traces carry arguments unlesszend.exception_ignore_argsis on. It's off inappwrite/utopia-base:php-8.5-2.0.0(production) andphpswoole/swoole:6.2.0-php8.5-alpine(CI), and neither ships a php.ini.The message is the most exposed.
Runner\Dockercopies an exception's message into the build output it returns ('content' => $throwable->getMessage(), and theFailed to store cache artifact:warnings). So a malformedOPR_EXECUTOR_CONNECTION_STORAGEorOPR_EXECUTOR_CONNECTION_BUILD_CACHE_STORAGEwould put the operator's storage keys into build logs.Fix
Unable to parse storage DSNand chains nothing. What it prints no longer depends on which utopia-php/dsn version a consumer resolves. The parser's own message is fixed at the source in fix: keep credentials out of DSN parse failures utopia-php/dsn#10.$connectionis#[\SensitiveParameter], as utopia-php/storage already does for$secretKey.The cost is the parser's reason (
malformed,scheme is required,host is required). That detail isn't worth a leak that depends on the dependency version, and the message still names the setting to check.Uncaught output with this PR, while dsn 0.2.1 is still locked:
Before, the same input printed the DSN twice in full (the chained message and the forwarded one) and three more times, truncated, in trace frames. Executor's own
has no devicerefusal went fromgetDevice('/', 'ftp://AKIAKEY:S...')togetDevice('/', Object(SensitiveParameterValue)).Tests
testRefusalDoesNotPrintTheConnectiongoes through every connection inunusableConnections()with argument capture on and the length limit lifted. It asserts that the printed exception (message, trace and any chain) shows the$rootargument, which proves arguments were captured, but not the connection. Onmainit fails withInvalidArgumentException: Unable to parse DSN: s3://accessKey:secret@/mybucket?region=garage ... Next InvalidArgumentException: Unable to parse storage DSN: Unable to parse DSN: s3://accessKey:secret@....Ran locally with the CI images:
composer test:unitcomposer format:checkcomposer analyzecomposer refactor:checkFollow-up
Once utopia-php/dsn#10 is released, bump utopia-php/dsn (currently
0.2.*, lock at0.2.1) so directDSNusers get the fixed message too.🤖 Generated with Claude Code