Skip to content

[Invoices]: Peppol hardening follow-ups (Qonto settings contract, log redaction, audit-tool gap, scheduler cron) #774

Description

@nielsdrost7

Summary

A checklist of smaller hardening gaps found while auditing feature/126-implement-peppol, each individually small but worth tracking rather than losing.

1. Qonto's settings() declares an optional key as mandatory

Modules/Invoices/Peppol/Clients/Qonto/QontoClient::settings() returns ['access_token', 'staging_token']. QontoProvider doesn't override authenticate(), so it falls through to BaseProvider::authenticate() (Modules/Invoices/Peppol/Providers/BaseProvider.php:106-117), which loops over every key settings() returns and fails if any is empty — treating both as equally mandatory. But staging_token is used only conditionally (QontoClient::getAuthenticationHeaders(): if ($this->stagingToken) { ... }) — it's an optional sandbox header, not a real auth requirement. Net effect: a fully and correctly configured production Qonto integration (real access_token, no staging_token) would report authenticate() === false. Compare with EInvoiceBeProvider (['api_key']) and StorecoveProvider (['api_key', 'legal_entity_id']), where every listed key genuinely is unconditionally required — Qonto is the one sibling that drifted from that pattern. Currently inert (nothing calls ->authenticate() on any provider yet outside the class hierarchy itself), but part of the public ProviderInterface contract, so it's a landmine for whenever something starts calling it.

Fix shape: decide how ProviderInterface should express "required vs. optional" settings (e.g. a second optionalSettings() method, or a keyed array with per-key required flags) and update BaseProvider::authenticate()'s default accordingly.

2. Request/response logger's secret redaction has a gap

The request/response logger's secret redaction misses the request payload and response body — dormant today (the feature flag is off by default, never enabled in production code), but would leak plaintext client_secret/access_token the moment it's switched on for debugging.

Fix shape: extend the redaction to cover the full request payload and response body, not just whatever fields it currently covers.

3. FormDbConstraintAuditTest has a blind spot: it can't see a NOT-NULL column missing from a form entirely

Modules/Core/Tests/Feature/FormDbConstraintAuditTest.php only iterates fields that are present in the resolved form schema and checks whether their rules are strict enough — it never walks the DB schema the other way to ask "is every NOT-NULL/no-default column represented by some form field at all?" This is exactly why it stayed green despite merchant_clients.client_id being NOT NULL with no form field (fixed in the PR this issue was filed from, but the audit tool itself still can't catch the next occurrence of this bug class).

Fix shape: extend auditResource() with a reverse pass — for each NOT-NULL, no-default, non-autoincrement column not in {id, created_at, updated_at, deleted_at}, assert it has a corresponding entry in the form's fields, subject to the same FormDbGapKnownExceptions::KNOWN_GAPS allowlist.

4. No cron/scheduler runner exists in the dev or deploy stack

bootstrap/app.php now has a withSchedule() callback registering peppol:poll-status (every 15 min) and peppol:retry-failed (every minute) — but nothing in the project's docker stack (ivpldock-workspace-1 and friends) or deploy configuration actually runs php artisan schedule:run on a timer. Confirmed: no cron container, no supervisor entry, no /etc/cron.d/* beyond the stock PHP session-cleanup job. Laravel's scheduler is inert without something invoking schedule:run every minute.

Fix shape: add a cron entry or supervisor process to the deploy/dev stack that runs php artisan schedule:run every minute (commonly a dedicated lightweight scheduler container, or a cron line in the existing php-fpm/workspace image).

Source

Found by mind-the-gap and release-resilience audits of feature/126-implement-peppol vs develop on 2026-09-12.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions