Skip to content

Releases: nowo-tech/VerifactuBundle

Release v1.4.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 07:07

v1.4.0

Changelog

SEC-004 hardening: PEM loader fix, submit-sandbox refuses production, production requires persistent hash chain, CIF check digit, strict dates/amounts, explicit cURL TLS policy, dist export-ignore See UPGRADING.

Security hardening from the independent SEC-004 review of 1.3.0 (Pass (good) / Low, Appendix BI). See UPGRADING.

Security

  • L1 — PEM certificates: CertificateLoader returned the public key as private_key and the whole PEM file (private key block included) as certificate, so PEM signing failed and the key travelled with the certificate. It now exports the private key and the X.509 certificate separately, refuses a PEM file without a certificate or with a certificate that does not match the key, and no longer returns the password in the key material.
  • L2 — nowo:verifactu:submit-sandbox: --submit outside the sandbox environment is refused unless --allow-production, explicit --nif / --numserie and an interactive confirmation are given (it used to only print a warning).
  • L3 — production chain storage: the container refuses aeat.environment: production with an AEAT client (certificate_path or submission_client) while hash_chain.storage is memory and no hash_chain.repository is set.
  • L4 — dist archives: .gitattributes export-ignore and composer.json archive.exclude keep tests/ (throwaway test.p12), demo/, scripts/, specs/, .specify/, .cursor/ and tooling out of dist archives.
  • #[\SensitiveParameter] on the certificate password of SoapAeatSubmissionClient and XadesBillingRecordSigner; scripts/aeat-sandbox-submit.php reads AEAT_CERT_PASSWORD and warns when --cert-password is used.
  • CurlSoapTransport sets an explicit TLS policy: HTTPS only, no redirects, CURLOPT_SSL_VERIFYPEER / CURLOPT_SSL_VERIFYHOST = 2, 10 MiB response cap (MAX_RESPONSE_BYTES).
  • twig/twig floor raised to ^3.19 (patched line).
  • Demo: published port bound to 127.0.0.1; the audit log masks the issuer NIF.

Fixed

  • SpanishTaxIdValidator now checks the CIF control character (digit or letter, by entity type); before, any CIF with a valid shape was accepted although the docs claimed a check digit.
  • AeatBusinessRulesValidator rejects impossible calendar dates (31-02-2026) and amounts outside the AEAT format (exponents, more than 2 decimals, more than 12 integer digits).
  • XML text values use ENT_SUBSTITUTE, so invalid UTF-8 no longer produces an empty element.
  • doctrine.table_prefix validation regex is anchored with D (a trailing newline is rejected).

Documentation

  • SECURITY.md: AI audit record 2026-10-30 (Pass (good) / Low, L1–L4 fixed), PEM file requirements, business rule details, host transaction around BillingRecordProcessor::process() and BillingRecord mutability. CONFIGURATION.md: production storage requirement and host transaction. COMMANDS.md: --allow-production.

Release v1.3.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 06:52

v1.3.0

Changelog

doctrine.table_prefix (TablePrefixListener), SECURITY.md threat model, SensitiveParameter on certificate passwords, PHP 8.5 curl_close deprecation See UPGRADING.

Added

  • doctrine.table_prefix (REQ-ORM-002). New option nowo_verifactu.doctrine.table_prefix (default '', letters / digits / underscores only). A Doctrine loadClassMetadata listener (Nowo\VerifactuBundle\Doctrine\TablePrefixListener) prepends it to the verifactu_hash_chain table, its unique constraint and any many-to-many join table of bundle entities (e.g. app_ yields app_verifactu_hash_chain). Host and third-party entities are never touched; the default keeps the historical name.

Security

  • Certificate passwords are #[\SensitiveParameter] in CertificateLoader::load() and CurlSoapTransport::send() (redacted from stack traces).

Fixed

  • CurlSoapTransport no longer calls curl_close() (no effect since PHP 8.0, deprecated in PHP 8.5).

Deprecated

  • nowo_verifactu.hash_chain.table_prefix was never applied; setting it now triggers a deprecation pointing to doctrine.table_prefix. It is still accepted and still has no effect.

Changed

  • Developer tooling: Rector targets PHP 8.2, the declared php floor (REQ-CS-003, REQ-SF-001).

Added

  • Developer tooling: make check-no-raw-html-form fails on raw <form> / <input> / <textarea> / <select> in bundle views and demo templates, and runs in release-check (REQ-TWIG-005).

Changed

  • Demo: the invoice generator renders through a Symfony Form Type (App\Form\InvoiceType; flat field names unchanged, CSRF token id demo_invoice) with form_start / form_row / child loop / form_end; the controller validates with handleRequest + isValid(); the demo now requires symfony/form and symfony/security-csrf (REQ-TWIG-005).
  • Developer tooling: make release-check runs the full REQ-MAKE-002 chain in order (ensure-up first, then check-no-cursor-coauthor, check-open-prs, composer-sync, cs-fix, cs-check, rector-dry, phpstan, igor, test-coverage-100, demo release-check); check-open-prs also fails on conflicted pull requests (REQ-REL-003).
  • Dependencies refreshed (Composer and pnpm/npm lock files of the bundle and its demos, campaign 2026-10-19).
  • .php-cs-fixer.dist.php: new ParallelConfig(2) (REQ-CS-001).

Documentation

  • PHPDoc descriptions on every public method in src/ (REQ-CS-001).
  • docs/SECURITY.md restructured as a security analysis: scope, attack surface, threat model (threat → mitigation), secrets and cryptography, logging policy, dependency policy, exposure (REQ-SEC-001).
  • docs/CONFIGURATION.md documents doctrine.table_prefix with the resulting table names.
  • Documentation: wave-1 REQ compliance — README documentation list order (canonical labels, PSR under Additional documentation), Symfony badge lists 7.4 explicitly and GitHub stars image uses the package name, canonical issue templates, demo README links DEMO-FRANKENPHP, per-variable comments in the demo .env.example, CONTRIBUTING Cursor Agent settings, TOCs for long docs, Spec Kit inventory refresh (55/55, LockableHashChainRepositoryInterface).

Release v1.2.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 10:14

Release v1.2.0: TaxBreakdown tax type emitted as DetalleDesglose/Impuesto; Symfony >=6.4.

Changelog

Fixed

  • TaxBreakdown::$taxType is now emitted as <Impuesto> (first child of DetalleDesglose, as required by the AEAT DetalleType sequence) when it is not empty. Previously it was ignored, so IGIC (03), IPSI (02) and other (05) lines were sent without their tax type and AEAT assumed IVA. Generated XML with Impuesto is XSD-validated against the bundled SuministroInformacion.xsd in tests.

Changed

  • Requirements: all symfony/* constraints are now ^6.4 || ^7.0 || ^8.0 (was ^6.0 || ...) so the lowest allowed version is the one tested in CI. README badge and INSTALLATION aligned (PHP requirement in INSTALLATION corrected to 8.2+).

Release v1.1.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 08:51

Release v1.1.0: AEAT compliance fixes (see CHANGELOG/UPGRADING).

Changelog

Security

  • nowo:verifactu:submit-sandbox without --submit (or with --dry-run) no longer stores the generated record as the new hash chain head, so smoke tests cannot rewrite the real persistent chain. BillingRecordProcessor::process() gains a persistChainState argument (default true).
  • AEAT SOAP responses are only reported as successful when the HTTP status is 2xx and EstadoRegistro is Correcto. A missing EstadoRegistro (e.g. HTML error pages) or a non-2xx status is now a failure; namespace-prefixed AEAT responses (<tikR:EstadoRegistro>) are parsed and DescripcionErrorRegistro is reported.
  • The chain head read-then-write is now serialized per issuer NIF: new LockableHashChainRepositoryInterface; the Doctrine repository runs it in a transaction with SELECT … FOR UPDATE, preventing forked chains under concurrent requests.

Fixed

  • Apps without doctrine/doctrine-bundle no longer fail at container build: the extension checks hash_chain.storage first and probes ServiceEntityRepository instead of autoloading the Doctrine repository. storage: doctrine without Doctrine now throws a clear LogicException.
  • Anulación fingerprints now use IDEmisorFacturaAnulada / NumSerieFacturaAnulada / FechaExpedicionFacturaAnulada, as required by the AEAT hash specification (verified with the official test vector).
  • Hash amounts are normalized as strings (insignificant zeros only) instead of through float, so large amounts such as 999999999999.99 are no longer altered.
  • Desglose emits one DetalleDesglose per TaxBreakdown entry instead of a single line with the first rate and the invoice totals.
  • QR URLs use ValidarQRNoVerifactu in no_verifactu mode (QrUrlBuilder takes the configured mode; buildUrl() accepts an optional per-call $mode).

Changed

  • Documented that XadesBillingRecordSigner produces plain enveloped XML-DSig, not full XAdES, and that hash_chain.table_prefix and aeat_spec_version are reserved options with no effect.

Release v1.0.12

Choose a tag to compare

@github-actions github-actions released this 13 Oct 07:17

Release v1.0.12: dependency refresh and repository maintenance.

Changelog

Changed

  • Development: composer.json pins config.platform.php to 8.2.0 so the committed lock stays installable on the minimum PHP; CI overrides the platform per matrix cell.

Dependencies

  • doctrine/orm 3.7.4 → 3.7.5 (development lock; #12).

Release v1.0.11

Choose a tag to compare

@github-actions github-actions released this 09 Oct 08:24

v1.0.11

Changelog

Dependencies

  • Dev tooling (Dependabot + lockfile refresh): igor-php/igor-php ^0.10.0, doctrine/orm 3.7.3, phpstan/phpstan 2.3.1, phpstan/phpstan-phpunit 2.1.1, phpstan/phpstan-symfony 2.1.0, nowo-tech/phpstan-frankenphp 1.2.3, rector/rector 2.7.0. The bundle lockfile stays resolved for the PHP 8.2 floor (Symfony 7.4).
  • Demo (demo/symfony8): Symfony 8.1.8, doctrine/orm 3.7.4, doctrine/dbal 4.5.0, twig/twig 3.30.0; regenerated config/reference.php.

Release v1.0.10

Choose a tag to compare

@github-actions github-actions released this 27 Sep 16:58

v1.0.10

Changelog

Added

  • REQ-CS-008: igor-php/igor-php (require-dev only), root igor.json, Composer/Makefile igor target, and release-check wiring for FrankenPHP worker-state audit.

Changed

  • Worker safety (Igor): justified // @igor-ignore annotations and/or ResetInterface / request-scoped fixes so make igor passes on package src/.

Release v1.0.9

Choose a tag to compare

@github-actions github-actions released this 25 Sep 06:24

Release v1.0.9

FrankenPHP worker mode hardening (kernel not reset between requests).
See docs/CHANGELOG.md ## [1.0.9] and docs/FRANKENPHP-WORKER-AUDIT.md.

Changelog

Fixed

  • hash_chain.repository now also aliases HashChainRepositoryInterface (previously only nowo_verifactu.hash_chain_repository, so BillingRecordProcessor kept using the in-memory storage).
  • FrankenPHP worker mode (no kernel reset between requests): see docs/FRANKENPHP-WORKER-AUDIT.md.
    • InMemoryHashChainRepository is request-scoped: new clear() method, tagged kernel.reset, and called at the start of every main request by the new EventSubscriber\WorkerStateResetSubscriber (priority 4096). Workers no longer build per-worker hash chains. When kernel.debug is false it logs a warning on every store (the chain is not persistent).
    • DoctrineHashChainRepository reads the last state fresh from the database (Query::HINT_REFRESH), detaches the entity after each read/write, deletes with a DQL DELETE, and resets a closed EntityManager when a flush/query fails before rethrowing.
    • XsdValidator restores the previous libxml_use_internal_errors() value in finally.

Release v1.0.8

Choose a tag to compare

@github-actions github-actions released this 03 Sep 22:24

v1.0.8

Changelog

Fixed

  • FrankenPHP / PHPStan: replace mutable static $translations with immutable class constant in unit helper.
  • CI: drop obsolete PHP 8.1 from the test matrix (composer requires PHP ≥8.2).
  • Demo: install pdo_mysql in FrankenPHP image (demo was HTTP 500: could not find driver).

Changed

  • Deps (dev): refresh Composer lockfiles (xmlseclibs and demo lock alignment).

Release v1.0.7

Choose a tag to compare

@github-actions github-actions released this 24 Aug 16:22

Release 1.0.7

See docs/CHANGELOG.md for details.

Changelog

Changed

  • Raise minimum PHP to 8.2 and sync README badge (REQ-SF-001).
  • Demos: MySQL env policy in FrankenPHP stack (REQ-DEMO-011).
  • Makefile: detect Docker Compose V2 (REQ-MAKE-010).
  • QA: add phpstan-frankenphp extension (REQ-CS-005).
  • README: FrankenPHP-friendly worker-mode banner (REQ-DOCS-017).
  • Docs: PHP-FIG PSR evaluation (REQ-CS-007).
  • Style: PHP CS Fixer alignment.

Notes

  • No API or configuration changes for integrators unless noted above.