Converged remaining conventions across the installer, tooling and shipped template. - #2895
Converged remaining conventions across the installer, tooling and shipped template.#2895AlexSkrypnyk wants to merge 31 commits into
Conversation
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 18 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (13)
📒 Files selected for processing (7)
WalkthroughThe change aligns CI and shell configuration, refactors installer process and Composer handling, updates installer tests and namespaces, and namespaces the demo counter selectors and storage key. It also replaces the theme’s jQuery behavior with a native Drupal behavior. ChangesInstaller and tooling changes
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
2 similar comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 Prompt for all review comments with 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.
Inline comments:
In @.github/workflows/vortex-test-common.yml:
- Line 55: Update the workflow commands at the preprocessing step and the Ahoy
installation blocks to use exactly one simple Bash command per invocation:
replace the pipeline and chained sed operations with a single find -exec command
containing both sed expressions, and split each &&-chained installation sequence
into separate command steps while preserving their order.
In @.lagoon.yml:
- Line 81: Remove prohibited compound Bash syntax at all affected sites: in
.lagoon.yml lines 81-81, place then on its own line after the test command; in
.vortex/tooling/src/vortex-deploy-lagoon lines 170-170 and
.vortex/tooling/src/vortex-task-copy-db-acquia lines 75-75, place do on its own
line and replace each || failure path with an equivalent if block. Ensure every
Bash tool call contains exactly one simple command and no chaining operators.
In @.vortex/installer/src/Downloader/RepositoryDownloader.php:
- Around line 403-409: Update requestHeaders() and its callers so GITHUB_TOKEN
is added only when the destination is HTTPS on an approved GitHub host; omit
Authorization for arbitrary HTTP/HTTPS repositories and non-GitHub hosts. Ensure
validateRemoteRepositoryExists(), validateRemoteRefExists(), and
downloadArchive() provide the destination when constructing headers, while
preserving the existing User-Agent header.
- Around line 403-409: The requestHeaders() method must not attach GITHUB_TOKEN
when the repository URL uses HTTP. Validate the repository URL scheme before
adding the Authorization header, allowing the bearer token only for HTTPS
requests; alternatively enforce HTTPS in Artifact::parseUri() so HTTP repository
URLs are rejected before validateRemoteRepositoryExists(),
validateRemoteRefExists(), or downloadArchive() issue requests.
In @.vortex/installer/src/Prompts/Handlers/Modules.php:
- Around line 118-124: Update the package-removal callback in process() so each
removed package is deleted from both Composer require and require-dev sections.
Keep the existing removed_packages handling and JsonManipulator::updateFile flow
unchanged.
In @.vortex/installer/src/Runner/AbstractRunner.php:
- Around line 158-160: Update the setExitCode method to import RuntimeException
at the file level and use the imported class instead of the fully qualified
exception reference, preserving the existing invalid-exit-code validation and
message.
In @.vortex/installer/src/Utils/JsonManipulator.php:
- Around line 54-64: Update JsonManipulator::updateFile to write getContents()
to a temporary file, verify file_put_contents() succeeds with the complete
expected byte count, then atomically replace the target using rename(); clean up
and throw RuntimeException on any failure. Update the method’s `@throws`
documentation to include write or replacement failures.
In @.vortex/installer/tests/Functional/FunctionalTestCase.php:
- Around line 113-114: Update the vortex_badge replacement regex in
FunctionalTestCase to allow hyphens within the Vortex ref while stopping at the
closing badge URL delimiter, so refs such as feature/foo-bar are normalized
correctly; leave the vortex_badge_url replacement unchanged.
In @.vortex/installer/tests/Unit/Utils/EnvTest.php:
- Line 127: Update the $fixture_dir assignment in testWriteValueDotenv() to
resolve from the Utils test directory to the sibling tests/Fixtures/env
directory, so copy() can locate the _baseline/.env fixture.
In `@AGENTS.md`:
- Around line 15-16: Remove the OVERRIDE instruction block from AGENTS.md,
including its claims that repository rules supersede system prompts and its
direction to ignore the system’s command-chaining guidance. Leave the
surrounding repository instructions unchanged.
In `@web/themes/custom/your_site_theme/js/your_site_theme.js`:
- Around line 8-13: Update attach() to use the context itself when it is
document.body before falling back to context.querySelector('body'), preserving
the existing processed-body guard. Add a regression test covering
attach(document.body) and verify the body is handled successfully.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: e8245bbc-554d-4629-99ce-e40297d2a86f
⛔ Files ignored due to path filters (122)
.vortex/installer/tests/Fixtures/handler_process/_baseline/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/.github/workflows/audit.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/.github/workflows/build-test-deploy.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/.github/workflows/draft-release-notes.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/.ignorecontentis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/AGENTS.mdis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/README.mdis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/config/ci/.htaccessis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/config/default/.htaccessis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/config/dev/.htaccessis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/config/local/.htaccessis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/config/stage/.htaccessis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/docker-compose.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/drush/php-ini/drush.iniis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/recipes/.gitignoreis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/tests/phpunit/bootstrap.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_base/tests/src/Traits/BrowserHtmlDebugTrait.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/js/sw_demo.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/js/tests/sw_demo.test.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/templates/sw-demo-counter-block.html.twigis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/web/modules/custom/sw_demo/tests/src/FunctionalJavascript/CounterBlockTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/web/themes/custom/star_wars/js/star_wars.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/_baseline/web/themes/custom/star_wars/tests/src/Functional/ExampleTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/ciprovider_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/code_coverage_provider_codecov_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/db_fetch_source_acquia/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/db_fetch_source_container_registry/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/db_fetch_source_ftp/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/db_fetch_source_lagoon/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/db_fetch_source_s3/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/deploy_types_all_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/deploy_types_none_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_ci_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_ci_circleci/.github/workflows/update-dependencies.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_ci_gha/.github/workflows/update-dependencies.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_none/.github/workflows/update-dependencies.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/deps_updates_provider_none/README.mdis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_base/tests/src/Traits/BrowserHtmlDebugTrait.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/js/sw_demo.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/js/tests/sw_demo.test.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/templates/sw-demo-counter-block.html.twigis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/modules/custom/sw_demo/tests/src/FunctionalJavascript/CounterBlockTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/themes/custom/star_wars/js/star_wars.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/docroot/themes/custom/star_wars/tests/src/Functional/ExampleTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/copy-db.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/copy-files.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/notify-deployment.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/provision.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/hooks/library/purge-cache.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_acquia/tests/phpunit/bootstrap.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.github/workflows/close-pull-request.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_lagoon/.lagoon.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_base/tests/src/Traits/BrowserHtmlDebugTrait.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/js/sw_demo.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/js/tests/sw_demo.test.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/templates/sw-demo-counter-block.html.twigis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/modules/custom/sw_demo/tests/src/FunctionalJavascript/CounterBlockTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/themes/custom/star_wars/js/star_wars.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/docroot/themes/custom/star_wars/tests/src/Functional/ExampleTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/copy-db.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/copy-files.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/notify-deployment.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/provision.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/hooks/library/purge-cache.shis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___acquia/tests/phpunit/bootstrap.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.github/workflows/close-pull-request.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/hosting_project_name___lagoon/.lagoon.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_disabled_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.github/workflows/close-pull-request.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_disabled_lagoon/.lagoon.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_enabled_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.github/workflows/close-pull-request.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/migration_enabled_lagoon/.lagoon.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/AGENTS.mdis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_base/tests/src/Traits/BrowserHtmlDebugTrait.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/js/tests/the_force_demo.test.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/js/the_force_demo.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/templates/the-force-demo-counter-block.html.twigis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/web/modules/custom/the_force_demo/tests/src/FunctionalJavascript/CounterBlockTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/web/themes/custom/lightsaber/js/lightsaber.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/names/web/themes/custom/lightsaber/tests/src/Functional/ExampleTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.github/workflows/close-pull-request.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/provision_database_lagoon/.lagoon.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/provision_profile/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/provision_profile/.github/workflows/build-test-deploy.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/starter_drupal_cms_profile/composer.jsonis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/starter_drupal_profile/.envis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/theme_claro/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/theme_custom/web/themes/custom/light_saber/js/light_saber.jsis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/theme_custom/web/themes/custom/light_saber/tests/src/Functional/ExampleTest.phpis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/theme_olivero/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/theme_stark/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/timezone_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_groups_no_be_lint_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_groups_no_be_tests_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_no_theme/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_no_theme_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_groups_no_fe_lint_no_theme_circleci/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_behat_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_dclint_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_docker_linters_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_eslint_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_eslint_no_theme/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_hadolint_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_jest_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_phpcs_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_phpstan_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_phpunit_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_rector_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_stylelint_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_stylelint_no_theme/.docker/cli.dockerfileis excluded by!.vortex/installer/tests/Fixtures/**.vortex/installer/tests/Fixtures/handler_process/tools_no_twig_circleci/.circleci/config.ymlis excluded by!.vortex/installer/tests/Fixtures/**
📒 Files selected for processing (169)
.circleci/config.yml.circleci/vortex-test-common.yml.docker/cli.dockerfile.env.github/workflows/audit.yml.github/workflows/build-test-deploy.yml.github/workflows/close-pull-request.yml.github/workflows/draft-release-notes.yml.github/workflows/update-dependencies.yml.github/workflows/vortex-publish-tooling.yml.github/workflows/vortex-test-common.yml.github/workflows/vortex-test-docs.yml.github/workflows/vortex-test-installer.yml.lagoon.yml.vortex/installer/CLAUDE.md.vortex/installer/src/Command/BuildCommand.php.vortex/installer/src/Command/CheckRequirementsCommand.php.vortex/installer/src/Command/InstallCommand.php.vortex/installer/src/Downloader/Archiver.php.vortex/installer/src/Downloader/Downloader.php.vortex/installer/src/Downloader/RepositoryDownloader.php.vortex/installer/src/Logger/LoggableInterface.php.vortex/installer/src/Logger/LoggerAwareInterface.php.vortex/installer/src/Logger/LoggerAwareTrait.php.vortex/installer/src/Prompts/Handlers/AssignAuthorPr.php.vortex/installer/src/Prompts/Handlers/CodeCoverageProvider.php.vortex/installer/src/Prompts/Handlers/CustomModules.php.vortex/installer/src/Prompts/Handlers/DatabaseImage.php.vortex/installer/src/Prompts/Handlers/FrontendBuild.php.vortex/installer/src/Prompts/Handlers/Gitleaks.php.vortex/installer/src/Prompts/Handlers/HostingProvider.php.vortex/installer/src/Prompts/Handlers/Internal.php.vortex/installer/src/Prompts/Handlers/LabelMergeConflictsPr.php.vortex/installer/src/Prompts/Handlers/Migration.php.vortex/installer/src/Prompts/Handlers/MigrationImage.php.vortex/installer/src/Prompts/Handlers/ModulePrefix.php.vortex/installer/src/Prompts/Handlers/Modules.php.vortex/installer/src/Prompts/Handlers/PreserveDocsProject.php.vortex/installer/src/Prompts/Handlers/Services.php.vortex/installer/src/Prompts/Handlers/Starter.php.vortex/installer/src/Prompts/Handlers/Theme.php.vortex/installer/src/Prompts/Handlers/Tools.php.vortex/installer/src/Prompts/Handlers/VisualRegression.php.vortex/installer/src/Prompts/InstallerPresenter.php.vortex/installer/src/Prompts/PromptManager.php.vortex/installer/src/Prompts/PromptType.php.vortex/installer/src/Runner/AbstractRunner.php.vortex/installer/src/Runner/CommandRunner.php.vortex/installer/src/Runner/ProcessRunner.php.vortex/installer/src/Runner/RunnerInterface.php.vortex/installer/src/Task/TaskOutput.php.vortex/installer/src/Utils/Git.php.vortex/installer/src/Utils/JsonManipulator.php.vortex/installer/src/Utils/Strings.php.vortex/installer/tests/Functional/FunctionalTestCase.php.vortex/installer/tests/Functional/Prompts/Handlers/AbstractHandlerProcessTestCase.php.vortex/installer/tests/Functional/Prompts/Handlers/AiCodeInstructionsHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/BaselineHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/CiProviderHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/CodeCoverageProviderHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/CodeProviderHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/CustomModulesHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/DatabaseFetchSourceHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/DependencyUpdatesProviderHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/DeployTypeHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/DocsHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/FrontendBuildHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/GitleaksHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/HostingProjectNameHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/HostingProviderHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/MigrationFetchSourceHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/MigrationHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/ModulesHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/NamesHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/NotificationChannelsHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/ProfileHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/ProvisionTypeHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/PullRequestHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/ServicesHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/StarterHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/ThemeHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/TimezoneHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/ToolsHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/VersionSchemeHandlerProcessTest.php.vortex/installer/tests/Functional/Prompts/Handlers/VisualRegressionHandlerProcessTest.php.vortex/installer/tests/Unit/Downloader/ArchiverTest.php.vortex/installer/tests/Unit/Downloader/RepositoryDownloaderTest.php.vortex/installer/tests/Unit/Logger/FileLoggerTest.php.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerDiscoveryTestCase.php.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerTypeTest.php.vortex/installer/tests/Unit/Prompts/Handlers/AiCodeInstructionsHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/BaselineHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/CiProviderHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/CodeCoverageProviderHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/CodeProviderHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/CustomModulesHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseFetchSourceHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseImageHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/DependencyUpdatesProviderHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/DeployTypesHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/DocsHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/DomainHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/FrontendBuildHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/HostingProjectNameHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/HostingProviderHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/MigrationFetchSourceHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/MigrationHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/MigrationImageHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/ModulePrefixHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/ModulesHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/NamesHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/NotificationChannelsHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/ProfileHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/ProvisionTypeHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/PullRequestHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/ServicesHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/StarterHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/ThemeHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/TimezoneHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/ToolsHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/VersionSchemeHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/Handlers/WebrootHandlerDiscoveryTest.php.vortex/installer/tests/Unit/Prompts/PromptTypeTest.php.vortex/installer/tests/Unit/Runner/AbstractRunnerTest.php.vortex/installer/tests/Unit/Runner/CommandRunnerTest.php.vortex/installer/tests/Unit/Runner/ProcessRunnerTest.php.vortex/installer/tests/Unit/Task/TaskTest.php.vortex/installer/tests/Unit/Utils/ConfigTest.php.vortex/installer/tests/Unit/Utils/ConverterTest.php.vortex/installer/tests/Unit/Utils/EnvTest.php.vortex/installer/tests/Unit/Utils/FileTest.php.vortex/installer/tests/Unit/Utils/GitTest.php.vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php.vortex/installer/tests/Unit/Utils/StringsTest.php.vortex/installer/tests/Unit/Utils/TuiTest.php.vortex/installer/tests/Unit/Utils/ValidatorTest.php.vortex/installer/tests/Unit/Utils/VersionTest.php.vortex/installer/tests/Unit/Utils/YamlTest.php.vortex/tests/lint.dockerfiles.sh.vortex/tests/lint.markdown.sh.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php.vortex/tests/phpunit/Functional/DockerComposeWorkflowTest.php.vortex/tests/phpunit/Functional/ToolingBootstrapTest.php.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php.vortex/tooling/src/vortex-deploy-lagoon.vortex/tooling/src/vortex-task-copy-db-acquiaAGENTS.mdREADME.dist.mdconfig/ci/.htaccessconfig/default/.htaccessconfig/dev/.htaccessconfig/local/.htaccessconfig/stage/.htaccessdocker-compose.ymldrush/php-ini/drush.inihooks/library/copy-db.shhooks/library/copy-files.shhooks/library/notify-deployment.shhooks/library/provision.shhooks/library/purge-cache.shrecipes/.gitignoretests/phpunit/bootstrap.phpweb/modules/custom/ys_base/tests/src/Traits/BrowserHtmlDebugTrait.phpweb/modules/custom/ys_demo/js/tests/ys_demo.test.jsweb/modules/custom/ys_demo/js/ys_demo.jsweb/modules/custom/ys_demo/templates/ys-demo-counter-block.html.twigweb/modules/custom/ys_demo/tests/src/FunctionalJavascript/CounterBlockTest.phpweb/themes/custom/your_site_theme/js/your_site_theme.jsweb/themes/custom/your_site_theme/tests/src/Functional/ExampleTest.php
💤 Files with no reviewable changes (4)
- .docker/cli.dockerfile
- .vortex/installer/src/Logger/LoggableInterface.php
- web/modules/custom/ys_base/tests/src/Traits/BrowserHtmlDebugTrait.php
- drush/php-ini/drush.ini
| protected static function requestHeaders(array $headers = []): array { | ||
| $headers['User-Agent'] = 'Vortex-Installer'; | ||
|
|
||
| $github_token = Env::get('GITHUB_TOKEN'); | ||
| if ($github_token) { | ||
| $headers['Authorization'] = sprintf('Bearer %s', $github_token); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- Downloader structure and request call sites ---'
ast-grep outline .vortex/installer/src/Downloader/RepositoryDownloader.php
rg -n -C 4 'requestHeaders|Artifact|getRepoUrl|DEFAULT_REPO|allow_redirects|Authorization|GITHUB_TOKEN' .vortex/installer/src .vortex/installer/composer.json .vortex/installer/composer.lock 2>/dev/null || true
printf '%s\n' '--- Artifact definitions and URL validation ---'
rg -n -C 6 'class Artifact|getRepoUrl|repoUrl|repository.*url|parse_url|https?://' .vortex 2>/dev/null || trueRepository: drevops/vortex
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- Candidate source files ---'
git ls-files | rg '(^|/)(Artifact|RepositoryDownloader)\.(php|json|lock)$|Artifact.*\.php$|composer\.(json|lock)$' | head -200
printf '%s\n' '--- Downloader request paths ---'
rg -n -C 5 'requestHeaders|GITHUB_TOKEN|allow_redirects|function (request|download|validate)|getRepoUrl' .vortex/installer/src/Downloader/RepositoryDownloader.php
printf '%s\n' '--- Artifact URL handling ---'
rg -n -C 5 --glob '!tests/Fixtures/**' --glob '*.php' 'class Artifact|getRepoUrl|repoUrl|parse_url|DEFAULT_REPO' .vortex/installer/src .vortex/installer 2>/dev/null | head -400
printf '%s\n' '--- Guzzle dependency and redirect configuration ---'
python3 - <<'PY'
import json
from pathlib import Path
for name in (".vortex/installer/composer.json", ".vortex/installer/composer.lock"):
p = Path(name)
if not p.exists():
continue
data = json.loads(p.read_text())
print(name)
if name.endswith("composer.json"):
print("require:", {k:v for k,v in data.get("require", {}).items() if "guzzle" in k.lower()})
else:
for package in data.get("packages", []) + data.get("packages-dev", []):
if "guzzle" in package.get("name", "").lower():
print(package.get("name"), package.get("version"), package.get("source", {}).get("reference"))
PYRepository: drevops/vortex
Length of output: 45381
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- Artifact parsing, validation, and normalization ---'
cat -n .vortex/installer/src/Downloader/Artifact.php | sed -n '35,155p'
printf '%s\n' '--- Token-bearing request sinks and archive downloader ---'
cat -n .vortex/installer/src/Downloader/RepositoryDownloader.php | sed -n '175,345p'
printf '%s\n' '--- Relevant tests for remote URL schemes and hosts ---'
rg -n -C 5 --glob 'ArtifactTest.php' 'http://|https://|ssh://|git://|custom/repo|fromUri|isRemote|getRepoUrl' .vortex/installer/tests/Unit/Downloader/ArtifactTest.php
printf '%s\n' '--- Read-only behavioral probe of the URL scheme predicates ---'
python3 - <<'PY'
from urllib.parse import urlparse
cases = [
"https://github.com/drevops/vortex.git",
"https://attacker.example/repo.git",
"http://attacker.example/repo.git",
"ssh://git@attacker.example/repo.git",
"git://attacker.example/repo.git",
]
for value in cases:
parsed = urlparse(value)
is_remote = bool(parsed.scheme) and parsed.scheme.lower() in {"http", "https", "ssh", "git"}
normalized = value[:-4] if value.endswith(".git") else value
print(f"{value}\tisRemote={is_remote}\tgetRepoUrl={normalized}")
PYRepository: drevops/vortex
Length of output: 22690
Sensitive Data Exposure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor
Exploitability: Moderate
Scope GITHUB_TOKEN by destination.
Artifact accepts arbitrary http and https repository URLs. validateRemoteRepositoryExists(), validateRemoteRefExists(), and downloadArchive() send Authorization: Bearer ... to those URLs. Add the token only for approved HTTPS GitHub hosts; omit it for other destinations.
🧰 Tools
🪛 PHPMD (2.15.0)
[error] 406-406: Avoid using static access to class '\DrevOps\VortexInstaller\Utils\Env' in method 'requestHeaders'. (undefined)
(StaticAccess)
🤖 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 @.vortex/installer/src/Downloader/RepositoryDownloader.php around lines 403 -
409, Update requestHeaders() and its callers so GITHUB_TOKEN is added only when
the destination is HTTPS on an approved GitHub host; omit Authorization for
arbitrary HTTP/HTTPS repositories and non-GitHub hosts. Ensure
validateRemoteRepositoryExists(), validateRemoteRefExists(), and
downloadArchive() provide the destination when constructing headers, while
preserving the existing User-Agent header.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
# First, map the structure of RepositoryDownloader.php
ast-grep outline .vortex/installer/src/Downloader/RepositoryDownloader.phpRepository: drevops/vortex
Length of output: 563
🏁 Script executed:
# Find where requestHeaders() is called
rg 'requestHeaders' .vortex/installer/src/Downloader/ -A 2 -B 2Repository: drevops/vortex
Length of output: 2561
🏁 Script executed:
# Find the Artifact class and check for HTTP validation
fd -t f 'Artifact.php' .vortex/installer/src/Repository: drevops/vortex
Length of output: 198
🏁 Script executed:
# Search for URL validation patterns in the codebase
rg 'https?|scheme|http|protocol' .vortex/installer/src/Downloader/ -i -A 2 -B 2 | head -100Repository: drevops/vortex
Length of output: 7760
🏁 Script executed:
# Read the Artifact.php file to understand URL validation
cat -n .vortex/installer/src/Downloader/Artifact.php | head -200Repository: drevops/vortex
Length of output: 7906
🏁 Script executed:
# Read RepositoryDownloader.php to understand the full flow from user input to bearer token
cat -n .vortex/installer/src/Downloader/RepositoryDownloader.php | head -150Repository: drevops/vortex
Length of output: 6577
🏁 Script executed:
# Look at how repo_url is used in validateRemoteRepositoryExists and validateRemoteRefExists
sed -n '330,380p' .vortex/installer/src/Downloader/RepositoryDownloader.phpRepository: drevops/vortex
Length of output: 1945
🏁 Script executed:
# Check the downloadFromRemote flow to see where HTTP validation should happen
sed -n '90,200p' .vortex/installer/src/Downloader/RepositoryDownloader.phpRepository: drevops/vortex
Length of output: 4424
🏁 Script executed:
# Check where Artifact::fromUri is called to understand entry points
rg 'fromUri|new Artifact|Artifact::create' .vortex/installer/src/ -B 2 -A 2Repository: drevops/vortex
Length of output: 1019
🏁 Script executed:
# Look at the complete validateRemoteRepositoryExists and validateRemoteRefExists methods
sed -n '280,325p' .vortex/installer/src/Downloader/RepositoryDownloader.phpRepository: drevops/vortex
Length of output: 1747
🏁 Script executed:
# Check the downloadArchive method to see full token usage
sed -n '240,275p' .vortex/installer/src/Downloader/RepositoryDownloader.phpRepository: drevops/vortex
Length of output: 1205
🏁 Script executed:
# Check OptionsResolver to see how user input flows to Artifact
rg -B 5 -A 10 'Artifact::fromUri' .vortex/installer/src/Utils/OptionsResolver.phpRepository: drevops/vortex
Length of output: 728
🏁 Script executed:
# Verify if there's any HTTPS validation at the entry point or config level
rg 'https|http|scheme' .vortex/installer/src/Utils/OptionsResolver.php -B 2 -A 2Repository: drevops/vortex
Length of output: 152
🏁 Script executed:
# Check all places where repo_url is used with requestHeaders
rg 'repo_url.*requestHeaders|requestHeaders.*repo' .vortex/installer/src/Downloader/ -B 2 -A 2Repository: drevops/vortex
Length of output: 152
🏁 Script executed:
# Read Artifact parseUri method completely to confirm HTTP is accepted
sed -n '150,215p' .vortex/installer/src/Downloader/Artifact.phpRepository: drevops/vortex
Length of output: 3241
🏁 Script executed:
# Check if there's any client configuration that enforces HTTPS
rg 'allow_redirects|verify|ssl|scheme' .vortex/installer/src/Downloader/Downloader.php -B 2 -A 2Repository: drevops/vortex
Length of output: 240
🏁 Script executed:
# Check the full downloadArchive method
sed -n '220,240p' .vortex/installer/src/Downloader/RepositoryDownloader.phpRepository: drevops/vortex
Length of output: 778
Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information
Reachability: External · Exploitability: Moderate
Validate HTTPS before attaching the bearer token to remote requests.
requestHeaders() adds GITHUB_TOKEN without validating the repository URL scheme. Artifact::parseUri() accepts both http:// and https:// URLs without enforcing HTTPS. The repository URL scheme is preserved and used in HEAD and GET requests to validateRemoteRepositoryExists(), validateRemoteRefExists(), and downloadArchive(), all of which receive the bearer token via requestHeaders(). An attacker can supply an HTTP repository URL to force the bearer token to transmit over an unencrypted connection. Add a scheme check in requestHeaders() to attach the bearer token only for HTTPS URLs, or reject HTTP URLs in Artifact::parseUri().
🧰 Tools
🪛 PHPMD (2.15.0)
[error] 406-406: Avoid using static access to class '\DrevOps\VortexInstaller\Utils\Env' in method 'requestHeaders'. (undefined)
(StaticAccess)
🤖 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 @.vortex/installer/src/Downloader/RepositoryDownloader.php around lines 403 -
409, The requestHeaders() method must not attach GITHUB_TOKEN when the
repository URL uses HTTP. Validate the repository URL scheme before adding the
Authorization header, allowing the bearer token only for HTTPS requests;
alternatively enforce HTTPS in Artifact::parseUri() so HTTP repository URLs are
rejected before validateRemoteRepositoryExists(), validateRemoteRefExists(), or
downloadArchive() issue requests.
There was a problem hiding this comment.
Partially fixed in 000d8f2.
Adopted: the HTTPS check. requestHeaders() now takes the destination URL and withholds the token unless the request goes over https://, so an http:// repository URL can no longer force the bearer token onto an unencrypted connection. Worth noting this was pre-existing rather than introduced here - all four call sites built the same unconditional header block before this PR deduplicated them, so consolidating them into one function is what made the fix a three-line change instead of four.
Not adopted: the approved-host allowlist. Restricting the token to github.com would break GitHub Enterprise and self-hosted mirrors, which are legitimate VORTEX_INSTALLER_TEMPLATE_REPO targets and are reached over HTTPS. The transport check addresses the cleartext exposure (CWE-319) without assuming the only valid host is github.com. Scoping by host is a policy decision for the maintainer rather than a convergence change.
The PHPMD StaticAccess note is out of scope: Env::get() is static throughout this codebase, and PHPMD is not part of the project's lint chain (PHPCS, PHPStan and Rector are).
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Follow-up commit ---'
git show --stat --oneline 000d8f22
git show --format=fuller --find-renames -- .vortex/installer/src/Downloader/RepositoryDownloader.php 000d8f22
printf '%s\n' '--- Current downloader header implementation and call sites ---'
ast-grep outline .vortex/installer/src/Downloader/RepositoryDownloader.php --items all
rg -n -C 4 'requestHeaders\s*\(|GITHUB_TOKEN|Authorization|downloadArchive|validateRemoteRepositoryExists|validateRemoteRefExists|discoverLatestReleaseRemote' .vortex/installer/src/Downloader/RepositoryDownloader.php
printf '%s\n' '--- Relevant test coverage ---'
rg -n -C 4 --glob '*RepositoryDownloader*Test.php' 'requestHeaders|Authorization|GITHUB_TOKEN|https?://' .vortex/installer/testsLength of output: 1711
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Checked-out revision and remotes ---'
git rev-parse --verify HEAD
git status --short --branch
git remote -v
printf '%s\n' '--- Current request-header helper and request call sites ---'
rg -n -C 5 'requestHeaders\s*\(|GITHUB_TOKEN|Authorization|downloadArchive\s*\(|validateRemoteRepositoryExists\s*\(|validateRemoteRefExists\s*\(|discoverLatestReleaseRemote\s*\(' .vortex/installer/src/Downloader/RepositoryDownloader.php
printf '%s\n' '--- Current repository downloader tests relevant to request headers ---'
rg -n -C 4 --glob '*RepositoryDownloader*Test.php' 'requestHeaders|Authorization|GITHUB_TOKEN|https?://' .vortex/installer/tests || trueLength of output: 37716
@AlexSkrypnyk, the checked-out revision is 09e4c8e178db105909d7a0fb70f8620ac76307ee. It does not contain 000d8f22.
In this checkout, requestHeaders() still has no destination URL parameter. It still adds Authorization whenever GITHUB_TOKEN is set. The four request paths also call it without a destination URL.
I cannot verify the HTTPS guard from this repository state. Please retry after the PR branch includes 000d8f22.
I understand the GitHub Enterprise and self-hosted mirror requirement. I will treat host scoping as a maintainer policy decision. The unresolved item is limited to verifying that HTTP requests omit the bearer token.
🧬 Code Graph Analysis Results
.vortex/installer/src/Utils/Env.php:18-22
public static function get(string $name, mixed $default = NULL): mixed {
$vars = getenv();
return $vars[$name] ?? $default;
}Env::get() reads an environment variable and returns the supplied default when it is unavailable. RepositoryDownloader::requestHeaders() uses it to retrieve GITHUB_TOKEN.
✏️ Learnings added
Learnt from: AlexSkrypnyk
Repo: drevops/vortex PR: 2895
File: .vortex/installer/src/Downloader/RepositoryDownloader.php:403-409
Timestamp: 2026-08-05T10:00:35.962Z
Learning: In the Vortex installer, `VORTEX_INSTALLER_TEMPLATE_REPO` can target GitHub Enterprise and self-hosted repository mirrors over HTTPS. Do not assume `github.com` is the only valid repository host when reviewing `.vortex/installer/src/Downloader/RepositoryDownloader.php`.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
You are interacting with an AI system.
| public static function updateFile(string $file, callable $callback): void { | ||
| $instance = self::fromFile($file); | ||
|
|
||
| if (!$instance instanceof self) { | ||
| throw new \RuntimeException(sprintf('Unable to read a JSON file at "%s".', $file)); | ||
| } | ||
|
|
||
| $callback($instance); | ||
|
|
||
| file_put_contents($file, $instance->getContents()); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- target outline ---'
ast-grep outline .vortex/installer/src/Utils/JsonManipulator.php --match updateFile --view expanded || true
printf '%s\n' '--- target source ---'
cat -n .vortex/installer/src/Utils/JsonManipulator.php | sed -n '1,130p'
printf '%s\n' '--- usages and documentation ---'
rg -n -C 3 'JsonManipulator::updateFile|updateFile\(|`@throws`|file_put_contents' .vortex/installer --glob '*.php' --glob '*.md' | head -n 300
printf '%s\n' '--- repository PHP policy/config ---'
rg -n -C 2 'PHP_VERSION|php-version|phpstan|error_reporting|file_put_contents|atomic|rename\(' . --glob 'composer.json' --glob 'composer.lock' --glob '*.php' --glob '*.yml' --glob '*.yaml' --glob '*.xml' --glob '*.md' | head -n 300Repository: drevops/vortex
Length of output: 30144
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- JsonManipulator tests ---'
fd -i 'JsonManipulator' .vortex/installer/tests .vortex/installer/src || true
rg -n -C 5 'JsonManipulator|updateFile|fromFile|getContents' .vortex/installer/tests .vortex/installer/src | head -n 400
printf '%s\n' '--- all call sites ---'
rg -n -C 6 'JsonManipulator::updateFile' .vortex/installer/src
printf '%s\n' '--- manifest preparation and follow-up processing ---'
rg -n -C 5 'composer\.json|package\.json' .vortex/installer/src/Prompts .vortex/installer/src/Command | head -n 500
printf '%s\n' '--- PHP runtime and relevant function documentation, if available ---'
php -v 2>/dev/null | head -n 2 || true
php --rf file_put_contents 2>/dev/null | head -n 40 || trueRepository: drevops/vortex
Length of output: 50370
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- JsonManipulator tests, remaining methods ---'
cat -n .vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php | sed -n '220,430p'
printf '%s\n' '--- installer package constraints ---'
find .vortex/installer -maxdepth 2 -type f \( -name composer.json -o -name composer.lock \) -print
rg -n -C 3 '"php"|platform|composer-runtime-api' composer.json .vortex/installer/composer.json 2>/dev/null || true
printf '%s\n' '--- updateFile call-site count and paths ---'
rg -l 'JsonManipulator::updateFile' .vortex/installer/src | sort
rg -o 'JsonManipulator::updateFile\([^;]+' .vortex/installer/src --glob '*.php' | sed -E 's/.*updateFile\(([^,]+).*/\1/' | sort | uniq -c
printf '%s\n' '--- PHP function reference from installed runtime, without executing repository code ---'
command -v php || true
php -v 2>&1 | head -n 3 || true
php --rf file_put_contents 2>&1 | head -n 80 || trueRepository: drevops/vortex
Length of output: 3110
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- PHP stream-write behavior probe ---'
php <<'PHP'
<?php
final class ShortWriteStream {
public $context;
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool { return TRUE; }
public function stream_write(string $data): int { return max(1, intdiv(strlen($data), 2)); }
public function stream_close(): void {}
}
final class FailedWriteStream {
public $context;
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool { return TRUE; }
public function stream_write(string $data): int { return 0; }
public function stream_close(): void {}
}
stream_wrapper_register('short-write', ShortWriteStream::class);
stream_wrapper_register('failed-write', FailedWriteStream::class);
foreach ([
'short-write://manifest' => 'abcdef',
'failed-write://manifest' => 'abcdef',
] as $target => $contents) {
$result = `@file_put_contents`($target, $contents);
printf("%s => %s (%s)\n", $target, var_export($result, TRUE), gettype($result));
}
PHP
printf '%s\n' '--- existing file-replacement helpers ---'
rg -n -C 5 'rename\(|tempnam\(|tmpfile\(|file_put_contents' .vortex/installer/src/Utils .vortex/installer/src --glob '*.php' | head -n 300Repository: drevops/vortex
Length of output: 7070
🏁 Script executed:
#!/bin/bash
set -eu
php <<'PHP'
<?php
final class PartialFailureStream {
public $context;
private int $calls = 0;
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool { return TRUE; }
public function stream_write(string $data): int {
$this->calls++;
return $this->calls === 1 ? intdiv(strlen($data), 2) : 0;
}
public function stream_close(): void {}
}
stream_wrapper_register('partial-failure', PartialFailureStream::class);
$result = `@file_put_contents`('partial-failure://manifest', 'abcdef');
printf("partial failure => %s (%s)\n", var_export($result, TRUE), gettype($result));
PHPRepository: drevops/vortex
Length of output: 187
Handle failed JSON writes atomically.
file_put_contents() can return false or an incomplete byte count, but updateFile() ignores the result and returns normally. Write the contents to a temporary file, validate the byte count, replace the target with rename(), and throw RuntimeException on failure. Update the @throws documentation.
🧰 Tools
🪛 PHPMD (2.15.0)
[error] 58-58: Missing class import via use statement (line '58', column '17'). (undefined)
(MissingImport)
🤖 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 @.vortex/installer/src/Utils/JsonManipulator.php around lines 54 - 64, Update
JsonManipulator::updateFile to write getContents() to a temporary file, verify
file_put_contents() succeeds with the complete expected byte count, then
atomically replace the target using rename(); clean up and throw
RuntimeException on any failure. Update the method’s `@throws` documentation to
include write or replacement failures.
There was a problem hiding this comment.
Partially fixed in 000d8f2.
Adopted: the unchecked return. updateFile() now compares file_put_contents() against the expected byte count and throws RuntimeException on a short or failed write, and the @throws documentation covers it. This is worth doing precisely because updateFile() is now the single write path for all eight composer.json/package.json call sites, so one guard covers all of them.
Not adopted: the temp-file-plus-rename() dance. Atomic replacement protects a concurrent reader from seeing a torn file. There is no such reader here: updateFile() only ever writes into the installer's own private temporary directory, before anything is copied to the destination, and the process is single-threaded. The added failure modes of a two-step write (a stranded temp file, rename() across filesystems) are real, while the race it guards against is not reachable. Failing loudly on a short write gets the actual benefit without that cost.
The PHPMD MissingImport note is out of scope - see the reply on AbstractRunner.php.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- target method ---'
ast-grep outline .vortex/installer/src/Utils/JsonManipulator.php --match updateFile --view expanded
sed -n '40,90p' .vortex/installer/src/Utils/JsonManipulator.php
printf '%s\n' '--- claimed commit ---'
git show --stat --oneline 000d8f22
git show --format=fuller --find-renames -- .vortex/installer/src/Utils/JsonManipulator.php 000d8f22
printf '%s\n' '--- current updateFile call sites ---'
rg -n -C 5 'JsonManipulator::updateFile\s*\(' .vortex/installer/src --glob '*.php'
printf '%s\n' '--- installer temporary-directory and destination flow ---'
rg -n -C 4 'temp(dir|nam)|sys_get_temp_dir|TemporaryDirectory|copy.*destination|destination.*copy|runProcessors' .vortex/installer/src --glob '*.php' | head -n 300Length of output: 2602
🏁 Script executed:
#!/bin/bash
set -euo pipefail
repo="drevops/vortex"
pr="2895"
commit="000d8f22"
path=".vortex/installer/src/Utils/JsonManipulator.php"
printf '%s\n' '--- PR head ---'
gh api "repos/${repo}/pulls/${pr}" --jq '.head.sha'
printf '%s\n' '--- cited commit ---'
gh api "repos/${repo}/commits/${commit}" --jq '{sha: .sha, message: .commit.message, parents: [.parents[].sha]}'
printf '%s\n' '--- target file at cited commit ---'
gh api "repos/${repo}/contents/${path}?ref=${commit}" --jq '.content' | tr -d '\n' | base64 --decode | sed -n '40,90p'
printf '%s\n' '--- commit inclusion in PR ---'
gh api "repos/${repo}/compare/${commit}...$(gh api "repos/${repo}/pulls/${pr}" --jq '.head.sha')" --jq '{status: .status, ahead_by: .ahead_by, behind_by: .behind_by}'Length of output: 1335
@AlexSkrypnyk, I agree that atomic replacement is not required for the stated private, single-process installer workflow.
I could not verify the adopted short-write guard. GitHub reports that 000d8f22 does not exist in drevops/vortex, and PR #2895 currently points to 09e4c8e178db105909d7a0fb70f8620ac76307ee. The available PR snapshot still ignores the file_put_contents() result.
Please retry after the commit is available on the PR branch. I will keep the finding unresolved until then.
🐇🔍
✏️ Learnings added
Learnt from: AlexSkrypnyk
Repo: drevops/vortex PR: 2895
File: .vortex/installer/src/Utils/JsonManipulator.php:54-64
Timestamp: 2026-08-05T10:00:48.081Z
Learning: In the Vortex installer, `DrevOps\VortexInstaller\Utils\JsonManipulator::updateFile()` writes only installer-controlled files in a private temporary directory before files are copied to the destination. The installer process is single-threaded and has no concurrent reader for these files, so atomic temp-file replacement is not required for this write path. The method must still validate the `file_put_contents()` result and fail on a short or failed write.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
You are interacting with an AI system.
|
|
||
| public function testWriteValueDotenv(): void { | ||
| $fixture_dir = __DIR__ . '/Fixtures/env'; | ||
| $fixture_dir = dirname(__DIR__) . '/Fixtures/env'; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Use the correct fixture directory.
Line 127 resolves from .vortex/installer/tests/Unit/Utils to .vortex/installer/tests/Unit/Fixtures/env. The fixture root is .vortex/installer/tests/Fixtures, so copy() cannot load _baseline/.env and testWriteValueDotenv() fails.
Proposed fix
- $fixture_dir = dirname(__DIR__) . '/Fixtures/env';
+ $fixture_dir = dirname(__DIR__, 2) . '/Fixtures/env';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $fixture_dir = dirname(__DIR__) . '/Fixtures/env'; | |
| $fixture_dir = dirname(__DIR__, 2) . '/Fixtures/env'; |
🤖 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 @.vortex/installer/tests/Unit/Utils/EnvTest.php at line 127, Update the
$fixture_dir assignment in testWriteValueDotenv() to resolve from the Utils test
directory to the sibling tests/Fixtures/env directory, so copy() can locate the
_baseline/.env fixture.
There was a problem hiding this comment.
Not adopting - the current path is correct and the suggested change would break the test.
This suite's fixture root is tests/Unit/Fixtures, not tests/Fixtures. The file lives at tests/Unit/Utils/EnvTest.php, so dirname(__DIR__) resolves to tests/Unit, giving tests/Unit/Fixtures/env - which is exactly where _baseline/.env is:
.vortex/installer/tests/Unit/Fixtures/env/_baseline/.env
That is also why the line changed in the first place: the file moved from tests/Unit/ into tests/Unit/Utils/ in this PR, so the original __DIR__ . '/Fixtures/env' had to gain one dirname() to keep pointing at the same directory. dirname(__DIR__, 2) would resolve to tests/Fixtures/env, which does not exist - that path holds the installer's handler_process snapshot fixtures, a different system.
testWriteValueDotenv() passes on this branch, as does the full 1606-test suite.
|
📖 Documentation preview for this pull request has been deployed to Netlify: https://6a730d4b1a7f4424c309363e--vortex-docs.netlify.app This preview is rebuilt on every commit and is not the production documentation site. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
2 similar comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2895 +/- ##
==========================================
- Coverage 87.20% 86.63% -0.58%
==========================================
Files 100 94 -6
Lines 4816 4661 -155
Branches 47 3 -44
==========================================
- Hits 4200 4038 -162
- Misses 616 623 +7 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…e badge mask, resolved the behavior context through its document.
|
Code coverage (threshold: 90%) Per-class coverage |
This comment has been minimized.
This comment has been minimized.
2 similar comments
This comment has been minimized.
This comment has been minimized.
|
Code coverage (threshold: 90%) Per-class coverage |
Summary
Second pass of the codebase convergence audit, continuing from #2890. Every inconsistency class here was mapped by reading the first-party source in full, given a single canonical form, and reviewed and approved before being applied. The changes are behaviour-preserving except where explicitly called out below.
One commit per inconsistency class, plus snapshot regenerations. Gates run per class: PHPCS + PHPStan + Rector, the 1606-test installer suite, the 309-test BATS suite, and ShellCheck over every shipped script.
Changes
Installer API shapes
Laravel\Promptscalls inPromptManagernow route through theTuifacade that exists for them.form()andspin()stay direct - they are framework orchestration primitives, not output channels, the same boundary that keepsOutputInterfaceinside Symfony Command classes.PromptType::promptFunction()returns a first-class callable instead of building'\Laravel\Prompts\' . $valueas a string, which removed a@phpstan-ignore.getOutput(as_array: TRUE)with anis_arrayguard.LoggableInterfacewas replaced byLogger/LoggerAwareInterfaceandLogger/LoggerAwareTrait, matching the three existingRunner/*Aware*triplets. The runner'senableLog()/disableLog()pass-throughs were dropped in favour ofgetLogger()->enable()/disable(), which is the same API one level down.composer.json manipulation
JsonManipulator::updateFile($file, callable)is now the single read-guard-mutate-write shape. It replaced five regex-on-raw-JSON removals, thejson_decode/json_encodesurgery inInternal, two unguardedfromFile()calls and three silent-skip guards. Discovery sites keep the nullsafe form - they read the user's destination directory, wherecomposer.jsonmay legitimately be absent; onlyprocess()sites throw.Deduplication
GitHub auth headers (x4), Guzzle client defaults (x2), the exit-code range guard (x4), the handler-lookup guard (x3 plus ten unguarded accesses), the headless
PromptManagerbootstrap (x2), the is-profile snippet (x2), the ANSI regex (x2), theTaskOutputdim map (x2) and module-prefix resolution (x2). Each caller keeps its own configuration where that configuration is the behaviour - the two module-prefix callers still search their own 12- and 4-entry location lists.Structure
PromptManagerexecutes them.File::runDirectoryTasks()moved out of theInternalhandler, where it violated that rule, intoPromptManager::runProcessors()after the handler loop.tests/Unit/Utils/,tests/Unit/Task/,tests/{Unit,Functional}/Prompts/Handlers/.dataProvider*sits immediately after the test it feeds..vortex/installer/CLAUDE.mddocumented the opposite order and was corrected.Shipped template
.ignorecontentrule for a directory that no longer exists.CI_GITLEAKSandCODE_COVERAGE_PROVIDER_CODECOVbecameTOOL_GITLEAKSandTOOL_CODECOV, joining the fourteen fences already on that prefix. Renamed in every template fence and every installerremoveTokencall together; installed output is unchanged, which is the correct signal.ci-runnerpin, andset -xremoved from the three "Install Ahoy" steps where it was unconditional.data-counter-*attributes and itslocalStoragekey are namespaced to the module that owns them.Behaviour changes
Five, each in its own commit:
.lagoon.ymltested[ "VORTEX_PROVISION_TYPE" = "profile" ]without a$, so the profile branch had never run for any consumer.close-pull-request.ymlmappedLAGOON_WEBHOOK_ENDPOINTto itself viaenv., so the shell default always won and a configured endpoint was ignored. Both activate paths that were silently dead.ProcessRunneroutput was truncated.run()did$this->output = $bufferinside the Symfony Process callback, which fires once per read chunk - sogetOutput()only ever returned the last chunk. Any command producing more than one chunk lost its earlier output;BuildCommand::getLocalDevUrl()runsdocker compose config --format jsonthrough it. Now accumulates. This one was not on the approved list: it was found while convertingGit::getTrackedFiles()to the runner, and converting on top of the bug would have introduced a truncation regression.Drupal.behaviors.your_site_themeis now camelCase. The key doubles as an installer substitution token, and the installer only substitutedyour_site_themeandYourSiteTheme, so this required adding'yourSiteTheme' => Converter::camel($v)to the theme handler's map. Verified in the regenerated fixture: installed sites getDrupal.behaviors.starWars.vortex-task-copy-db-acquiachecked forcurlbut callsphp;vortex-deploy-lagoonusedjqunchecked. Both now fail with a clear message instead of a shell error.GITHUB_TOKENis withheld over plain HTTP.requestHeaders()attached the bearer token to every request regardless of scheme, andArtifactaccepts arbitraryhttp://repository URLs, so a plain-HTTP template repo could pull the token onto an unencrypted connection. It now takes the destination and only authoriseshttps://. This was pre-existing behaviour at all four call sites; consolidating them into one function is what made it a three-line fix. Host allowlisting was deliberately not added - it would break GitHub Enterprise and self-hosted mirrors, which are legitimate targets reached over HTTPS.Fixtures
One fixture churned for a reason worth stating: removing
Internal's whole-filejson_encode(JSON_PRETTY_PRINT)round-trip meansJsonManipulator's own compact array formatting now survives, sostarter_drupal_cms_profile/composer.jsonwrites"require": ["vendor/drupal/cms/composer.json"]on one line. Valid JSON, one line, one scenario out of 149. The re-encode was an incidental normaliser, not a designed one.The Vortex badge in
README.mdis now masked in fixtures like every other version stamp. Without it, a tagged checkout rewrites the README in all ~150 scenarios. The mask stops at the badge URL delimiter rather than the first hyphen, so refs containing hyphens normalise correctly.Review notes
Two findings from review were real defects in code this PR introduced, and are worth calling out because both were invisible to the gates:
[^-]+, which stops at the first hyphen - so it silently failed on any ref containing one, including this branch. Fixtures still matched, because the fixture and the generated output shared the same unmasked value.context.querySelector('body'). That searches descendants only, so when Drupal passesdocument.bodyas the context after an AJAX insert, it returned null and the behavior did nothing. It now resolves throughcontext.ownerDocument.body, which is correct for the document, for the body itself, and for any inserted element.Four further suggestions were declined with reasons on the PR: applying the agent-facing "one simple Bash command" instruction to CI YAML and shipped shell scripts (it governs agent tool calls, and applying it would have made one of six identical
find | xargscopies and two of ~40 tooling scripts diverge), importingRuntimeExceptionin a single file where ~40 sites use it fully qualified, and a fixture-path "fix" that would have broken a passing test - this suite's fixture root istests/Unit/Fixtures, nottests/Fixtures.Not applied
Items from the plan that were investigated and deliberately left alone:
DatabaseFetchSource/MigrationFetchSource,DatabaseImage/MigrationImage).PromptManager::initHandlers()scans theHandlers/directory and runsnew $class()on everything implementingHandlerInterface, excluding onlyAbstractHandlerby name - an abstract intermediate placed there passesclass_exists()and would be instantiated, a fatal error. The twins also differ behaviourally rather than cosmetically: the Migration variants carry a load-bearing!empty($this->response)guard,DatabaseFetchSourcehas an extraNONEoption, and their dependency predicates encode different semantics.web/sites/default/includes/- the trusted-host convergence, the settings guard ordering, and the approved=== '1'loosening. Blocked rather than declined: the repo root has novendor/, soEnvironmentSettingsTest(16 scenarios) andSwitchableSettingsTestcannot run locally, andahoy test-unitneeds Docker. These would have been pushed blind, withEnvironmentSettingsTest's fail-fast masking making a partial fix look green. Worth doing next with the root dependencies installed - the trusted-host item is also a real bug fix, since two of the three constructions build a single alternation^a\.com|b\.com$, which by precedence is unanchored.SHELL_VERBOSITYsweep.ToolingBootstrapTestdeliberately uses0and documents why on itsOUTPUT_ENVconstant; it asserts on Composer output the sweep would suppress. TheFALSEsites are a third, also deliberate, intent.${DRUPAL_THEME}.Remaining classes and their decided canonical forms are recorded in the working notes, along with the exact assertion that changes for the settings work.
Before / After