Skip to content

fix: upgrade brace-expansion to patched version (CVE-2026-102276) - #344

Open
anupamme wants to merge 243 commits into
OpenDataRepository:developfrom
anupamme:fix-repo-data-publisher-cve-2026-102276-brace-expansion
Open

anupamme wants to merge 243 commits into
OpenDataRepository:developfrom
anupamme:fix-repo-data-publisher-cve-2026-102276-brace-expansion

Conversation

@anupamme

Copy link
Copy Markdown

This upgrades brace-expansion (currently 1.1.12) to 5.0.10, 3.0.7, 2.1.5, 1.1.19, which carries the fix for CVE-2026-102276. The package is present in this repository's dependency tree; I have not verified that your code reaches the affected function.

Reference: CVE-2026-102276

What changed

  • background_services/package.json
  • background_services/package-lock.json

Verification

No automated check could be run against this repository, so this change is unverified beyond review. Please treat it as a suggestion.


Automated security fix by OrbisAI Security

natestone and others added 30 commits April 8, 2026 04:32
- Apply rector PHP 8.3 rules: array() to [], promoted constructor properties,
  null-to-string function argument fixes (302 files)
- Update all YAML service configs (68 named arguments) to match rector's
  renamed constructor parameters
- Update composer.lock for PHP 8.3 compatible package versions
- Disable composer security advisory blocking during upgrade transition
- Delete 68 orphaned .php~ backup files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Preparatory cleanup before raising the symfony/symfony constraint to 4.4.
App still boots and runs on Symfony 3.4.49 / PHP 8.3 after these changes.

- Twig 3 readiness: {% spaceless %} -> {% apply spaceless %}...{% endapply %}
  across 426 templates (deprecated/removed in Twig 3). All migrated
  templates lint clean.
- Symfony 4 service visibility: mark services fetched via the container as
  public: true (services are private by default in Symfony 4+).
- PHP 8.2 dynamic property fix in WPAutoLoginSubscriber: store integrated_user
  as a request attribute instead of a dynamic property on the controller.
- Enable deprecation logging handler in config_dev.yml (migration checklist).
- Track rector.php (PHP 8.3 config); gitignore the rector.phar binary.
- Remove web/phpinfo.php debug file from the web root.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Framework, dependency, and code migration to Symfony 4.4 LTS. The kernel
now boots on 4.4.51 / PHP 8.3: cache:clear succeeds, 476 routes register,
and the service container builds.

Dependencies (composer.json / composer.lock):
- symfony/symfony 3.4 -> 4.4; doctrine-bundle 1 -> 2, ORM 2.6 -> 2.20,
  DBAL 2 -> 3, collections 1 -> 2, common 2 -> 3 (user opted to take the
  Doctrine majors now rather than defer to Phase 6); gedmo 2 -> 3;
  hwi/oauth 0.6 -> 1.4; commonmark 0.18 -> 1.6; sensio/framework-extra 5 -> 6.
- Removed jms/security-extra-bundle (hard-blocked SF4: required
  security-bundle ~2.3|~3.0) and its transitive jms/aop + jms/di-extra;
  removed sensio/distribution-bundle and sensio/generator-bundle.
- Dropped symfony/browser-kit and symfony/css-selector from require-dev
  (provided by symfony/symfony).

Code migration (rector SYMFONY_40..44 across 175 files):
- Symfony 3->4 class moves (e.g. EventDispatcher\Event ->
  Contracts\EventDispatcher\Event), Command::execute() returns int,
  render() template short-notation -> @Bundle/Folder/x syntax.
- Replaced JMS SecureRandom with native random_bytes(16) in CryptoService.
- Doctrine\Common\Persistence\ObjectManager -> Doctrine\Persistence
  (6 form transformers/types).

Bootstrap / config:
- AppKernel: dropped JMS + Sensio Distribution/Generator bundle registrations.
- app/console, web/app.php, web/app_dev.php: removed obsolete
  bootstrap.php.cache / loadClassCache(); deleted bootstrap.php.cache.
- security.yml: removed jms_security_extra block.
- config.yml: Doctrine cache drivers -> PSR-6 memcached pools; gedmo v3
  mapping paths (src/ layout) + is_bundle: false.
