Skip to content

First agent pass#1

Open
valzargaming wants to merge 21 commits intomasterfrom
first-agent-pass
Open

First agent pass#1
valzargaming wants to merge 21 commits intomasterfrom
first-agent-pass

Conversation

@valzargaming
Copy link
Member

This pull request introduces a new backend architecture for the project, implementing foundational modules for account, authentication, character, contract, and database management. It also updates dependencies and autoloading in composer.json to support the new backend structure. The most important changes are grouped below:

Backend Module Foundations

  • Added new backend modules for Account, Auth, Character, and Contract, each with their own Handler, Service, and Repository classes to establish clear separation of concerns and enable asynchronous operations using ReactPHP promises. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]
  • Implemented contract domain logic with status management (Contract.php), contract types (Types.php), and a resolver for contract completion (Resolver.php). [1] [2] [3]

Authentication and Security

  • Added JWT-like token creation and verification in TokenService.php, and an AuthMiddleware for request authentication, returning 401 responses for unauthorized access. [1] [2]

Database Layer

  • Introduced a basic synchronous ConnectionPool for PDO connections and a helper for generating PostgreSQL DSNs. [1] [2]

Composer and Autoloading Updates

  • Updated composer.json to include new dependencies (monolog/monolog, sharkk/router) and autoloading for the new backend namespace BackendPhpThis pull request introduces a new backend architecture for the project, implementing foundational modules for account, authentication, character, contract, and database management. It also updates dependencies and autoloading in composer.json` to support the new backend structure. The most important changes are grouped below:

Backend Module Foundations

  • Added new backend modules for Account, Auth, Character, and Contract, each with their own Handler, Service, and Repository classes to establish clear separation of concerns and enable asynchronous operations using ReactPHP promises. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]
  • Implemented contract domain logic with status management (Contract.php), contract types (Types.php), and a resolver for contract completion (Resolver.php). [1] [2] [3]

Authentication and Security

  • Added JWT-like token creation and verification in TokenService.php, and an AuthMiddleware for request authentication, returning 401 responses for unauthorized access. [1] [2]

Database Layer

  • Introduced a basic synchronous ConnectionPool for PDO connections and a helper for generating PostgreSQL DSNs. [1] [2]

Composer and Autoloading Updates

. [1] [2]

Frontend Architecture Documentation

  • Changed access modifiers in WebSocketManager class from private to protected in the frontend architecture design doc to allow for easier extension. [1] [2]

Copilot AI review requested due to automatic review settings February 27, 2026 21:51
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request lays the groundwork for a new backend architecture, establishing a robust and modular foundation for the project. It introduces core functionalities for user accounts, authentication, character management, and contract handling, alongside essential infrastructure for database interaction, dependency management, and server operations. The changes aim to create a scalable and maintainable system, supported by new utility classes and comprehensive testing.

Highlights

  • New Backend Architecture: Introduced a modular backend architecture with dedicated components for Account, Authentication, Character, and Contract management, promoting clear separation of concerns and supporting asynchronous operations.
  • Contract Domain Logic: Implemented core domain logic for contracts, including status management (PENDING, ACTIVE, PAUSED, RESOLVED), contract types, and a resolver for contract completion.
  • Authentication and Security: Established authentication mechanisms through a TokenService for JWT-like token creation and verification, complemented by an AuthMiddleware to secure API endpoints.
  • Database Layer Foundation: Developed a foundational database layer featuring a synchronous ConnectionPool for PDO connections and a helper for generating PostgreSQL DSNs, along with a migration runner.
  • Dependency and Autoloading Updates: Updated composer.json to include new dependencies such as monolog/monolog and sharkk/router, and configured PSR-4 autoloading for the new BackendPhp namespace.
  • Frontend Architecture Documentation: Modified the frontend architecture documentation to change access modifiers in the WebSocketManager class from private to protected, enhancing extensibility.
  • Core Domain Calculations: Added utility classes for calculating effort, efficiency, weighted expertise, and converting active seconds into work units.
  • Comprehensive Testing: Included new unit and integration tests covering contract lifecycle, domain calculations, part functionality, and reservation logic to ensure robustness.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Design Docs/03-frontend-architecture.md
    • Updated access modifiers for WebSocketManager properties and methods from private to protected.
  • composer.json
    • Added monolog/monolog and sharkk/router to required dependencies.
    • Configured PSR-4 autoloading for the BackendPhp namespace to src/IA/backend/.
  • src/IA/backend/Account/Handler.php
    • Added a handler for account-related requests.
  • src/IA/backend/Account/Repository.php
    • Added a repository for account data access.
  • src/IA/backend/Account/Service.php
    • Added a service for account business logic.
  • src/IA/backend/Auth/AuthMiddleware.php
    • Added an authentication middleware for token-based request validation.
  • src/IA/backend/Auth/Handler.php
    • Added a handler for authentication requests like login and registration.
  • src/IA/backend/Auth/TokenService.php
    • Added a service for creating and verifying JWT-like tokens.
  • src/IA/backend/Character/Handler.php
    • Added a handler for character-related requests.
  • src/IA/backend/Character/Repository.php
    • Added a repository for character data access.
  • src/IA/backend/Character/Service.php
    • Added a service for character business logic.
  • src/IA/backend/Contract/Contract.php
    • Added a class defining contract structure and lifecycle statuses.
  • src/IA/backend/Contract/Handler.php
    • Added a handler for contract creation requests.
  • src/IA/backend/Contract/PartRepository.php
    • Added a repository for hydrating contract parts from database rows.
  • src/IA/backend/Contract/Repository.php
    • Added a repository for contract data access.
  • src/IA/backend/Contract/Resolver.php
    • Added a resolver for contract completion logic.
  • src/IA/backend/Contract/Service.php
    • Added a service for contract business logic.
  • src/IA/backend/Contract/Types.php
    • Added a class defining various contract types as constants.
  • src/IA/backend/Database/ConnectionPool.php
    • Added a synchronous PDO connection pool.
  • src/IA/backend/Database/Postgres.php
    • Added a helper for generating PostgreSQL DSN strings.
  • src/IA/backend/Domain/EffortCalculator.php
    • Added a utility class for calculating efficiency and effort.
  • src/IA/backend/Domain/ExpertiseCalculator.php
    • Added a utility class for calculating weighted expertise from contributors.
  • src/IA/backend/Domain/WorkUnit.php
    • Added a utility class for converting active seconds into work units.
  • src/IA/backend/Migrations/Runner.php
    • Added a class to run pending database migrations.
  • src/IA/backend/Parts/Character.php
    • Added a data transfer object for character entities.
  • src/IA/backend/Parts/Contract.php
    • Added a data transfer object for contract entities.
  • src/IA/backend/Research/Handler.php
    • Added a handler for research-related requests.
  • src/IA/backend/Research/Repository.php
    • Added a repository for research data access.
  • src/IA/backend/Research/Service.php
    • Added a service for research business logic.
  • src/IA/backend/Research/TrialService.php
    • Added a service for running experimental trials.
  • src/IA/backend/Reservation/Service.php
    • Added a service for managing item inventory and reservations.
  • src/IA/backend/Server/Http.php
    • Added an HTTP server class using ReactPHP and Sharkk Router.
  • src/IA/backend/Server/WebSocket.php
    • Added a WebSocket server class skeleton.
  • src/IA/backend/Support/Config.php
    • Added a utility class for managing application configuration.
  • src/IA/backend/Support/Container.php
    • Added a simple dependency injection container.
  • src/IA/backend/Support/DiscordServiceProvider.php
    • Added a service provider for Discord client and factory integration.
  • src/IA/backend/Support/MonologFactory.php
    • Added a factory for creating Monolog logger instances.
  • src/IA/backend/Support/PartFactory.php
    • Added a factory for creating various backend parts, including Discord-compatible ones.
  • src/IA/backend/Support/ServiceProvider.php
    • Added an abstract base class for service providers.
  • src/IA/backend/Utils/Helpers.php
    • Added a utility class for accessing environment variables.
  • src/IA/backend/World/Ticker.php
    • Added a class for periodic world state updates.
  • src/IA/backend/bin/server.php
    • Added the main entry point script for the backend server.
  • tests/Contract/ContractLifecycleTest.php
    • Added unit tests for contract status transitions.
  • tests/Domain/ExpertiseTest.php
    • Added unit tests for the expertise calculation logic.
  • tests/EffortTest.php
    • Added unit tests for efficiency and effort calculation.
  • tests/IntegrationTest.php
    • Added an integration test for the migration runner with SQLite.
  • tests/Part/PartTest.php
    • Added unit tests for part magic access, nesting, and dirty tracking.
  • tests/Reservation/ReservationTest.php
    • Added unit tests for item reservation logic.
  • tests/WorkUnitTest.php
    • Added unit tests for work unit conversion.
  • tests/bootstrap.php
    • Added a bootstrap file for PHPUnit tests, including a Discord factory stub.
  • tests/integration_migrations/001_init.sql
    • Added an initial SQL migration file for integration testing.
Activity
  • Initial commit introducing the new backend architecture and foundational modules.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new backend architecture with well-organized modules, clear separation of concerns, and the use of ReactPHP for asynchronous operations, complemented by a comprehensive test suite. However, a critical security vulnerability has been identified in the authentication system: the TokenService lacks cryptographic signatures, allowing for trivial token forgery and authentication bypass. Additionally, a hardcoded development token was found in the authentication handler. Beyond these critical security concerns, attention is also needed for improving dependency management, error handling, encapsulation, and avoiding global state.

Comment on lines +18 to +27
public function createToken(array $payload): string
{
return base64_encode(json_encode($payload));
}

public function verifyToken(string $token): ?array
{
return json_decode(base64_decode($token), true) ?: null;
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The TokenService implementation for createToken and verifyToken is critically insecure. It lacks cryptographic signatures, allowing an attacker to forge tokens by modifying the JSON payload (e.g., changing the sub field) and bypass authentication, enabling impersonation of any user. It is recommended to use a standard like JSON Web Tokens (JWT) with a strong signature algorithm (e.g., RS256 or HS256) and a library such as firebase/php-jwt for secure implementation.

Comment on lines +48 to +50
} catch (\PDOException $e) {
$this->pool['default'] = new \PDO($dsn, $user, $pass, $opts);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The catch (\PDOException $e) block attempts a fallback connection but completely swallows the original exception. If both connection attempts fail, only the second exception will be visible, and the root cause of the first failure will be lost. This can make debugging database connection issues very difficult. It's important to log the caught exception before attempting the fallback.

            } catch (\PDOException $e) {
                // TODO: Log the exception $e to avoid losing information about why the first connection attempt failed.
                $this->pool['default'] = new \PDO($dsn, $user, $pass, $opts);
            }

{
public function login(ServerRequestInterface $request)
{
return ['token' => 'DEV-TOKEN'];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The login method returns a hardcoded token string 'DEV-TOKEN'. While likely intended for development purposes, if this code is deployed to production, it provides a predictable and static credential that could be used to bypass authentication if the rest of the system accepts it. This should be replaced with a dynamic token generation process once the TokenService is secured.

composer.json Outdated
"team-reflex/discord-php": "^10.45.22"
"team-reflex/discord-php": "^10.45.22",
"monolog/monolog": "^3.0",
"sharkk/router": "*"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The dependency sharkk/router is set to version *. This is not recommended as it can pull in breaking changes from new major versions automatically, which could break your application unexpectedly. Since this package does not have tagged versions, it's better to lock it to a specific commit hash to ensure repeatable builds.

Suggested change
"sharkk/router": "*"
"sharkk/router": "dev-main#f46338a"

Comment on lines +23 to +25
protected string $id;
protected string $status = self::STATUS_PENDING;
protected ?string $pauseReason = null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The properties id, status, and pauseReason are declared as protected. Since they are only accessed and modified through methods within this final class, they can be declared as private to improve encapsulation.

    private string $id;
    private string $status = self::STATUS_PENDING;
    private ?string $pauseReason = null;

* with this source code in the LICENSE.md file.
*/

namespace BackendPhp\Contract\Contract;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The namespace BackendPhp\Contract\Contract seems redundant. Given the file path src/IA/backend/Contract/PartRepository.php, the namespace should probably be BackendPhp\Contract to make the namespace structure more consistent and less confusing.

namespace BackendPhp\Contract;

Comment on lines +91 to +93
} catch (\Throwable $e) {
// fallthrough
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The catch (\Throwable $e) block is empty. If a date string is invalid and new \DateTimeImmutable($value) throws an exception, the error is silently ignored, and the code falls through to assign the original (invalid) string value to the attribute. This can hide data integrity issues. You should consider logging the exception to make it easier to track down invalid date formats.

            } catch (\Throwable $e) {
                // Consider logging this exception to track invalid date formats.
                // fallthrough
            }

Comment on lines +40 to +43
// Eagerly bind our PartFactory to use the Discord factory so parts created
// through PartFactory will use the Discord client where appropriate.
$factory = $container->get('discord.factory');
\BackendPhp\Support\PartFactory::setDiscordFactory($factory);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a static setter PartFactory::setDiscordFactory() introduces global state into the application. This can make the system harder to reason about and test, as the behavior of PartFactory::create() now depends on whether this static method has been called. A more robust approach would be to register PartFactory itself as a service in the container and inject it where needed, rather than relying on static methods.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR lays down an initial PHP backend foundation under the new BackendPhp\ namespace, including a minimal ReactPHP-based HTTP/WS server skeleton, basic domain utilities, a PDO migration runner/connection pool, and initial Pest-style tests.

Changes:

  • Add foundational backend modules (Account/Auth/Character/Contract/Research/Reservation) with Handler/Service/Repository scaffolding.
  • Introduce basic infrastructure (container, config, logging, parts factory integration with DiscordPHP, migrations runner, connection pool).
  • Update Composer dependencies/autoloading and add initial automated tests + an integration migration fixture.

Reviewed changes

Copilot reviewed 51 out of 52 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
composer.json Adds new dependencies and PSR-4 autoloading for BackendPhp\.
tests/integration_migrations/001_init.sql Adds a minimal SQL migration fixture for integration testing.
tests/bootstrap.php Adds test bootstrap and a Discord Part factory stub for tests.
tests/WorkUnitTest.php Adds WorkUnit conversion tests (Pest-style).
tests/Reservation/ReservationTest.php Adds Reservation service tests (Pest-style).
tests/Part/PartTest.php Adds tests for Part behavior (nested parts + dirty tracking).
tests/IntegrationTest.php Adds integration test for applying SQL migrations via SQLite memory DB.
tests/EffortTest.php Adds tests for effort/efficiency calculations.
tests/Domain/ExpertiseTest.php Adds tests for weighted expertise calculator.
tests/Contract/ContractLifecycleTest.php Adds tests for contract lifecycle state transitions.
src/IA/backend/bin/server.php Adds backend entrypoint to boot React loop and HTTP/WS skeleton servers.
src/IA/backend/World/Ticker.php Adds a periodic timer “ticker” skeleton for game loop logic.
src/IA/backend/Utils/Helpers.php Adds an env() helper wrapper.
src/IA/backend/Support/ServiceProvider.php Adds base ServiceProvider abstraction.
src/IA/backend/Support/PartFactory.php Adds PartFactory bridging local parts + DiscordPHP parts instantiation.
src/IA/backend/Support/MonologFactory.php Adds a simple Monolog logger factory.
src/IA/backend/Support/DiscordServiceProvider.php Adds DI registration for Discord client/factory and binds PartFactory.
src/IA/backend/Support/Container.php Adds a minimal service container for lazy service resolution.
src/IA/backend/Support/Config.php Adds a simple config wrapper and JSON config loader.
src/IA/backend/Server/WebSocket.php Adds a placeholder WS server class.
src/IA/backend/Server/Http.php Adds a React\Http server wrapper and router dispatch wiring.
src/IA/backend/Reservation/Service.php Adds a simple inventory reservation service.
src/IA/backend/Research/TrialService.php Adds a placeholder research trial service.
src/IA/backend/Research/Service.php Adds async-style research service using React promises.
src/IA/backend/Research/Repository.php Adds placeholder research repository.
src/IA/backend/Research/Handler.php Adds HTTP handler for research trial endpoint.
src/IA/backend/Parts/Contract.php Adds DiscordPHP-backed Contract Part with dirty tracking + serialization.
src/IA/backend/Parts/Character.php Adds DiscordPHP-backed Character Part with dirty tracking + serialization.
src/IA/backend/Migrations/Runner.php Adds a SQL migrations runner with a migrations tracking table.
src/IA/backend/Domain/WorkUnit.php Adds WorkUnit utility for WU conversion.
src/IA/backend/Domain/ExpertiseCalculator.php Adds weighted expertise calculation utility.
src/IA/backend/Domain/EffortCalculator.php Adds effort/efficiency calculation utilities.
src/IA/backend/Database/Postgres.php Adds a Postgres DSN helper.
src/IA/backend/Database/ConnectionPool.php Adds a basic single-connection PDO “pool”.
src/IA/backend/Contract/Types.php Adds contract type constants.
src/IA/backend/Contract/Service.php Adds async-style contract service scaffold.
src/IA/backend/Contract/Resolver.php Adds placeholder contract resolver.
src/IA/backend/Contract/Repository.php Adds async-style contract repository scaffold.
src/IA/backend/Contract/PartRepository.php Adds a repository to hydrate Contract parts from DB rows.
src/IA/backend/Contract/Handler.php Adds HTTP handler for contract creation.
src/IA/backend/Contract/Contract.php Adds contract domain model with basic status transitions.
src/IA/backend/Character/Service.php Adds async-style character service scaffold.
src/IA/backend/Character/Repository.php Adds async-style character repository scaffold.
src/IA/backend/Character/Handler.php Adds HTTP handler for character stats endpoint.
src/IA/backend/Auth/TokenService.php Adds token creation/verification utility.
src/IA/backend/Auth/Handler.php Adds placeholder auth handler endpoints.
src/IA/backend/Auth/AuthMiddleware.php Adds auth middleware that checks Bearer tokens.
src/IA/backend/Account/Service.php Adds async-style account service scaffold.
src/IA/backend/Account/Repository.php Adds async-style account repository scaffold.
src/IA/backend/Account/Handler.php Adds HTTP handler for account endpoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

$pdo->exec('CREATE TABLE IF NOT EXISTS migrations (id TEXT PRIMARY KEY, ran_at TIMESTAMPTZ DEFAULT now())');
}

$files = glob(rtrim($this->migrationsPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'*.sql');
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migrations are applied in the order returned by glob(), which is filesystem-dependent and not guaranteed to be sorted. For deterministic migration behavior, sort the list (e.g., by filename) before applying.

Suggested change
$files = glob(rtrim($this->migrationsPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'*.sql');
$files = glob(rtrim($this->migrationsPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'*.sql');
if ($files === false) {
$files = [];
}
sort($files, SORT_STRING);

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +69
public function jsonSerialize(): array
{
$out = [];
foreach ($this->getRawAttributes() as $k => $v) {
if ($v instanceof DiscordPart) {
$out[$k] = $v->jsonSerialize();
} elseif (is_array($v)) {
$out[$k] = array_map(fn ($x) => $x instanceof DiscordPart ? $x->jsonSerialize() : $x, $v);
} else {
$out[$k] = $v;
}
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON serialization currently passes DateTimeImmutable values through as objects, which json_encode() will not serialize into an ISO string (often resulting in {} in JSON). Consider detecting \DateTimeInterface values in jsonSerialize() and converting them to a string format (e.g., ATOM) so API consumers get usable timestamps.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +50
// Try DSN-only construction first (works for sqlite and many drivers),
// fall back to DSN+credentials+options if that fails.
try {
$this->pool['default'] = new \PDO($dsn);
$this->pool['default']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
} catch (\PDOException $e) {
$this->pool['default'] = new \PDO($dsn, $user, $pass, $opts);
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This DSN-only connection attempt can succeed while ignoring configured credentials/options (e.g., for drivers that allow OS-user auth), which makes behavior depend on environment and can be surprising in production. Prefer constructing PDO with DSN + credentials + options consistently (passing null is fine) so config is always honored.

Suggested change
// Try DSN-only construction first (works for sqlite and many drivers),
// fall back to DSN+credentials+options if that fails.
try {
$this->pool['default'] = new \PDO($dsn);
$this->pool['default']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
} catch (\PDOException $e) {
$this->pool['default'] = new \PDO($dsn, $user, $pass, $opts);
}
$this->pool['default'] = new \PDO($dsn, $user, $pass, $opts);

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +18
test('1 minute (60s) equals 1 WU', function () {
$wu = WorkUnit::fromActiveSeconds(60);
Assert::assertEquals(1.0, $wu);
});
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests use Pest's test() function, but the repository's phpunit.xml is configured to run under PHPUnit directly. Without running via vendor/bin/pest (or configuring PHPUnit to load Pest), this file will fatal with Call to undefined function test(). Either switch to PHPUnit-style TestCase classes or ensure the test runner is Pest-based.

Copilot uses AI. Check for mistakes.
* with this source code in the LICENSE.md file.
*/

namespace BackendPhp\Contract\Contract;
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Namespace/path mismatch: this file is located at BackendPhp/Contract/PartRepository.php but declares namespace BackendPhp\Contract\Contract; which breaks PSR-4 autoloading (BackendPhp\\ => src/IA/backend/). Adjust the namespace (or move the file) so it can be autoloaded.

Suggested change
namespace BackendPhp\Contract\Contract;
namespace BackendPhp\Contract;

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +81
$this->attributes[$name] = $value;
}

public function __isset(string $name): bool
{
return isset($this->attributes[$name]);
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overriding __set() here writes directly to $this->attributes, which may bypass DiscordPHP Part's built-in attribute/mutator logic (e.g., setCreatedAtAttribute() / setUpdatedAtAttribute() won’t run on property assignment). Consider delegating to parent::__set() when possible.

Suggested change
$this->attributes[$name] = $value;
}
public function __isset(string $name): bool
{
return isset($this->attributes[$name]);
parent::__set($name, $value);
}
public function __isset(string $name): bool
{
return parent::__isset($name);

Copilot uses AI. Check for mistakes.

public function create(ServerRequestInterface $request): PromiseInterface
{
return $this->service->create(json_decode((string) $request->getBody(), true));
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json_decode(..., true) can return null for invalid JSON, but Service::create() is typed to accept an array. This will raise a TypeError instead of returning a 4xx response. Validate the JSON decode result (and/or decode with JSON_THROW_ON_ERROR) and convert invalid JSON into a 400-style error response.

Suggested change
return $this->service->create(json_decode((string) $request->getBody(), true));
$body = (string) $request->getBody();
$data = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) {
return \React\Promise\reject(
new \InvalidArgumentException('Invalid JSON body for create request.')
);
}
return $this->service->create($data);

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +33
// Return account object for authenticated user
$res = ['id' => $auth['sub'] ?? null, 'banked' => 0];

return resolve($res);
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Account\Service::getAccount trusts the sub field from the auth request attribute as the account identifier without ensuring that this value comes from a cryptographically verified token. Given the current token implementation, an attacker can forge a token with an arbitrary sub and, once persistence is added, retrieve or operate on another user's account data. Even after hardening token verification, this method should treat auth as untrusted input and re-check authorization (e.g., by loading the account and confirming ownership/permissions) before returning sensitive account information.

Suggested change
// Return account object for authenticated user
$res = ['id' => $auth['sub'] ?? null, 'banked' => 0];
return resolve($res);
// Treat $auth as untrusted input and validate before using it to look up an account.
if (!is_array($auth) || !array_key_exists('sub', $auth)) {
// No subject present; fail closed.
return resolve(null);
}
$subject = $auth['sub'];
// Only accept simple scalar identifiers (e.g., string or int).
if (!is_string($subject) && !is_int($subject)) {
return resolve(null);
}
// Load the account from the repository based on the claimed subject.
$accountPromise = $this->repo->getAccountById((string) $subject);
// Confirm that the loaded account actually matches the claimed subject
// before returning sensitive account information.
return $accountPromise->then(
function ($account) use ($subject) {
if ($account === null) {
// No account found for this subject.
return null;
}
// Ensure the repository result matches the claimed subject.
if (is_array($account) && array_key_exists('id', $account)) {
if ((string) $account['id'] !== (string) $subject) {
return null;
}
}
return $account;
}
);

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +25
class TokenService
{
public function createToken(array $payload): string
{
return base64_encode(json_encode($payload));
}

public function verifyToken(string $token): ?array
{
return json_decode(base64_decode($token), true) ?: null;
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TokenService creates tokens by base64-encoding JSON and verifyToken simply decodes them without any signature, MAC, or expiry checks, so there is no guarantee the token was issued or approved by the server. An attacker can forge arbitrary tokens (e.g., with any sub or role claims) by base64-encoding their own JSON and have them accepted as valid by code that trusts this payload for authentication/authorization. Replace this with a cryptographically signed token scheme (such as JWT with HMAC/RS* using a server-side secret or opaque random tokens stored server-side) and enforce signature and expiration checks before treating a token as valid.

Suggested change
class TokenService
{
public function createToken(array $payload): string
{
return base64_encode(json_encode($payload));
}
public function verifyToken(string $token): ?array
{
return json_decode(base64_decode($token), true) ?: null;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class TokenService
{
private const SECRET_KEY = 'change_this_secret_key';
private const ALGORITHM = 'HS256';
private const DEFAULT_TTL_SECONDS = 3600;
public function createToken(array $payload): string
{
if (!isset($payload['exp'])) {
$issuedAt = time();
$payload['iat'] = $payload['iat'] ?? $issuedAt;
$payload['exp'] = $payload['exp'] ?? ($issuedAt + self::DEFAULT_TTL_SECONDS);
}
return JWT::encode($payload, self::SECRET_KEY, self::ALGORITHM);
}
public function verifyToken(string $token): ?array
{
try {
$decoded = JWT::decode($token, new Key(self::SECRET_KEY, self::ALGORITHM));
$decodedArray = json_decode(json_encode($decoded), true);
return is_array($decodedArray) ? $decodedArray : null;
} catch (\Throwable $e) {
return null;
}

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +35
$payload = $this->tokens->verifyToken($token);
if ($payload) {
return $next($request->withAttribute('auth', $payload));
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthMiddleware::handle treats any non-null payload from TokenService::verifyToken as an authenticated identity and passes it downstream via the auth request attribute. Because TokenService accepts any base64-encoded JSON as a "valid" token, an attacker can craft arbitrary Bearer tokens to impersonate other users or gain unauthorized access wherever auth is used for authorization decisions. After securing token creation/verification with proper signatures and expiry, this middleware should reject tokens that fail verification and enforce required claims (e.g., issuer, audience, expiration) before calling $next.

Suggested change
$payload = $this->tokens->verifyToken($token);
if ($payload) {
return $next($request->withAttribute('auth', $payload));
}
try {
$payload = $this->tokens->verifyToken($token);
} catch (\Throwable $e) {
$payload = null;
}
if ($payload !== null) {
// Normalize payload to array for claim checks
if (is_array($payload)) {
$claims = $payload;
} elseif ($payload instanceof \stdClass) {
$claims = (array) $payload;
} else {
$claims = null;
}
// Require a valid, non-expired 'exp' claim before treating the token as authenticated
if (is_array($claims)
&& array_key_exists('exp', $claims)
&& (is_int($claims['exp']) || ctype_digit((string) $claims['exp']))
&& (int) $claims['exp'] >= time()
) {
return $next($request->withAttribute('auth', $payload));
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants