Repository navigation
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:
CertificateLoaderreturned the public key asprivate_keyand the whole PEM file (private key block included) ascertificate, 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:--submitoutside the sandbox environment is refused unless--allow-production, explicit--nif/--numserieand an interactive confirmation are given (it used to only print a warning). - L3 — production chain storage: the container refuses
aeat.environment: productionwith an AEAT client (certificate_pathorsubmission_client) whilehash_chain.storageismemoryand nohash_chain.repositoryis set. - L4 — dist archives:
.gitattributesexport-ignoreandcomposer.jsonarchive.excludekeeptests/(throwawaytest.p12),demo/,scripts/,specs/,.specify/,.cursor/and tooling out of dist archives. #[\SensitiveParameter]on the certificate password ofSoapAeatSubmissionClientandXadesBillingRecordSigner;scripts/aeat-sandbox-submit.phpreadsAEAT_CERT_PASSWORDand warns when--cert-passwordis used.CurlSoapTransportsets an explicit TLS policy: HTTPS only, no redirects,CURLOPT_SSL_VERIFYPEER/CURLOPT_SSL_VERIFYHOST = 2, 10 MiB response cap (MAX_RESPONSE_BYTES).twig/twigfloor raised to^3.19(patched line).- Demo: published port bound to
127.0.0.1; the audit log masks the issuer NIF.
Fixed
SpanishTaxIdValidatornow 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.AeatBusinessRulesValidatorrejects 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_prefixvalidation regex is anchored withD(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()andBillingRecordmutability. CONFIGURATION.md: production storage requirement and host transaction. COMMANDS.md:--allow-production.