- Fixed committed duplicate "public" YAML key in SearchBundle services.yml.
- .gitignore: ignore SF4 var/log.

Known deferred to Phase 4 (abandoned bundles, as planned):
- FOSOAuthServerBundle managers still type-hint the removed
  Doctrine\Common\Persistence\ObjectManager and fail to instantiate;
  slated for replacement with league/oauth2-server.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause of the "can't log in" failure: doctrine/persistence 3 removed the
"Bundle:Entity" short-alias syntax for getRepository()/DQL. Every query page
(including the post-login redirect target) threw a fatal NotSupported error,
so authentication succeeded but the redirect 500'd.

- Converted 1698 "Bundle:Entity" short aliases to fully-qualified entity
  class names across 84+ files (getRepository, DQL FROM/JOIN, ->from(),
  targetEntity, etc.). Conversion was entity-aware: only names matching a real
  entity class in the bundle's Entity/ dir were rewritten, so controller refs
  (e.g. ODRAdminBundle:APIController in docblocks) and the non-existent
  ODRAdminBundle:ThemeElementField were left untouched. Two case-typos were
  corrected to the real class (Datafields->DataFields, ThemeDatatype->ThemeDataType).
- ODRExceptionController: token_storage type hint TokenStorage ->
  TokenStorageInterface (SF4 injects UsageTrackingTokenStorage). This had been
  masking every other error behind a second 500.
- Render plugin DI: rewired $templating "@templating" -> "@twig" (32 args in
  7 config files); rector had already retyped the constructors to
  Twig\Environment, but the DI still passed the old templating engine.

Verified: /login 200, bad-credential POST -> 302 (proper auth-failure
handling, no 500), kernel getRepository+DQL works (DataType count 474),
and a query-heavy search page (/rruff_sample) renders 200 with no errors.

Known pre-existing issue left in place: DisplaytemplateController has 3 DQL
references to ODRAdminBundle:ThemeElementField, an entity class that does not
exist; that code path was already broken before the upgrade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rector retyped controllers from Controller to AbstractController, but these
legacy controllers rely on the full container (services were marked public so
$this->container->get() keeps working). AbstractController::getParameter()
instead requires "parameter_bag" in a service-subscriber locator, which is
private on the full container — so every $this->getParameter() call threw
"missing a parameter bag" 500s (hit when searching a datatype).

Rewrote the 104 bare $this->getParameter(...) calls across 26 controllers to
$this->container->getParameter(...), matching the 397 calls that already use
that form. $this->get(...) is unaffected — it delegates to the full container.

README -> 2.05.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The earlier templating fix only matched double-quoted "@templating"; ~20 plugin
service args used single quotes ('@templating') and were missed, so plugins like
LinkedDescendantMergerPlugin still received TwigEngine where their constructors
now require Twig\Environment. Replaced all remaining @templating refs (both quote
styles) with @twig across the GraphBundle plugin configs.

Verified by instantiating every odr.* / odr_plugins.* service from the booted
container: 108 services, 0 failures.

README -> 2.06.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 47 console worker commands (odr_record:*, odr_datatype:*, odr_xml_import:*,
etc.) failed with "There are no commands defined in the <ns> namespace" and,
once reachable, with container errors. Three causes:

1. Symfony 4 removed auto-registration of a bundle's Command/ directory.
   Registered all Command classes as services tagged console.command in the
   AdminBundle, GraphBundle, and OAuthServerBundle service configs.

2. Rector retyped the commands from ContainerAwareCommand to the base Command
   class, but their bodies still call $this->getContainer(). Restored the
   ContainerAwareCommand base class on all 47 (the import was still present);
   getContainer() resolves the full container from the kernel at runtime.

3. The commands fetch several services straight from the container
   ($this->getContainer()->get('pheanstalk') etc.), which Symfony 4 makes
   private by default. Added PublicServicePass to mark the fetched third-party
   services public (pheanstalk, memcached, snc_redis.default; logger and
   fos_user.user_manager were already public aliases).

Verified: every namespace lists (odr_record now shows all 10 commands), and a
worker (odr_record:clear_storage_entity_cleanup) runs past container setup into
its beanstalk reserve loop with no errors. Web login/search still 200.

README -> 2.07.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…prep)

The "Bundle:Controller:action" controller notation is deprecated since
Symfony 4.1 and removed in 5.0. Converted all 451 _controller entries across
the 9 routing/config files to fully-qualified "FQCN::methodAction" notation.

- Mechanical, map-based transform (bundle short-name -> namespace); all 51
  distinct referenced controller classes validated to exist on disk.
- Router loads cleanly (465 routes); /admin/type/list/databases renders and
  its per-request deprecation count drops from ~400 to ~8 (the remaining ones
  are the templating-engine and container auto-injection buckets, handled next).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… 1/2)

The "Bundle:Folder:name.html.twig" template notation relies on the deprecated
FrameworkBundle templating component (TemplateNameParser/TemplateLocator) and
is rejected by Twig\Environment. Converted all 818 references (PHP render()
args + Twig include/extends/embed/from/import/use) across 276 files to the
"@Namespace/Folder/name.html.twig" form (strip "Bundle", prepend "@").

This is safe on the current engine -- TwigEngine still accepts @-notation -- so
it is committed independently. The /admin/type/list/databases page renders
unchanged. Swapping the templating service/EngineInterface to Twig\Environment
(which actually clears the templating deprecations) follows in part 2.

Pre-existing, unrelated twig lint errors (a stray "." in Metadata Person plugin,
removed form_enctype() in FOSUser reset_content) were left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…5.0 prep, part 2/2)

The "templating" engine (TwigEngine/EngineInterface/TemplateNameParser/...) is
deprecated since Symfony 4.3 and removed in 5.0. With all template names already
on @-namespace notation (part 1), and only ->render() ever called, the swap is
behaviour-preserving:

- get('templating') -> get('twig') across 112 call sites in 31 controllers
  ($this->get('twig') resolves via AbstractController's service subscriber).
- Removed 79 now-unused "use ...Templating\EngineInterface;" imports and
  retargeted the remaining @var/@PARAM docblocks to \Twig\Environment.

The framework "templating" config is kept (with a comment) only because
FOSOAuthServerBundle's authorize controller still type-hints EngineInterface;
it stays lazy and no longer instantiates on normal ODR requests. The last
per-request consumer (twig.exception_listener via the custom exception
controller) is a separate error_controller migration, handled next.

Verified: container compiles, all 85 changed PHP files lint clean, login page
renders (HTTP 200).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mfony 5.0 prep)

TwigBundle's twig.exception_controller / exception listener is deprecated in 4.4
and removed in 5.0, and it was the last per-request consumer of the deprecated
"templating" engine in ODR code (it eagerly instantiated templating to render
error pages).

- ODRExceptionController no longer extends the deprecated TwigBundle
  ExceptionController; the three helpers it relied on (findTemplate,
  templateExists, getAndCleanOutputBuffering) are inlined, and it now type-hints
  the new Symfony\Component\ErrorHandler\Exception\FlattenException (the Debug\
  namespace is deprecated). Behaviour (AJAX-aware format detection, 403->401,
  custom error/exception templates) is unchanged.
- config: wire framework.error_controller to ODR.exception_controller::showAction;
  set twig.exception_controller: ~ (null) to stop the deprecated listener being
  registered (omitting it falls back to the default, so null is required).

Verified: a 404 renders ODR's custom error response (HTTP 404, no 500, no
FlattenException namespace errors) and twig.exception_listener no longer appears
in deprecation traces. The remaining "templating" instantiations all come from
FOSUserBundle / FOSOAuthServerBundle (Phase 4 removals); no ODR code touches it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
app/config/config.yml is gitignored (local env config); the tracked template is
config.yml.dist. The A2/A3 changes had only been applied to the live config, so
fold them into the .dist template so they are version-controlled and deployed:

- framework.error_controller: ODR.exception_controller::showAction
- twig.exception_controller: ~  (disables the deprecated listener)
- keep framework.templating with the "FOSOAuthServer only, remove in Phase 4" note

config.yml.dist passes lint:yaml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First controller migrated off the deprecated container auto-injection (removed
in Symfony 5.0), establishing the pattern for the rest:

