5 Commits
Author SHA1 Message Date
7d13d8bd39 Fix the toApiArray auditor crashing on try/catch blocks (#4599)
* Fix the toApiArray auditor crashing on try/catch blocks

The TryCatch branch read $node->try, which php-parser does not have (the
try body is $node->stmts), so the first toApiArray() containing a
try/catch passed null to collectAssignments() and the audit test died
with a TypeError. The catch and finally nodes were passed in whole, and
collectAssignments() has no branch for them, so their bodies were never
examined either.

Walk the try, catch and finally bodies instead. A new test audits a
temporary module whose toApiArray() sets a field conditionally in each
of the three; it fails with the TypeError before this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Name the audit test's temporary directory in plain words

cspell rejects "toapiarray".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Adam Daley <admdly@users.noreply.github.com>
2026-10-28 11:04:46 +00:00
Adam Daley b364588f30 Harden release cleanup and updater integrity (#4166) 2026-08-15 15:27:15 +01:00
Adam Daley 6a10ee3e9f Migrate admin identity from Model_Admin to Doctrine entity (#4136)
* Migrate admin identity from Model_Admin to Doctrine entity

* Fix admin ID access in logger when admin identity is a Doctrine entity

* Harden staff service flows and cron admin creation against race conditions
2026-08-10 23:15:24 +01:00
FOSSBilling BotandFOSSBilling Bot 3c6ab0bd4f Apply PHP Code Styling & Quality Changes (#3739)
Co-authored-by: FOSSBilling Bot <fossbilling-bot>
2026-06-09 19:17:44 +01:00
Adam Daley 5ecfeb335e Harden templates and API responses against missing data (#3730)
* Guard Activity index template against missing client/staff nested data

The activity log API at Activity/Api/Admin.php:38-49 only injects a
nested 'client' or 'staff' key when the corresponding *_id is set
(via isset() which returns false for null). System events and
client-only events therefore have no 'client' key, while staff-only
events have no 'client' key.

Under strict_variables, '{% if event.client %}' (line 191) throws
"Key 'client' ... does not exist" for any event without a client.

Switch the conditional to event.client_id / event.staff_id (always
present as SQL columns) and add '|default("")' to the nested
event.client.email / event.client.name / event.staff.email / event.staff.name
accesses so deleted-but-referenced records render gracefully.

* Guard Security iplookup template against empty country/asn data

When the GeoIP or ASN lookup fails (IncompleteRecord from MaxMind), the
Service catches and substitutes [] for the missing data. Under
strict_variables, the empty mapping at record.asn / record.country
causes line 48 to throw "sequence/mapping is empty".

Twig's |default filter on a GetAttrExpression compiles to a safe
is-defined check, so it correctly falls through to the placeholder
when the underlying array is empty or the key is missing.

Verified:
- /admin/security/iplookup?ip=192.168.1.1 (private IP, empty asn)
  renders the row with 'Unknown' ASN cells.
- /admin/security/iplookup?ip=8.8.8.8 still renders
  '15169' / 'GOOGLE' / 'United States'.

* Initialize notes/rel arrays in Support toApiArray so templates render without errors

Two related issues surfaced under strict_variables on the admin support
ticket page:

1. toApiArray only set $data['notes'] inside the foreach loop over
   ticket notes. Tickets without any notes therefore had no 'notes' key
   and 'ticket.notes|length' in mod_support_ticket.html.twig:42 threw
   'Key "notes" does not exist'.

2. _getRelDetails returned [] (empty array) when a ticket has no
   related entity. mod_support_ticket.html.twig sets {% set task =
   ticket.rel %} and then accesses task.task, task.type, task.status
   etc. on the next lines. Any access into an empty mapping throws
   under strict_variables.

Fixes:
- Service::toApiArray: always initialize $data['notes'] = [] for admin
  identity so the key is present even when empty.
- Service::_getRelDetails: always return the base structure with all
  fields (id, type, task, new_value, status) set to null when there
  is no related entity. Skip the DB lookups for the non-rel case.

Template hardening (defense-in-depth, in case other code paths return
a different shape):
- mod_support_ticket.html.twig:42: 'ticket.notes|length' -> 'is defined and
  ticket.notes|length'.
- mod_support_ticket.html.twig:222: 'ticket.notes' -> 'ticket.notes|default([])'.
- mod_support_tickets.html.twig:238: same is defined guard.

Verified on dev:
- /admin/support/ticket/1 renders without errors.
- API now returns rel as {id: null, type: null, ...} and notes: [] for
  tickets without related data.
- 1777 local tests pass, PHPStan clean.

* Harden tests and add Twig/ToApi linters

* Remove redundant setAccessible calls in tests

* Update cspell.json word list

* Add UrlAwarePermissiveContainer & trim errors

* Add geoip stub and fix ParserFactory usage

* Enforce and apply parentheses for new expressions
2026-06-06 18:32:19 +01:00