Skip to content
Open
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4e797dd
chore: apply Rector to codeception fixture BundleCompilerPass
dotani1111 Jun 4, 2026
d0787f8
feat: MCP サーバ土台と商品/在庫 3 ツールを追加
dotani1111 Jun 4, 2026
0527b4c
test(mcp): 商品/在庫 3 Tool の DB 結合テスト
dotani1111 Jun 4, 2026
bf61603
fix(mcp): EntityArraySerializer の Doctrine Proxy 対応 + 深さデフォルトを 1 に
dotani1111 Jun 8, 2026
e708261
feat(mcp): 注文領域 3 ツールを追加 (search_orders / get_order / get_shipping)
dotani1111 Jun 8, 2026
2801d7a
feat(mcp): 顧客会員領域 3 ツールを追加 (search_customers / get_customer / get_cus…
dotani1111 Jun 8, 2026
28e78a0
feat(mcp): プラグイン管理 2 ツールを追加 (list_plugins / get_plugin)
dotani1111 Jun 8, 2026
4722657
fix(mcp): scope 不足を ToolCallException で返す
dotani1111 Jun 8, 2026
273096f
fix(mcp): Get 系 Tool の不在応答を {"found": false} に統一
dotani1111 Jun 8, 2026
80396f7
feat(mcp): IP / client_id 2 段の Rate Limiter を追加
dotani1111 Jun 8, 2026
7ed1f07
test(mcp): tools/list と allow_list の契約テストを追加
dotani1111 Jun 9, 2026
83cea02
feat(mcp): mcp チャネル直書きを禁止する PHPStan custom rule を追加
dotani1111 Jun 9, 2026
3cf35d6
test(mcp): MCP firewall が oauth2 経路を通ることを統合テスト化
dotani1111 Jun 9, 2026
03fc2c2
test(mcp): 失効 / Member 無効化で即 401 をテスト化 (AC #6 #9)
dotani1111 Jun 9, 2026
16e1b0f
test(mcp): Api44 install/enable で MCP 依存登録を担保 (AC #7)
dotani1111 Jun 9, 2026
2e1f316
refactor(mcp): scope 検査を中央 ReferenceHandler 層に集約 (設計案 A)
dotani1111 Jun 9, 2026
8dc4c9a
refactor(mcp): 監査ログの単一入口を alias 削除で保証 (PHPStan rule 撤去)
dotani1111 Jun 9, 2026
2ba2692
fix(mcp): Rate Limiter を cache 障害時 fail-closed にする
dotani1111 Jun 9, 2026
962cd6e
test(mcp): 正常 JWT 受理を 200 + JSON-RPC result で検証
dotani1111 Jun 9, 2026
9d73280
test(mcp): 全 Tool が McpToolScopeMap に scope を持つ契約テストを追加
dotani1111 Jun 10, 2026
80c89d4
fix(mcp): 監査ログ alias 削除の空振りを build 失敗で検出する
dotani1111 Jun 10, 2026
e319562
test(mcp): scope 強制の配線を回帰テストで担保
dotani1111 Jun 10, 2026
ac42582
fix(mcp): Rate Limiter の監査ログ失敗で拒否レスポンスを崩さない
dotani1111 Jun 10, 2026
3a4ca7e
fix(mcp): 監査ログ失敗時に default チャンネルへフォールバック記録する
dotani1111 Jun 10, 2026
92c7d03
test(mcp): 401 の WWW-Authenticate と経路を検証する
dotani1111 Jun 10, 2026
05f2d28
fix(mcp): 監査ログを専用ファイル mcp.log に分離する
dotani1111 Jun 11, 2026
eca5f3d
test(mcp): 監査ログが mcp.log に分離され site.log に漏れないことを担保
dotani1111 Jun 11, 2026
ddd1fd0
feat(mcp): 認証失敗(401)を mcp.log に記録する
dotani1111 Jun 11, 2026
c264a87
test(mcp): 認証失敗ログの reason 分岐 (ヘッダ有無) を検証
dotani1111 Jun 11, 2026
aca6597
Merge branch '4.4' into feature/poc-mcp
dotani1111 Jun 12, 2026
6b3b383
fix(mcp): 関連 Entity 要約の id 露出を allow_list で塞ぐ
dotani1111 Jun 14, 2026
05c29cf
fix(mcp): Tool の検索キーを正規化し日付書式を検証する
dotani1111 Jun 14, 2026
73228c8
fix(mcp): Tool 結果の data 契約違反を内部エラーとして扱う
dotani1111 Jun 14, 2026
2c610fe
test(mcp): 検索系テストを作成データの出現まで検証する
dotani1111 Jun 14, 2026
fa5fad4
style(mcp): 型注釈と重複する @param mixed $level を除去する
dotani1111 Jun 14, 2026
3ccae9a
fix(mcp): 本体を Api44/league クラスへのハード依存から切り離す
dotani1111 Jun 14, 2026
525091b
test(mcp): Api44 前提テストを mcp グループに分離し専用 CI ジョブで実走
dotani1111 Jun 14, 2026
1db2254
fix(ci): mcp ジョブの Api44 アーカイブを dist 展開可能な形式にする
dotani1111 Jun 14, 2026
ec85cd4
ci(mcp): Api44 を MCP firewall を含む feat/mcp-server-scorp で導入する
dotani1111 Jun 14, 2026
f0d801d
Merge branch '4.4' into feature/poc-mcp
dotani1111 Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 95 additions & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ jobs:
timeout_minutes: 10
max_attempts: 2
retry_on: error
command: vendor/bin/phpunit --exclude-group cache-clear --exclude-group cache-clear-install --exclude-group update-schema-doctrine --exclude-group plugin-service
command: vendor/bin/phpunit --exclude-group cache-clear --exclude-group cache-clear-install --exclude-group update-schema-doctrine --exclude-group plugin-service --exclude-group mcp
env:
APP_ENV: 'test'
DATABASE_URL: ${{ matrix.database_url }}
Expand Down Expand Up @@ -174,3 +174,97 @@ jobs:
echo "session.save_path=$PWD/var/sessions/test" > php.ini
echo "memory_limit=512M" >> php.ini
php -c php.ini vendor/bin/phpunit --group plugin-service

## MCP は Api44 (OAuth2 / scope / allow_list) が前提のため、 メインのマトリクスからは
## --exclude-group mcp で外し、 ここで Api44 を導入した専用環境で --group mcp を実走する。
## Api44 は packagist 非公開のため、 eccube-api4 を checkout → mock-package-api で配信して
## eccube:composer:require する (Api44 自身の CI と同じ方式)。
mcp:
name: PHPUnit (mcp)
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

## MCP の scope 認可と /admin/mcp 用 OAuth2 firewall は Api44 の feat/mcp-server-scorp に実装されており、
## eccube-api4 の 4.4 にはまだ無い (このブランチに無いと firewall が prepend されず /admin/mcp が
## ログインへ 302 リダイレクトし、 McpFirewallContractTest 等が 401/200 を得られず失敗する)。
## 当該ブランチが 4.4 にマージされたら ref を '4.4' に戻すこと。
- name: Checkout Api44 plugin
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
repository: 'EC-CUBE/eccube-api4'
ref: 'feat/mcp-server-scorp'
path: 'eccube-api4'
Comment on lines +186 to +198

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check persist-credentials usage across workflows

rg -nP --type=yaml 'uses:\s+actions/checkout' .github/workflows/ -A5 | rg 'persist-credentials'

Repository: EC-CUBE/ec-cube

Length of output: 41


🌐 Web query:

actions/checkout persist-credentials default behavior security GitHub token

💡 Result:

The persist-credentials input in actions/checkout defaults to true [1][2][3]. When enabled, this setting configures the local Git environment—specifically the Git configuration—to use the provided GitHub token or SSH key [1][4][2]. This allows subsequent steps in the same GitHub Actions job to execute authenticated Git commands (such as git fetch or git push) without needing to re-authenticate [1][4][2]. From a security perspective, persisting credentials makes them available to any subsequent process or action running within that job [5][6]. Historically, this meant the token was stored directly in.git/config, which made it easily accessible to other steps [5][6]. Recent versions of actions/checkout have improved security by storing these credentials in a separate, temporary file under $RUNNER_TEMP rather than directly in.git/config, though they remain accessible to the workflow process [1][7]. If your security requirements dictate that subsequent steps should not have access to these credentials, you should explicitly set persist-credentials to false in your workflow configuration [1][4][2]: - with: persist-credentials: false While setting this to false prevents the action from configuring Git with the token, it does not affect the inherent accessibility of the GITHUB_TOKEN if it is explicitly passed to other actions or scripts [5][6]. Always ensure your workflow permissions are set to the least privilege necessary [5][6][2].

Citations:


🏁 Script executed:

# Verify the specific lines in the workflow file
cat -n .github/workflows/unit-test.yml | sed -n '186,194p'

Repository: EC-CUBE/ec-cube

Length of output: 447


checkout アクションに persist-credentials: false の設定を検討してください。

両方の checkout アクション(本体リポジトリと Api44 リポジトリ)で persist-credentials が設定されていません。デフォルトでは true となり、GITHUB_TOKEN がジョブ内の後続ステップからアクセス可能な状態になります。最新バージョンでは認証情報は .git/config ではなく一時ファイル($RUNNER_TEMP 配下)に保存されていますが、ワークフロープロセスには引き続きアクセス可能です。

最小権限の原則に従い、必要がなければ両方の checkout に persist-credentials: false を追加することを推奨します。

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 186-187: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[warning] 189-194: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/unit-test.yml around lines 186 - 194, Both checkout
actions in the workflow are missing the `persist-credentials: false`
configuration. Add `persist-credentials: false` to the "Checkout" action that
checks out the main repository and also add `persist-credentials: false` to the
"Checkout Api44 plugin" action that checks out the eccube-api4 repository. This
follows the principle of least privilege by preventing the GITHUB_TOKEN from
being unnecessarily accessible in subsequent workflow steps.

Source: Linters/SAST tools


- name: Setup PostgreSQL
uses: ankane/setup-postgres@6d3ffa1aa7498a42b79e9f9f5838b99971839300 # v1
with:
postgres-version: '16'
user: postgres
- name: Configure PostgreSQL
run: |
for _ in $(seq 1 30); do pg_isready -h 127.0.0.1 && break; sleep 1; done
pg_isready -h 127.0.0.1 || { echo 'PostgreSQL did not become ready within 30s'; exit 1; }
psql -h 127.0.0.1 -U postgres -c "ALTER USER postgres PASSWORD 'password'"

- name: Setup PHP
uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # v2
with:
php-version: '8.3'
github-token: ''
extensions: :xdebug, redis
- name: Initialize Composer
uses: ./.github/actions/composer

- name: Generate ECCUBE_AUTH_MAGIC
run: echo "ECCUBE_AUTH_MAGIC=$(openssl rand -hex 32)" >> $GITHUB_ENV

- name: Setup mock-package-api
run: |
# eccube-api4 自身の CI と同じ形式で固める (.git/.github 等の dotfiles を除外し ./* で固める)。
# .git を含めると composer の dist 展開が失敗する (Install of ec-cube/api44 failed)。
( cd eccube-api4 && tar cvzf "$GITHUB_WORKSPACE/Api44.tar.gz" ./* )
mkdir -p /tmp/repos
cp "$GITHUB_WORKSPACE/Api44.tar.gz" /tmp/repos/Api44.tgz
docker run --name package-api -d -v /tmp/repos:/repos -e MOCK_REPO_DIR=/repos -p 8080:8080 eccube/mock-package-api:composer2

- name: Setup EC-CUBE
env:
APP_ENV: 'test'
DATABASE_URL: postgres://postgres:password@127.0.0.1:5432/eccube_db
DATABASE_SERVER_VERSION: 16
DATABASE_CHARSET: utf8
ECCUBE_AUTH_MAGIC: ${{ env.ECCUBE_AUTH_MAGIC }}
run: |
bin/console doctrine:database:create
bin/console doctrine:schema:create
bin/console eccube:fixtures:load

- name: Install Api44
env:
APP_ENV: 'test'
DATABASE_URL: postgres://postgres:password@127.0.0.1:5432/eccube_db
DATABASE_SERVER_VERSION: 16
DATABASE_CHARSET: utf8
ECCUBE_PACKAGE_API_URL: 'http://127.0.0.1:8080'
USE_SELFSIGNED_SSL_CERTIFICATE: '1'
run: |
bin/console doctrine:query:sql "update dtb_base_info set authentication_key='dummy'"
bin/console eccube:composer:require ec-cube/api44
bin/console eccube:plugin:enable --code=Api44
bin/console doctrine:schema:update --force --dump-sql
bin/console cache:clear --no-warmup
chmod 600 app/PluginData/Api44/oauth/private.key

- name: PHPUnit (mcp)
env:
APP_ENV: 'test'
DATABASE_URL: postgres://postgres:password@127.0.0.1:5432/eccube_db
DATABASE_SERVER_VERSION: 16
DATABASE_CHARSET: utf8
MAILER_URL: 'smtp://127.0.0.11025'
run: |
echo "session.save_path=$PWD/var/sessions/test" > php.ini
echo "memory_limit=512M" >> php.ini
php -c php.ini vendor/bin/phpunit --group mcp
1 change: 1 addition & 0 deletions app/config/eccube/bundles.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Eccube\EccubeBundle::class => ['all' => true],
Symfony\AI\McpBundle\McpBundle::class => ['all' => true],
];
2 changes: 2 additions & 0 deletions app/config/eccube/packages/dev/monolog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ monolog:
level: debug
formatter: eccube.log.formatter.line
max_files: 10
# mcp は専用ハンドラ (mcp) に出すため main(site.log) からは除外する
channels: ['!mcp']
console:
type: console
process_psr_3_messages: false
Expand Down
10 changes: 10 additions & 0 deletions app/config/eccube/packages/http_discovery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
Psr\Http\Message\RequestFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\ResponseFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\ServerRequestFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\StreamFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\UploadedFileFactoryInterface: '@http_discovery.psr17_factory'
Psr\Http\Message\UriFactoryInterface: '@http_discovery.psr17_factory'

http_discovery.psr17_factory:
class: Http\Discovery\Psr17Factory
14 changes: 14 additions & 0 deletions app/config/eccube/packages/mcp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mcp:
app: 'EC-CUBE MCP Server'
version: '4.4.0'
description: 'EC-CUBE 4.4 の管理データ (商品/在庫・注文・顧客会員・プラグイン管理) を AI クライアントから自然言語で参照する読み取り専用 MCP サーバ。 認証認可は API プラグイン (api44) の OAuth2 / scope に委譲する。'
client_transports:
http: true
stdio: true
http:
path: '/%eccube_admin_route%/mcp'
session:
store: file
discovery:
scan_dirs:
- src/Eccube/Service/Mcp/Tool
19 changes: 19 additions & 0 deletions app/config/eccube/packages/mcp_rate_limiter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# MCP サーバの Rate Limiter 設定 (設計 §5「Rate Limiter 連携」)。
#
# 2 段構成:
# - mcp_ip: リモート IP 単位の制限 (firewall 前で消費、 認証エラー連発攻撃にも効く)
# - mcp_client: OAuth2 client_id 単位の制限 (firewall 通過後の OAuth2Token から client_id を取得して消費)
#
# 既定値は PoC レベルの控えめな値。 GA 運用開始時に再評価する。
framework:
rate_limiter:
mcp_ip:
policy: fixed_window
limit: 60
interval: '1 minute'
cache_pool: rate_limiter.cache
mcp_client:
policy: fixed_window
limit: 300
interval: '1 minute'
cache_pool: rate_limiter.cache
14 changes: 13 additions & 1 deletion app/config/eccube/packages/monolog.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
monolog:
channels: ['front', 'admin']
channels: ['front', 'admin', 'mcp']
handlers:
# MCP 監査ログ: PII を含み得るため site.log と分離した専用ファイルに、 1 レコード 1 JSON で出力する。
# fingers_crossed を挟まず info から常時書き出す (監査記録は error 連動で握り潰してはならない)。
# 保管日数は ECCUBE_MCP_LOG_RETENTION_DAYS (既定 90)。 ファイルは所有者/グループのみ読める権限にする。
mcp:
type: rotating_file
path: '%kernel.logs_dir%/%kernel.environment%/mcp.log'
channels: ['mcp']
level: info
formatter: eccube.mcp.log.formatter.json
max_files: '%env(int:ECCUBE_MCP_LOG_RETENTION_DAYS)%'
file_permission: 0640
3 changes: 2 additions & 1 deletion app/config/eccube/packages/prod/monolog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ monolog:
handler: main_rotating_file
excluded_http_codes: [404, 405]
buffer_size: 50
channels: ['!doctrine', '!event', '!php']
# mcp は専用ハンドラ (mcp) に出すため main(site.log) からは除外する
channels: ['!doctrine', '!event', '!php', '!mcp']
main_rotating_file:
type: rotating_file
max_files: 60
Expand Down
3 changes: 3 additions & 0 deletions app/config/eccube/routes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ controllers:
customize_controllers:
resource: ../../../app/Customize/Controller
type: attribute
mcp:
resource: .
type: mcp

# prefix: /{_locale}
# prefix: /
Expand Down
25 changes: 25 additions & 0 deletions app/config/eccube/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ parameters:
env(ECCUBE_LOCALE): 'ja'
env(ECCUBE_TIMEZONE): 'Asia/Tokyo'
env(ECCUBE_CURRENCY): 'JPY'
env(ECCUBE_MCP_ALLOWED_ORIGINS): ''
# MCP 監査ログ (mcp.log) の保管日数 (rotating_file の世代数)。 設計 §4.2
env(ECCUBE_MCP_LOG_RETENTION_DAYS): '90'
locale: '%env(ECCUBE_LOCALE)%'
timezone: '%env(ECCUBE_TIMEZONE)%'
currency: '%env(ECCUBE_CURRENCY)%'
Expand All @@ -28,6 +31,9 @@ services:
$shoppingPurchaseFlow: '@eccube.purchase.flow.shopping'
$orderPurchaseFlow: '@eccube.purchase.flow.order'
$_orderStateMachine: '@state_machine.order'
# MCP サーバ用 (path prefix と Origin 許可リスト)
$eccubeAdminRoute: '%eccube_admin_route%'
$mcpAllowedOriginsCsv: '%env(ECCUBE_MCP_ALLOWED_ORIGINS)%'

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
Expand Down Expand Up @@ -215,6 +221,25 @@ services:
Eccube\EventListener\RateLimiterListener:
arguments: [ !tagged_locator { tag: 'eccube_rate_limiter' } ]

# MCP: Api44 (ec-cube/api44) の allow_list (`eccube.api.allow_list` タグ) を集約する
Eccube\Service\Mcp\AllowListResolver:
arguments:
$allowLists: !tagged_iterator eccube.api.allow_list

# MCP: scope 強制層。 $inner (本物の Tool 実行器) は McpScopeEnforcementPass が構築する
Eccube\Service\Mcp\ScopeEnforcingReferenceHandler:
arguments:
$inner: '@eccube.mcp.reference_handler.inner'

# MCP: 監査ログとして記録するため、唯一の書き手として設定します。
Eccube\Service\Mcp\McpAuditLogger:
arguments:
$mcpLogger: '@monolog.logger.mcp'

# MCP: 監査ログ (mcp.log) は 1 レコード 1 JSON で出力する (機械可読・設計 §4.2)
eccube.mcp.log.formatter.json:
class: Monolog\Formatter\JsonFormatter

Eccube\Security\PasswordHasher\PasswordHasher:
arguments:
- '%eccube_auth_magic%'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Plugin\Bundle\DependencyInjection\Compiler;

use League\Bundle\OAuth2ServerBundle\EventListener\AddClientDefaultScopesListener;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

Expand All @@ -22,8 +23,8 @@ public function process(ContainerBuilder $container)
{
$plugins = $container->getParameter('eccube.plugins.enabled');
if (!in_array('Bundle', $plugins)) {
if ($container->hasDefinition('League\Bundle\OAuth2ServerBundle\EventListener\AddClientDefaultScopesListener')) {
$def = $container->getDefinition('League\Bundle\OAuth2ServerBundle\EventListener\AddClientDefaultScopesListener');
if ($container->hasDefinition(AddClientDefaultScopesListener::class)) {
$def = $container->getDefinition(AddClientDefaultScopesListener::class);
$def->clearTags();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Plugin\Bundle\DependencyInjection\Compiler;

use League\Bundle\OAuth2ServerBundle\EventListener\AddClientDefaultScopesListener;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

Expand All @@ -22,8 +23,8 @@ public function process(ContainerBuilder $container)
{
$plugins = $container->getParameter('eccube.plugins.enabled');
if (!in_array('Bundle', $plugins)) {
if ($container->hasDefinition('League\Bundle\OAuth2ServerBundle\EventListener\AddClientDefaultScopesListener')) {
$def = $container->getDefinition('League\Bundle\OAuth2ServerBundle\EventListener\AddClientDefaultScopesListener');
if ($container->hasDefinition(AddClientDefaultScopesListener::class)) {
$def = $container->getDefinition(AddClientDefaultScopesListener::class);
$def->clearTags();
}
}
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"symfony/lock": "^7.4",
"symfony/mailer": "^7.4",
"symfony/maker-bundle": "^1.0",
"symfony/mcp-bundle": "^0.9",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"symfony/monolog-bridge": "^7.4",
"symfony/monolog-bundle": "^3.1",
"symfony/options-resolver": "^7.4",
Expand Down
Loading
Loading