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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/consumer/consumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
// Reads review jobs from Appwrite's queue with Appwrite's own queue library and hands each one
// to the Hansi service in the same container (apps/service), which starts this process.

// A stack trace prints each frame's arguments, and the queue's URL and password pass through
// the broker's: a consumer that dies before it reads the queue would write them to the logs.
ini_set('zend.exception_ignore_args', '1');

require __DIR__ . '/vendor/autoload.php';

use Hansi\Consumer\Adapter;
Expand Down
13 changes: 9 additions & 4 deletions apps/consumer/src/Broker.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ final class Broker
* @return \Closure(): (Consumer&Synchronous) One broker per call: a connection belongs to
* the worker process that opened it.
*/
public static function factory(string $url, string $queue, ?string $user = null, ?string $password = null): \Closure
public static function factory(
#[\SensitiveParameter] string $url,
string $queue,
?string $user = null,
#[\SensitiveParameter] ?string $password = null,
): \Closure
{
$dsn = new DSN($url);
$user = self::filled($user) ?? self::filled($dsn->getUser());
Expand All @@ -56,7 +61,7 @@ public static function factory(string $url, string $queue, ?string $user = null,
*
* @return \Closure(): Nats
*/
private static function nats(DSN $dsn, string $queue, ?string $user, ?string $password): \Closure
private static function nats(DSN $dsn, string $queue, ?string $user, #[\SensitiveParameter] ?string $password): \Closure
{
$options = new ConnectionOptions(
servers: 'nats://' . $dsn->getHost() . ':' . ($dsn->getPort() ?? '4222'),
Expand Down Expand Up @@ -110,7 +115,7 @@ public static function settings(ConsumerConfig $config): array
/**
* @return \Closure(): Redis
*/
private static function redis(DSN $dsn, ?string $user, ?string $password): \Closure
private static function redis(DSN $dsn, ?string $user, #[\SensitiveParameter] ?string $password): \Closure
{
$host = $dsn->getHost();
$port = (int) ($dsn->getPort() ?? 6379);
Expand All @@ -124,7 +129,7 @@ private static function redis(DSN $dsn, ?string $user, ?string $password): \Clos
}

/** Compose passes every variable it lists, so an empty one is unset. */
private static function filled(?string $value): ?string
private static function filled(#[\SensitiveParameter] ?string $value): ?string
{
return $value === null || $value === '' ? null : $value;
}
Expand Down
17 changes: 17 additions & 0 deletions apps/consumer/tests/BrokerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ public function testRefusesAQueueItCannotRead(): void
Broker::factory('amqp://rabbit:5672', 'v1-reviews');
}

public function testKeepsTheQueueCredentialsOutOfStackTraces(): void
{
$ignoring = ini_set('zend.exception_ignore_args', '0');
try {
Broker::factory('amqp://hansi:url-secret@rabbit:5672', 'v1-reviews', 'hansi', 'password-secret');
$this->fail('The broker accepted a queue it cannot read');
} catch (\InvalidArgumentException $error) {
$trace = $error->getTraceAsString();
} finally {
ini_set('zend.exception_ignore_args', (string) $ignoring);
}

$this->assertStringContainsString('Broker::factory(', $trace);
$this->assertStringNotContainsString('url-secret', $trace);
$this->assertStringNotContainsString('password-secret', $trace);
}

public function testReadsTheServicesOutcomes(): void
{
$this->assertSame(Outcome::Done, Outcome::fromResponse(['outcome' => 'done']));
Expand Down