- Inject all dependencies via the constructor (TrackedPathService, RouterInterface,
  SearchKeyService, ThemeInfoService, %site_baseurl%) as readonly promoted props.
- Replace every $this->get()/$this->container->get()/getParameter() with the
  injected property.
- Register the controller as a service (controller.service_arguments, public,
  FQCN id) in UserBundle services.yml.
- Drop "extends AbstractController": with everything constructor-injected the base
  is unused, and keeping it would re-trigger the auto-injection deprecation (the
  service factory doesn't call setContainer). Controllers that DO use base-class
  helpers will instead keep AbstractController + autoconfigure.

Verified: /save_url and /save_fragment return 200 and the
"Auto-injection of the container for UtilityController" deprecation is gone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cted services

Replaces the container auto-injection (deprecated 4.2, removed in 5.0) for all 42
ODRCustomController subclasses + UtilityController (already done). Hybrid scope:
odr.* domain services are constructor-injected; framework/vendor services resolve
through an explicit service locator.

- ODRCustomController base: injects its 12 odr.* services as protected readonly
  promoted props (subclasses inherit them, forwarding to parent::__construct and
  adding only their own extra odr.* services). Keeps AbstractController so the
  framework helpers (getDoctrine/generateUrl/render/getParameter) keep working.
- odr.controller_locator (AdminBundle services.yml): a ServiceLocator with exactly
  the AbstractController-subscribed services + the few extra framework/vendor ones
  controllers use (event_dispatcher, logger, pheanstalk, fos_user.*, snc_redis,
  hwi_oauth.security.oauth_utils, two dotted odr.* ids). Injected into each
  controller via an explicit setContainer call -- this is what suppresses the
  auto-injection deprecation in this pre-Flex app (autoconfigure does NOT wire it).
- Each controller registered as a service (controller.service_arguments) pulling the
  shared 12 base args via the *odr_base_args YAML anchor + its own extras.
- Edge cases: OAuthClient\LinkController uses dynamic service ids
  (hwi_oauth.resource_owner.$name) so it gets the full container instead of the
  locator; XSDController referenced a non-existent odr.datatype_info_service (dead
  code behind ODRNotImplementedException) -- left as-is.

The 7 SearchBundle/ApiBundle/OAuthServer controllers that extend AbstractController
directly (not ODRCustomController) are a separate deferred batch; they still work on
the old path. Verified: database list, datatype properties, and record view (/view/2)
all render with 0 auto-injection deprecations and 0 DI errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… services

Completes Bucket C. These extend AbstractController directly (not ODRCustomController),
so they have no base-12 to forward — each injects only its own odr.* services and
resolves framework/vendor services through the shared locator:

- SearchBundle: Default, Facade, Remote, SearchCache
- ApiBundle: Default (no services — just registered + setContainer to drop the
  auto-injection)
- OAuthServerBundle: Security, Client

- Extended odr.controller_locator with the services these need but weren't present:
  security.authentication_utils, fos_oauth_server.client_manager.default, and
  odr_custom_controller (SearchBundle\DefaultController fetches the base controller as
  a service to reuse its utility methods — gave that service its 12 constructor args).

Verified: container builds; the search page (SearchBundle\DefaultController, which also
pulls odr_custom_controller) instantiates and runs with 0 auto-injection / 0 DI errors.
Its remaining failure is a pre-existing league/commonmark issue (null to convertToHtml),
unrelated to DI (that's the separate markdown-upgrade bucket).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_PLAN

SYMFONY_5_DEPRECATION_CLEANUP.md documents the repeatable process for the work done
in commits 45d98d0..f074c3a (route notation, template notation, templating engine,
exception controller, controller constructor-injection). It is written to be re-run
if the codebase diverges: every step leads with a detection command that re-scans the
current code, and the transforms re-derive their inputs from grep rather than hardcoded
file lists. Includes the dead-ends to avoid, the working locator/base/anchor pattern,
the edge cases, and a "re-running on a changed codebase" checklist.

UPGRADE_PLAN.md links it from the header and from Phase 4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ODRMarkdownParserService::transformMarkdown() passed its argument straight to
CommonMarkConverter::convertToHtml(), whose parameter is type-hinted `string`. On
PHP 8 a null argument throws a TypeError instead of being silently coerced to ''
as it was on PHP 7, so any empty markdown field -- or an unguarded `|markdown`
twig filter (e.g. tag.tagMeta.tagName|markdown, content|markdown) -- crashed the
whole page with:
  League\CommonMark\Converter::convertToHtml(): Argument OpenDataRepository#1 ($commonMark) must be
  of type string, null given

Coerce to (string) at this single choke point so null/empty renders as empty html
for all callers.

Verified: the rruff search page (which previously 500'd here) now renders its
search results (HTTP 200) with no markdown error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DBAL 3.10 removed Connection/Result::fetchAll() and Connection::executeUpdate(),
throwing e.g. "fetchAll of class Doctrine\DBAL\Connection ... Did you mean
fetchAllAssociative?" — which broke search/query/display and so prevented many
datatypes from loading.

Mechanical rename across 20 files (all call sites are on $em->getConnection()
DBAL connections / Result objects, same signatures):
- ->fetchAll(   -> ->fetchAllAssociative(   (117 sites incl. $conn/$result/$stmt-as-Result)
- ->executeUpdate( -> ->executeStatement(   (44 sites)

No other removed DBAL methods are in use (fetch/fetchColumn/fetchAssoc/query/exec = 0).

Verified: the rruff search results (SearchQueryService->fetchAll, the original error)
now render (HTTP 200, "r":0) with 0 DBAL errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Datatype search/display pages failed with "Uncaught SyntaxError: ... appendChild
... Invalid or unexpected token" — jQuery .html() executes inserted <script> tags,
and the inline JS in three .js.twig partials had been mangled into \uXXXX escapes
(e.g. $( -> $(), which aborted the AJAX success handler before it cleared
the loading overlay.

Root cause: the earlier Twig 3 prep (6b132a3) converted `{% spaceless %}` to
`{% apply spaceless %}`. `apply` prints its block via {{ }}, which is subject to
autoescaping; the spaceless filter is declared is_safe=['html'] only. In a .js.twig
file (Symfony autoescape:'name' -> 'js' strategy) the block is therefore js-escaped,
turning the literal JavaScript into invalid tokens. (.xml/.json.twig use the html
strategy where spaceless IS safe, so they were unaffected.)

Fix: wrap the `apply spaceless` block in `{% autoescape false %}` in the three
affected .js.twig files (search_results_introjs, display_setup_datatables,
functions) — they emit literal JS and only interpolate controlled values.

Verified: AHED search results and record view (/view/2) now render with all inline
scripts parsing cleanly and no \u-escaping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Detailed plan for the Phase 4 bundle removals, grounded in a footprint scan:
- OAuth server unused (0 rows in fos_client/access_token/auth_code) -> FOSOAuthServerBundle is a removal
- GitHub/Google login unconfigured -> HWIOAuthBundle/OAuthClientBundle is a removal
- FOSUserBundle (271 users, user_manager x50) is the one real auth migration
Sequence 4.1 (FOSOAuth) -> 4.2 (HWIOAuth) -> 4.3 (FOSUser) -> 4.4 (bump to 5.4).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "ODR as an OAuth provider" feature is unused -- fos_client / fos_access_token /
fos_auth_code / fos_authorized_clients all have 0 rows -- and FOSOAuthServerBundle is
incompatible with Symfony 5 (its managers type-hint the removed Doctrine ObjectManager).
So this is a clean removal, not a league/oauth2-server replacement.

- Deleted src/ODR/OpenRepository/OAuthServerBundle/ and unregistered both it and
  FOS\OAuthServerBundle from AppKernel; dropped friendsofsymfony/oauth-server-bundle
  from composer.json.
- Removed the fos_oauth_server config, the oauth_token/oauth_authorize firewalls, and
  the /oauth/v2/* + ODR oauth-server routes (config.yml(.dist), security.yml(.dist),
  routing.yml, routing_prefixed.yml).
- User entity: removed the $clients ManyToMany (-> deleted Client entity) and its
  add/remove/get/isAuthorized methods + constructor init. Fixed a latent copy-paste bug
  in removeUserGroup() that operated on $this->clients instead of $this->userGroups.
- ODRUserController: dropped the now-dead, $this->has()-guarded OAuth-client blocks
  (profile page renders no OAuth-clients section); removed the locator + service-arg
  entries for the OAuth-server controllers/managers.

Verified: container builds, Doctrine mapping is correct (after clearing stale metadata
cache), router has 0 /oauth/v2 routes, login page renders, /oauth/v2/token -> 404.

Note: the four empty fos_* OAuth tables can be dropped in a later DB cleanup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…login)

GitHub/Google login was never configured (hwi_oauth resource_owners all commented out,
fos_user_link_oauth has 0 rows), so this is a clean removal.

- Deleted src/ODR/OpenRepository/OAuthClientBundle/ and unregistered both it and
  HWIOAuthBundle from AppKernel; dropped hwi/oauth-bundle from composer.json.
- Removed the hwi_oauth config, the main firewall's oauth: block + oauth_user_provider
  service, and the /connect + ODR oauth-client routes (config.yml(.dist),
  security.yml(.dist), routing.yml, routing_prefixed.yml).
- User entity: removed the $userLink OneToMany (-> deleted UserLink entity) + its
  add/remove/get methods and constructor init.
- ODRUserController: dropped the dead, $this->has()-guarded HWIOAuth blocks and the
  OAuthUtils/UserLink imports; removed the hwi_oauth.security.oauth_utils locator entry
  and the LinkController service def.
- Removed two bogus `use HWI\...\Tests\Fixtures\FOSUser` imports (accidental IDE
  auto-imports) from APIController + CloneDatatypePreloaderCommand.
- Removed the now-dead OAuth includes that referenced the deleted bundle templates:
  the login-page provider list in UserBundle layout.html.twig (would have broken the
  login page) and the OAuth-management section in user_profile.html.twig.

Verified: container builds, Doctrine mapping correct, 0 /connect|/login/check routes,
login page and profile page both render (HTTP 200, no template-not-found errors).

Note: empty fos_user_link_oauth table can be dropped in a later DB cleanup. php-http/*
packages were left in composer.json (not Symfony-5 blockers; reviewable later).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FOSUserBundle is incompatible with Symfony 5. Replaced it with native Security against
the existing fos_user table (column names + sha512 hashing unchanged, so the existing
271 password hashes keep validating).

New (UserBundle):
- User: standalone entity implementing Symfony's UserInterface (was: extends FOS BaseUser).
  Same columns/fields; getRoles()/hasRole()/addRole()/setEnabled()/isSuperAdmin()/
  confirmation-token + password-requested-at helpers reimplemented. __serialize for the session.
- UserRepository: UserLoaderInterface with a case-insensitive username/email lookup (replicates
  FOSUser's canonical login so no one is locked out by casing).
- ODRUserManager: drop-in for fos_user.user_manager (aliased to that id so ~50 call sites are
  untouched) -- findUserBy/findUserByEmail/findUserByUsernameOrEmail/findUsers/createUser/updateUser
  (canonicalizes + hashes plainPassword via the sha512 encoder).
- ODRTokenGenerator: drop-in for fos_user.util.token_generator (aliased).
- SecurityController: renders the login form (login_check/logout handled by the firewall).
- ResettingController: standalone password-reset flow (request -> emailed token link -> reset)
  using the existing confirmation_token/password_requested_at columns + existing templates;
  ResetPasswordForm for the new-password step.

Config/wiring:
- security.yml(.dist): entity provider (UserRepository::loadUserByUsername); encoders ->
  ODR\...\User: sha512; form_login login_path/check_path + logout -> the fos_user_security_* routes.
- UserBundle routing: defines fos_user_security_* + fos_user_resetting_* (same names the
  templates/firewall use) pointing at the new controllers.
- Removed FOSUserBundle from AppKernel + composer.json; removed the fos_user config block and the
  @FOSUserBundle routing imports (config.yml(.dist), routing.yml, routing_prefixed.yml).
- Retargeted all `FOS\UserBundle\(Doctrine\UserManager|Model\UserManagerInterface|Util\TokenGenerator)`
  imports to the ODR replacements (aliased) so constructor type-hints resolve.
- Deleted the FOSUser change-password form + the two FOSUserEvents listeners; cleaned the login/reset
  templates (FOSUser includes/routes/trans -> ODR equivalents; removed form_enctype()).

Verified structurally: container builds, Doctrine mapping correct, login page renders (CSRF +
posts to /login_check), reset request renders, bad reset token redirects, and a search page
exercises the aliased user_manager (HTTP 200). REAL-PASSWORD LOGIN + reset email still need a
human test -- rollback point is commit 8d63c28.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…NOT boot yet)

composer update resolved cleanly to Symfony 5.4.53 + Twig 3.27 (no dependency conflicts);
also dropped twig/extensions (unused, Twig-3-incompatible) and the now-unneeded FOS/HWI
vendor packages. Framework-level boot fixes done so far:
- app/autoload.php: removed AnnotationRegistry::registerLoader() (gone in doctrine/annotations 2).
- kernel.root_dir (removed in SF5) -> kernel.project_dir(+/app) across config*.yml(.dist),
  parameters.yml(.dist), and ODRSystemController.
- Removed framework.templating (templating component removed in SF5).
- Replaced the abandoned drymek/pheanstalk-bundle (SF5-incompatible TreeBuilder API) with a
  direct "pheanstalk" service definition.

STILL BLOCKING boot: dterranova/crypto-bundle (dev-master, abandoned) uses the removed
TreeBuilder() API -- this is the file-encryption subsystem, so its replacement must preserve
the existing encryption format. Decision pending. Rollback point (working SF4.4): c7b19d4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the bump started in 828b3bc. The app now compiles its container,
clears cache, and serves real requests on Symfony 5.4.53 / Twig 3.27.

Abandoned dev-master bundles removed:
- drymek/pheanstalk-bundle -> direct "pheanstalk" service definition (done earlier).
- dterranova/crypto-bundle -> CryptoAdapter/CipherInterface/AES (Cipher) extracted VERBATIM
  into ODR\AdminBundle\Component\Crypto so the AES-256-CBC file-encryption format is byte-for-byte
  identical (existing encrypted files still decrypt); registered as odr.crypto_adapter, bundle
  unregistered + removed from composer.

Symfony 5 / Twig 3 fallout fixed:
- ContainerAwareCommand (removed in SF5): added ODR\AdminBundle\Command\ContainerAwareCommand base
  that restores getContainer() from the full container (injected via setContainer in the two
  console.command service globs); 46 commands repointed at it. All 56 odr_* commands resolve.
- Custom Twig extensions: \Twig_Extension -> \Twig\Extension\AbstractExtension,
  \Twig_SimpleFilter -> \Twig\TwigFilter (PlugExtension, GraphExtension).
- Legacy template notation -> namespaced @-notation across ~160 PHP + Twig files:
  Bundle:Dir:tmpl -> @Bundle/Dir/tmpl (namespace strips the "Bundle" suffix:
  @ODRAdmin, @ODROpenRepositoryGraph), and ::tmpl -> tmpl (root namespace).
- twig.default_path set to app/Resources/views (SF5 default moved to /templates); registered the
  ODR error/exception templates under a new @ODRException namespace (SF5 removed @twig +
  app/Resources/TwigBundle/views overrides). Rewrote ODRExceptionController::findTemplate to use it
  with a safe fallback chain; added a standalone dev exception.html.twig; un-wrapped error.html.twig
  from the apply-spaceless-around-extends pattern Twig 3 rejects.
- framework.assets enabled (asset() was implied by the removed framework.templating).
- TwigBundle dev error routes moved to @FrameworkBundle (routing_dev.yml).
- Symfony\Component\Lock\Factory -> LockFactory (LockService).

Verified: container compiles + cache:clear OK; GET /login -> 200; GET / -> 302; error/exception
pages render; 56 console commands register; `lint:twig` = 488 valid. The 8 remaining lint errors
are pre-existing undefined custom filters (date_now/mock_internal_id/ceil/chemistry) in templates
not touched by this work, not upgrade regressions.

Still needs in-browser testing of logged-in flows + datatype/record rendering (exercises the
converted GraphBundle plugin templates). Rollback point (working SF4.4): c7b19d4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code-grounded plan. Order: 5.1 decompose the symfony/symfony metapackage into individual
components (prerequisite -- the metapackage is unsupported on 5.4 and removed in 6.0; this is
the source of the "Using symfony/symfony is NOT supported" dev-toolbar warning), 5.2 migrate
Security to the authenticator system (enable_authenticator_manager, encoders->password_hashers,
drop anonymous x4, lexik guard->jwt), 5.3 remove sensio/framework-extra-bundle (0 annotations in
use), 5.4 swiftmailer->symfony/mailer (abandoned, not SF6-compatible), 5.5 bump components to
6.4.* + fix fallout. Structural/auth-critical changes done on 5.4 first (reversible), then bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Puppeteer ships no arm64 Chrome and the only system Chromium on Ubuntu is the snap (which Puppeteer
can't drive), so on arm64 these services couldn't launch a browser. Fixes, all env-gated so production
behaviour is unchanged unless the vars are set:

- All 12 puppeteer.launch() calls now take:
    args: process.env.ODR_CHROME_NO_SANDBOX ? ['--no-sandbox','--disable-setuid-sandbox'] : []
    acceptInsecureCerts: !!process.env.ODR_CHROME_IGNORE_CERT
  ODR_CHROME_NO_SANDBOX works around Ubuntu 24.04's unprivileged-userns restriction (No usable
  sandbox!); ODR_CHROME_IGNORE_CERT accepts the dev box's self-signed odr.io cert
  (net::ERR_CERT_AUTHORITY_INVALID).
- The arm64 Chromium binary is supplied via PUPPETEER_EXECUTABLE_PATH (Playwright's download, which
  does provide arm64 builds) -- no code change needed, Puppeteer reads it automatically.
- DEV_SETUP_ARM.md documents the three env vars, one-time setup, the production-safety rationale, the
  sysctl alternative, and a troubleshooting table.

Node workers only; unrelated to the Symfony upgrade on this branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mponents (still 5.4.*)

The symfony/symfony metapackage is unsupported (the "Using symfony/symfony is NOT supported"
dev-toolbar warning) and is REMOVED in Symfony 6.0, so it must go before the 6.4 bump. Replaced
"symfony/symfony": "5.4.*" with the explicit set of symfony/* packages the app actually uses --
framework/security/twig bundles, doctrine + monolog bridges, and the directly-used components
(console, form, http-foundation, http-kernel, routing, security-*, validator, yaml, lock, intl,
cache, asset, translation, serializer, ...) -- all still pinned 5.4.* so behaviour is unchanged.
web-profiler-bundle + var-dumper moved to require-dev.

composer update split the monolith cleanly (vendor/symfony/symfony gone; 85 individual symfony/*
packages now resolve it). Verified: cache:clear OK, GET /login 200, GET / 302, console commands work.
This is purely the package split -- no version bump yet (that's 5.5).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The legacy security system (anonymous/guard) is removed in Symfony 6.0; migrate now while still on
5.4 (both systems supported) so it can be verified before the bump.

security.yml(.dist):
- enable_authenticator_manager: true
- encoders -> password_hashers (sha512 kept; existing hashes still validate)
- removed all anonymous: true (implicit in the new system)
- api firewall: lexik guard authenticator -> native `jwt: ~`
- form_login csrf_token_generator -> enable_csrf: true
- access_control: IS_AUTHENTICATED_ANONYMOUSLY -> PUBLIC_ACCESS (IS_AUTHENTICATED_FULLY kept)
- .dist: also dropped the dead jms_security_extra block (bundle long removed)

User entity: the new system splits password methods out of UserInterface, so implement
PasswordAuthenticatedUserInterface (getPassword) and -- because sha512 is salted --
LegacyPasswordAuthenticatedUserInterface (getSalt); added ?string return types. Without this the
new CheckCredentialsListener threw a TypeError in PasswordHasherFactory::getPasswordHasher().

Verified: container compiles; debug:firewall shows FormLogin+RememberMe (main) and Lexik
JWTAuthenticator (api); username/password login + JWT issuance confirmed working by the user.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
natestone and others added 30 commits October 7, 2026 15:32
…f a ParagraphText field, and the AMCSD plugin uses this for the table in the DIF file
Automated dependency upgrade by OrbisAI Security

This branch has not been deployed

No deployments
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.

3 participants