Conversation
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…nvoicePlane into fix/niels-1-sales-by-year
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Removed specific branch and path triggers for pushes.
Removed specific push triggers for branches and tags.
Updated workflow triggers for Docker image build.
Updated workflow trigger to allow manual dispatch.
…in permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…in permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…in permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…in permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…in permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…n permissions (#1389) * fixed error Removed item discount display from invoice template. * Potential fix for code scanning alert no. 5: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
) * fixed error Removed item discount display from invoice template. * Potential fix for code scanning alert no. 8: Unsafe jQuery plugin Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.github/workflows/docker-phpmyadmin.yml (1)
19-22: The phpMyAdmin version test command will fail and should be replaced.The
phpmyadmin --versioncommand does not exist in the official phpMyAdmin Docker image. The container has no CLI tool with that name, so this step will exit with "command not found" and break the workflow.Replace with a test that verifies the web server is functional:
- name: Build PHPMyAdmin Image run: | docker build -t phpmyadmin-test ./resources/docker/phpmyadmin - docker run --rm phpmyadmin-test phpmyadmin --version # Test version + docker run --rm phpmyadmin-test apache2ctl configtest # Verify Apache config is validAlternatively, verify PHP is functional with
docker run --rm phpmyadmin-test php -v..github/workflows/test-frontend.yml (1)
14-15: Update Node.js test matrix to currently supported LTS versions.Node.js 14 and 16 have reached end-of-life. Node.js 18 also reached end-of-life on April 30, 2025. Update the matrix to currently supported LTS versions (22 and 24).
node: ['14', '16', '18'] + node: ['22', '24']
🤖 Fix all issues with AI agents
In @.github/workflows/composer-update.yml:
- Around line 50-58: The current workflow step "Run Composer audit" uses a file
size check on audit-report.json which is a false positive because composer audit
emits {"advisories": {}} even when no issues exist; change the check to parse
the JSON and inspect the "advisories" object instead (for example, use jq or a
short python snippet to test if .advisories is empty) and set the GITHUB_OUTPUT
variable "vulnerabilities_found" to true only when .advisories contains entries;
update the step with the id "audit" to replace the -s audit-report.json
conditional with a JSON-aware check against .advisories.
In @.github/workflows/phpstan.yml:
- Around line 65-66: The "Comment on PR (if available)" step has an unreachable
condition (github.event_name == 'pull_request') because this workflow only
triggers on workflow_dispatch; update the workflow trigger or the step
condition: either add pull_request to the top-level on: triggers in this
phpstan.yml so the condition can be true, or change/remove the step's if: guard
and instead use a trigger/condition that matches how you intend to run it (e.g.,
use workflow_run or check github.event_name == 'workflow_run' / pass a PR
context from a caller) so the step can actually execute; locate the step by its
name "Comment on PR (if available)" to apply the change.
In @.github/workflows/phpunit.yml:
- Around line 37-49: The workflow contains Laravel-specific steps (php artisan
key:generate, php artisan test) and references .env.testing.example which are
not applicable to this CodeIgniter 3.3 project; remove or replace those lines
and instead run PHPUnit directly (e.g., vendor/bin/phpunit or phpunit) with the
repository's phpunit.xml configuration, ensure tests use the project's
configuration file (ipconfig.php) by copying or linking it into the test
environment before running tests, and delete any references to artisan commands
(php artisan key:generate, php artisan migrate, php artisan db:seed) so the CI
either runs CodeIgniter-compatible PHPUnit steps or the workflow is removed if
tests are not intended to run.
In @.github/workflows/pint.yml:
- Around line 93-101: The "Commit and Push Changes" workflow step currently
always runs git push and will fail for forked PRs because GITHUB_TOKEN is
read-only; update the step's if condition (the step named "Commit and Push
Changes" that already checks steps.verify_diff.outputs.has_changes) to also
require that the run is not a forked pull request by validating same-repo or
non-pull_request contexts (for example, require
github.event.pull_request.head.repo.full_name == github.repository or require
github.event_name != 'pull_request' / github.ref startsWith 'refs/heads/' for
manual/branch runs) so the push only runs when the action has push permission.
In @.github/workflows/quickstart.yml:
- Around line 64-65: The workflow currently runs the "Run database migrations"
step which invokes `php artisan migrate --force` unconditionally and will fail
without a DB; either remove or comment out that step (the step named "Run
database migrations" invoking `php artisan migrate --force`) if smoke tests
don't need DB, or alternatively ensure tests use an in-memory SQLite by updating
`.env.testing.example` to set DB_CONNECTION=sqlite and DB_DATABASE=:memory:, or
re-enable the MySQL service in the workflow so migrations can run; pick one
approach and apply it consistently so the `php artisan migrate --force` step no
longer fails.
In @.github/workflows/release.yml:
- Around line 425-439: The workflow step "Create GitHub Draft Pre-Release"
currently uses softprops/action-gh-release@v1 which is deprecated; update the
action reference to the v2 release (e.g., softprops/action-gh-release@v2 or
`@v2.5.0`) in the step that defines tag_name/name/body_path/files/draft/prerelease
so the newer action is used; after changing the version, verify input names
(tag_name, name, body_path, files, draft, prerelease) still match v2 docs and
adjust any differences in parameter names or behavior for the step if needed.
In @.github/workflows/test-php.yml:
- Around line 3-4: The workflow trigger was changed to only workflow_dispatch
which disables automatic PR testing; restore automatic runs by adding the
pull_request trigger back alongside workflow_dispatch (e.g., include a top-level
triggers block that contains both pull_request and workflow_dispatch) so tests
run on PR open/update while still allowing manual dispatch; update the
workflow's on: stanza to include both triggers.
In @.github/workflows/yarn-update.yml:
- Around line 107-131: The security-only upgrade step currently uses the command
referenced as yarn upgrade "$pkg" --latest which can bump across major versions;
change it to use yarn upgrade "$pkg" (remove --latest) so upgrades respect
existing semver ranges in package.json and avoid breaking major-version jumps
for the packages gathered in VULNERABLE_PACKAGES; ensure the loop that echoes
and calls yarn upgrade uses the updated command.
In `@application/helpers/file_security_helper.php`:
- Line 87: The code uses mb_rtrim (e.g., when building $realBaseWithSep in
file_security_helper.php) which exists only in PHP 8.4+, causing fatal errors on
the declared PHP 8.1+ target; replace mb_rtrim with the multibyte-safe rtrim (or
call rtrim with a UTF-8-aware normalization step) so the code runs on PHP
8.1–8.3, and apply the same replacement to other occurrences (index.php lines
around 209/248/275, phpmailer_helper.php usage, pdf_helper.php,
dropzone_helper.php) or alternatively update the project minimum PHP requirement
and Setup.php PHP version check to 8.4.0 to match mb_rtrim usage.
In `@application/helpers/mailer_helper.php`:
- Around line 248-260: Replace the brittle str_starts_with() origin check with a
same-origin comparison that parses and compares URL components: call parse_url()
on both base_url() and $_SERVER['HTTP_REFERER'], ensure scheme, host and port
(normalize default ports) match before assigning $redirect = $referer, and treat
root-relative referers (starting with "/") as safe; otherwise set $redirect =
base_url(); apply the same fix where str_starts_with is used (mailer_helper.php
logic around $_SERVER['HTTP_REFERER'] and the Sessions controller check) and
keep the final redirect($redirect) behavior unchanged.
In `@application/modules/clients/Enums/ClientTitleEnum.php`:
- Around line 20-31: The tryFrom method mis-handles array_search results and
returns the index instead of the matched string: change the truthy check to a
strict !== false (i.e. if ($searchResult !== false)) and set the returned
object's value to the actual string from getValues() (e.g. $returnObject->value
= $values[$searchResult] or simply $value) so lookups like 'mr' (index 0)
succeed and callers get the string value; keep the StdClass return pattern and
leave other behavior untouched.
🧹 Nitpick comments (5)
application/libraries/MY_Form_validation.php (1)
36-36: Consider using explicitiffor conditional assignment.Using short-circuit
&&for side effects (assignment) works correctly but is generally less readable than an explicitifstatement. The intent is clearer with:if (is_object($config)) { $this->CI = &$config; }This is a minor stylistic preference—the current code is functionally correct.
application/helpers/e-invoice_helper.php (1)
147-161: Consider explicitreturn null;for clarity.In PHP,
return;andreturn null;are functionally equivalent, so this isn't a behavioral change. However, given the function has multiple return paths (explicit null on validation failure, string on success, implicit null if file doesn't exist), an explicitreturn null;would improve readability and make the intent clearer.🔧 Suggested improvement
if ( ! is_valid_xml_config_id($xml_id)) { log_message('error', trans('log_invalid_xml_config_id_get_full_name') . ': ' . $xml_id); - return; + return null; }.github/workflows/docker-publish.yml (1)
25-26: Conditionals checking forpull_requestevent are now redundant.With the trigger changed to
workflow_dispatch, the conditiongithub.event_name != 'pull_request'will always be true. These checks are harmless but could be removed for clarity if desired.Also applies to: 35-37, 56-56, 64-64
.github/workflows/test-frontend.yml (1)
19-22: Consider updatingsetup-nodeaction to v4.The
actions/setup-node@v3action could be updated tov4for consistency with other checkout actions in the repository using@v4.- name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} cache: 'yarn'.github/workflows/quickstart.yml (1)
59-62: Inconsistent.envsetup compared to PHPUnit workflow.This copies
.env.testing.exampleto.envand runskey:generatewithout--env, whilephpunit.ymlcopies to.env.testingand uses--env=testing. This inconsistency could cause confusion or unexpected behavior.Consider aligning the approach:
♻️ Align with PHPUnit workflow pattern
- name: Prepare Laravel environment run: | - cp .env.testing.example .env - php artisan key:generate + cp .env.testing.example .env.testing + php artisan key:generate --env=testing
| - name: Prepare Laravel environment | ||
| run: | | ||
| cp .env.testing.example .env.testing | ||
| php artisan key:generate --env=testing | ||
|
|
||
| #- name: Run Laravel migrations | ||
| # run: php artisan migrate --force --env=testing | ||
|
|
||
| #- name: Run Laravel seeds (optional) | ||
| # run: php artisan db:seed --force --env=testing | ||
|
|
||
| - name: Run PHPUnit | ||
| run: php artisan test --env=testing |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if .env.testing.example exists and verify its database configuration
if [ -f ".env.testing.example" ]; then
echo "=== .env.testing.example exists ==="
echo ""
echo "=== Full contents of .env.testing.example ==="
cat .env.testing.example
else
echo "ERROR: .env.testing.example not found"
exit 1
fiRepository: InvoicePlane/InvoicePlane
Length of output: 106
🏁 Script executed:
#!/bin/bash
# Search for all .env files and .env.* files in the repository
echo "=== Searching for .env files ==="
find . -type f -name ".env*" | head -20
echo ""
echo "=== Checking if any example files exist ==="
find . -type f -name "*.example" | grep -E "(env|config)" | head -20
echo ""
echo "=== Checking root directory contents ==="
ls -la | grep -E "^-.*\.env" || echo "No .env files in root"Repository: InvoicePlane/InvoicePlane
Length of output: 233
🏁 Script executed:
#!/bin/bash
# Check the project's actual configuration setup
echo "=== ipconfig.php.example contents (first 50 lines) ==="
head -50 ipconfig.php.example
echo ""
echo "=== Check if Laravel env() is used in codebase ==="
rg "\.env" --type php -l | head -10
echo ""
echo "=== Check Laravel bootstrap files ==="
find . -path "*/bootstrap/*.php" -type f | head -5Repository: InvoicePlane/InvoicePlane
Length of output: 2426
🏁 Script executed:
#!/bin/bash
# Check composer.json to identify the actual framework
echo "=== composer.json dependencies (if exists) ==="
if [ -f "composer.json" ]; then
cat composer.json | jq '.require // {}' 2>/dev/null | head -20
else
echo "No composer.json found"
fi
echo ""
echo "=== Check for CodeIgniter or Laravel files ==="
echo "CodeIgniter index.php:"
ls -la index.php 2>/dev/null || echo "Not found"
echo ""
echo "Laravel artisan:"
ls -la artisan 2>/dev/null || echo "Not found"
echo ""
echo "=== Check project structure ==="
ls -la | grep -E "^d" | head -10Repository: InvoicePlane/InvoicePlane
Length of output: 1197
This workflow is incompatible with the CodeIgniter framework used by this project.
The workflow uses Laravel-specific commands (php artisan key:generate, php artisan test) that do not exist in CodeIgniter 3.3. Additionally, it references a .env.testing.example file that neither exists nor is used by this project, which uses ipconfig.php for configuration instead. The entire workflow needs to be redesigned for CodeIgniter or removed if testing is not intended to be part of the build pipeline.
🤖 Prompt for AI Agents
In @.github/workflows/phpunit.yml around lines 37 - 49, The workflow contains
Laravel-specific steps (php artisan key:generate, php artisan test) and
references .env.testing.example which are not applicable to this CodeIgniter 3.3
project; remove or replace those lines and instead run PHPUnit directly (e.g.,
vendor/bin/phpunit or phpunit) with the repository's phpunit.xml configuration,
ensure tests use the project's configuration file (ipconfig.php) by copying or
linking it into the test environment before running tests, and delete any
references to artisan commands (php artisan key:generate, php artisan migrate,
php artisan db:seed) so the CI either runs CodeIgniter-compatible PHPUnit steps
or the workflow is removed if tests are not intended to run.
nielsdrost7
left a comment
There was a problem hiding this comment.
@copilot
In @.github/workflows/composer-update.yml:
- Around line 50-58: The current workflow step "Run Composer audit" uses a file
size check on audit-report.json which is a false positive because composer audit
emits {"advisories": {}} even when no issues exist; change the check to parse
the JSON and inspect the "advisories" object instead (for example, use jq or a
short python snippet to test if .advisories is empty) and set the GITHUB_OUTPUT
variable "vulnerabilities_found" to true only when .advisories contains entries;
update the step with the id "audit" to replace the -s audit-report.json
conditional with a JSON-aware check against .advisories.
In @.github/workflows/phpstan.yml:
- Around line 65-66: The "Comment on PR (if available)" step has an unreachable
condition (github.event_name == 'pull_request') because this workflow only
triggers on workflow_dispatch; update the workflow trigger or the step
condition: either add pull_request to the top-level on: triggers in this
phpstan.yml so the condition can be true, or change/remove the step's if: guard
and instead use a trigger/condition that matches how you intend to run it (e.g.,
use workflow_run or check github.event_name == 'workflow_run' / pass a PR
context from a caller) so the step can actually execute; locate the step by its
name "Comment on PR (if available)" to apply the change.
In @.github/workflows/phpunit.yml:
- Around line 37-49: The workflow contains Laravel-specific steps (php artisan
key:generate, php artisan test) and references .env.testing.example which are
not applicable to this CodeIgniter 3.3 project; remove or replace those lines
and instead run PHPUnit directly (e.g., vendor/bin/phpunit or phpunit) with the
repository's phpunit.xml configuration, ensure tests use the project's
configuration file (ipconfig.php) by copying or linking it into the test
environment before running tests, and delete any references to artisan commands
(php artisan key:generate, php artisan migrate, php artisan db:seed) so the CI
either runs CodeIgniter-compatible PHPUnit steps or the workflow is removed if
tests are not intended to run.
In @.github/workflows/pint.yml:
- Around line 93-101: The "Commit and Push Changes" workflow step currently
always runs git push and will fail for forked PRs because GITHUB_TOKEN is
read-only; update the step's if condition (the step named "Commit and Push
Changes" that already checks steps.verify_diff.outputs.has_changes) to also
require that the run is not a forked pull request by validating same-repo or
non-pull_request contexts (for example, require
github.event.pull_request.head.repo.full_name == github.repository or require
github.event_name != 'pull_request' / github.ref startsWith 'refs/heads/' for
manual/branch runs) so the push only runs when the action has push permission.
In @.github/workflows/quickstart.yml:
- Around line 64-65: The workflow currently runs the "Run database migrations"
step which invokesphp artisan migrate --forceunconditionally and will fail
without a DB; either remove or comment out that step (the step named "Run
database migrations" invokingphp artisan migrate --force) if smoke tests
don't need DB, or alternatively ensure tests use an in-memory SQLite by updating
.env.testing.exampleto set DB_CONNECTION=sqlite and DB_DATABASE=:memory:, or
re-enable the MySQL service in the workflow so migrations can run; pick one
approach and apply it consistently so thephp artisan migrate --forcestep no
longer fails.
In @.github/workflows/release.yml:
- Around line 425-439: The workflow step "Create GitHub Draft Pre-Release"
currently uses softprops/action-gh-release@v1 which is deprecated; update the
action reference to the v2 release (e.g., softprops/action-gh-release@v2 or
@v2.5.0) in the step that defines tag_name/name/body_path/files/draft/prerelease
so the newer action is used; after changing the version, verify input names
(tag_name, name, body_path, files, draft, prerelease) still match v2 docs and
adjust any differences in parameter names or behavior for the step if needed.
In @.github/workflows/test-php.yml:
- Around line 3-4: The workflow trigger was changed to only workflow_dispatch
which disables automatic PR testing; restore automatic runs by adding the
pull_request trigger back alongside workflow_dispatch (e.g., include a top-level
triggers block that contains both pull_request and workflow_dispatch) so tests
run on PR open/update while still allowing manual dispatch; update the
workflow's on: stanza to include both triggers.
In @.github/workflows/yarn-update.yml:
- Around line 107-131: The security-only upgrade step currently uses the command
referenced as yarn upgrade "$pkg" --latest which can bump across major versions;
change it to use yarn upgrade "$pkg" (remove --latest) so upgrades respect
existing semver ranges in package.json and avoid breaking major-version jumps
for the packages gathered in VULNERABLE_PACKAGES; ensure the loop that echoes
and calls yarn upgrade uses the updated command.
In @application/helpers/file_security_helper.php:
- Line 87: The code uses mb_rtrim (e.g., when building $realBaseWithSep in
file_security_helper.php) which exists only in PHP 8.4+, causing fatal errors on
the declared PHP 8.1+ target; replace mb_rtrim with the multibyte-safe rtrim (or
call rtrim with a UTF-8-aware normalization step) so the code runs on PHP
8.1–8.3, and apply the same replacement to other occurrences (index.php lines
around 209/248/275, phpmailer_helper.php usage, pdf_helper.php,
dropzone_helper.php) or alternatively update the project minimum PHP requirement
and Setup.php PHP version check to 8.4.0 to match mb_rtrim usage.
In @application/helpers/mailer_helper.php:
- Around line 248-260: Replace the brittle str_starts_with() origin check with a
same-origin comparison that parses and compares URL components: call parse_url()
on both base_url() and $_SERVER['HTTP_REFERER'], ensure scheme, host and port
(normalize default ports) match before assigning $redirect = $referer, and treat
root-relative referers (starting with "/") as safe; otherwise set $redirect =
base_url(); apply the same fix where str_starts_with is used (mailer_helper.php
logic around $_SERVER['HTTP_REFERER'] and the Sessions controller check) and
keep the final redirect($redirect) behavior unchanged.
In @application/modules/clients/Enums/ClientTitleEnum.php:
- Around line 20-31: The tryFrom method mis-handles array_search results and
returns the index instead of the matched string: change the truthy check to a
strict !== false (i.e. if ($searchResult !== false)) and set the returned
object's value to the actual string from getValues() (e.g. $returnObject->value
= $values[$searchResult] or simply $value) so lookups like 'mr' (index 0)
succeed and callers get the string value; keep the StdClass return pattern and
leave other behavior untouched.
|
@nielsdrost7 I've opened a new pull request, #1392, to work on those changes. Once the pull request is ready, I'll request review from you. |
…1392) * Initial plan * Fix PHP 8.1+ compatibility: Replace mb_rtrim with rtrim and improve origin validation Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> * Fix workflow issues: JSON parsing, triggers, Laravel removal, fork checks Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> * Address code review feedback: Fix ClientTitleEnum value assignment and improve JSON validation Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com>
…1392) * Initial plan * Fix PHP 8.1+ compatibility: Replace mb_rtrim with rtrim and improve origin validation Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> * Fix workflow issues: JSON parsing, triggers, Laravel removal, fork checks Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> * Address code review feedback: Fix ClientTitleEnum value assignment and improve JSON validation Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
application/modules/mailer/helpers/phpmailer_helper.php (1)
140-146: Fixrtrim()character-mask behavior on line 144.
rtrim($attachment_path, '.pdf')removes characters from the right based on a character mask (.,p,d,f), not a suffix. Filenames ending withp,d, orflose extra characters:setup.pdf→setu.xml,staff.pdf→sta.xml. This breaks XML file detection, attachment, and deletion. Use suffix-safe removal:- $xml_file = rtrim($attachment_path, '.pdf') . '.xml'; + $xml_file = preg_replace('/\.pdf$/i', '', $attachment_path) . '.xml';
🤖 Fix all issues with AI agents
In @.github/workflows/composer-update.yml:
- Around line 93-99: The "Run smoke tests" workflow step currently invokes
Laravel-specific commands ("php artisan key:generate" and "php artisan test")
which are not applicable to CodeIgniter 3; update that step (named "Run smoke
tests") to remove those artisan commands and replace them with CI3-appropriate
checks such as running PHPUnit directly (e.g., "php vendor/bin/phpunit
--configuration phpunit.smoke.xml --env=testing") or a lightweight CI3 bootstrap
check (e.g., a script that loads index.php or runs a CI3 smoke test suite); also
remove or reconsider "continue-on-error: true" so the step reports meaningful
failures.
In @.github/workflows/phpstan.yml:
- Around line 33-45: The workflow step "Parse and format PHPStan results" (id:
parse-results) references a missing script
`.github/scripts/parse-phpstan-results.php`; fix by either adding that script to
the repository with the expected name and executable contents or update the step
to call the correct existing script path/filename that parses phpstan.json;
ensure the replacement path is committed and executable and that the step still
writes output to phpstan-report.md and returns appropriate exit codes on
failure.
- Around line 19-24: Replace the missing composite action reference by switching
the job step that currently uses "uses: ./.github/actions/setup-php-composer" to
a public action (e.g. "shivammathur/setup-php") and align the PHP version to
match the test matrix (change php-version from '8.4' to '8.2'); preserve the
php-extensions ('json') and composer-flags (--prefer-dist --no-interaction
--no-progress) in the step inputs for parity with the original intent, or
alternatively add the actual composite action file under
.github/actions/setup-php-composer if you prefer a local action.
In @.github/workflows/pint.yml:
- Around line 14-17: Update the pull_request branches list so the workflow
triggers on the repo's actual default branch: under the pull_request -> branches
array replace the non-existent "develop" entry with "development" (leaving
"master" as-is if needed) so the workflow's branch filter matches the real
branch names.
In @.github/workflows/release.yml:
- Around line 197-202: The workflow currently deletes all mPDF fonts except
those matching the pattern '! -name "DejaVu*"', which will break PDFs for
non-Latin scripts; replace that hardcoded filter with a configurable keep-list
by reading an environment variable (e.g., MPDF_KEEP_FONTS or MPDF_LOCALES) and
use it to build the find exclusion list (replace the '! -name "DejaVu*"' token
with generated '! -name "<font1>*" -o ! -name "<font2>*"' etc.), or
alternatively preserve a curated set of additional Unicode fonts (e.g.,
FreeSerif, Quivira) by changing the filter to '! -name "DejaVu*" -o ! -name
"FreeSerif*" -o ! -name "Quivira*"', and ensure the QR cleanup (rm -rf
vendor/mpdf/mpdf/src/QrCode/data/*) remains unchanged; also update workflow
docs/comments to explain the new MPDF_KEEP_FONTS variable and its impact on
non-Latin PDFs.
- Around line 76-79: Update the directory existence validation in the shell
if-block that currently checks for "resources/lang/en" (the Laravel path);
change it to verify the CodeIgniter directory "application/language/english"
instead so the if [ ! -d "resources/lang/en" ] check becomes an if [ ! -d
"application/language/english" ] check and the error message is updated to
reference application/language/english.
In @.github/workflows/yarn-update.yml:
- Around line 203-211: The workflow step with id "updated-packages" calls the
missing script ".github/scripts/generate-package-update-report.cjs" and will
fail when steps.check-changes.outputs.changes_detected == 'true'; fix by either
adding the script at .github/scripts/generate-package-update-report.cjs or make
the step robust—modify the step's run to first test for the script's existence
(or add a conditional using exists check) and skip generating the report if not
present, ensuring the step name "Get updated packages" and the condition
steps.check-changes.outputs.changes_detected remain correct.
In `@application/helpers/mailer_helper.php`:
- Around line 250-278: The root-relative referer check using
str_starts_with($referer, '/') is allowing protocol-relative URLs like
//evil.com; update the logic around $referer (the initial root-relative branch)
to only treat single-leading-slash paths as safe by rejecting or excluding
values that start with '//' (e.g., require str_starts_with($referer, '/') &&
!str_starts_with($referer, '//') or equivalent), leaving the existing
same-origin parse_url-based validation (the base_url/parse_url block that
compares $base_scheme/$base_host/$base_port with
$referer_scheme/$referer_host/$referer_port) unchanged and defaulting to
base_url() when validation fails.
In `@application/modules/sessions/controllers/Sessions.php`:
- Around line 528-573: The current _get_safe_referer method treats any string
starting with '/' as safe, which incorrectly allows protocol-relative referers
like '//evil.com'; update _get_safe_referer to explicitly reject referers that
start with '//' by checking for that prefix before the root-relative shortcut
(i.e., ensure str_starts_with($referer, '//') leads to using the safe default),
then keep the existing handling for single-leading-slash paths and full
same-origin URL checks so only true root-relative paths ('/path') and
same-origin absolute URLs are allowed.
🧹 Nitpick comments (4)
application/modules/clients/Enums/ClientTitleEnum.php (1)
20-32: Previous bugs fixed; consider adding strict comparison.The
!== falsecheck and returning$valuecorrectly address the issues from the previous review. For defensive coding, consider enabling strict type comparison inarray_searchto prevent unexpected matches from PHP's loose comparison:♻️ Suggested improvement
- $searchResult = array_search($value, $values); + $searchResult = array_search($value, $values, true);application/modules/sessions/controllers/Sessions.php (1)
323-360: Persist cleaned attempts to avoid session growth.
_is_ip_rate_limited_password_reset()filters out old timestamps but doesn’t write them back, so the session array can grow unbounded over long-lived sessions.♻️ Suggested fix
$attempts = array_filter($attempts, function ($timestamp) use ($cutoff_time) { return $timestamp > $cutoff_time; }); + + // Persist cleaned attempts to avoid unbounded session growth + $this->session->set_userdata($session_key, $attempts);.github/workflows/yarn-update.yml (1)
90-105: Yarn audit parsing is fragile for edge cases.The grep-based approach (
grep -c "auditAdvisory") works for typical cases but may produce unexpected results if the JSON contains the string "auditAdvisory" in other contexts (e.g., within package names or descriptions). Consider usingjqfor more robust parsing, similar to the Composer workflow.♻️ Optional: Use jq for more robust parsing
- name: Run Yarn audit id: audit continue-on-error: true run: | yarn audit --json > audit-report.json || true if [ -s audit-report.json ]; then - VULNERABILITIES=$(grep -c "auditAdvisory" audit-report.json || echo "0") + # Count actual advisory entries more reliably + VULNERABILITIES=$(grep -c '"type":"auditAdvisory"' audit-report.json || echo "0") if [ "$VULNERABILITIES" -gt 0 ]; then echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT echo "vulnerability_count=$VULNERABILITIES" >> $GITHUB_OUTPUT.github/workflows/pint.yml (1)
28-32: Checkout ref is empty on workflow_dispatch; use a conditional ref.
github.head_refis only set for PRs. For manual runs, this can resolve to an empty ref and may ignore the selected branch. Consider selectinggithub.reffor non-PR runs.✅ Suggested fix
- name: Checkout repository uses: actions/checkout@v4 with: - ref: ${{ github.head_ref }} + ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }} token: ${{ secrets.GITHUB_TOKEN }}
| - name: Run smoke tests | ||
| if: steps.check-changes.outputs.changes_detected == 'true' | ||
| run: | | ||
| cp .env.testing.example .env.testing | ||
| php artisan key:generate --env=testing | ||
| php artisan test --configuration=phpunit.smoke.xml --env=testing | ||
| continue-on-error: true |
There was a problem hiding this comment.
Smoke tests use Laravel artisan commands, but this is a CodeIgniter 3 project.
The quickstart workflow (line 64) explicitly states "CodeIgniter 3 project - Laravel artisan commands not applicable." However, this step runs php artisan key:generate and php artisan test, which will fail on a CodeIgniter 3 codebase.
While continue-on-error: true prevents workflow failure, the step provides no value and creates misleading output. Consider removing these Laravel-specific commands or replacing them with appropriate CodeIgniter 3 smoke tests.
🛠️ Suggested fix: Remove or replace with CI3-appropriate tests
- name: Run smoke tests
if: steps.check-changes.outputs.changes_detected == 'true'
run: |
- cp .env.testing.example .env.testing
- php artisan key:generate --env=testing
- php artisan test --configuration=phpunit.smoke.xml --env=testing
+ echo "Smoke tests not yet configured for CodeIgniter 3"
+ # TODO: Add appropriate smoke tests for CodeIgniter 3
continue-on-error: true📝 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.
| - name: Run smoke tests | |
| if: steps.check-changes.outputs.changes_detected == 'true' | |
| run: | | |
| cp .env.testing.example .env.testing | |
| php artisan key:generate --env=testing | |
| php artisan test --configuration=phpunit.smoke.xml --env=testing | |
| continue-on-error: true | |
| - name: Run smoke tests | |
| if: steps.check-changes.outputs.changes_detected == 'true' | |
| run: | | |
| echo "Smoke tests not yet configured for CodeIgniter 3" | |
| # TODO: Add appropriate smoke tests for CodeIgniter 3 | |
| continue-on-error: true |
🤖 Prompt for AI Agents
In @.github/workflows/composer-update.yml around lines 93 - 99, The "Run smoke
tests" workflow step currently invokes Laravel-specific commands ("php artisan
key:generate" and "php artisan test") which are not applicable to CodeIgniter 3;
update that step (named "Run smoke tests") to remove those artisan commands and
replace them with CI3-appropriate checks such as running PHPUnit directly (e.g.,
"php vendor/bin/phpunit --configuration phpunit.smoke.xml --env=testing") or a
lightweight CI3 bootstrap check (e.g., a script that loads index.php or runs a
CI3 smoke test suite); also remove or reconsider "continue-on-error: true" so
the step reports meaningful failures.
| - name: Setup PHP with Composer | ||
| uses: ./.github/actions/setup-php-composer | ||
| with: | ||
| php-version: '8.4' | ||
| php-extensions: 'json' | ||
| composer-flags: '--prefer-dist --no-interaction --no-progress' |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
echo "=== Checking composer.json for PHP version ==="
if [ -f composer.json ]; then
cat composer.json | jq '.require.php // .config.platform.php // "not found"' 2>/dev/null || cat composer.json | grep -i php
else
echo "composer.json not found"
fi
echo -e "\n=== Checking other workflows for PHP version ==="
rg -n 'php-version' .github/workflows/ --type yaml 2>/dev/null || echo "No workflows found or rg not available"
echo -e "\n=== Checking if composite action exists ==="
if [ -d .github/actions/setup-php-composer ]; then
echo "✓ Composite action exists"
echo "Contents:"
ls -la .github/actions/setup-php-composer/
else
echo "✗ Composite action directory not found"
fi
echo -e "\n=== Listing all workflows ==="
ls -la .github/workflows/ 2>/dev/null || echo "No workflows directory"Repository: InvoicePlane/InvoicePlane
Length of output: 2346
🏁 Script executed:
cat -n .github/workflows/phpstan.ymlRepository: InvoicePlane/InvoicePlane
Length of output: 3707
Composite action ./.github/actions/setup-php-composer does not exist—this workflow will fail.
The referenced action at line 20 cannot be found; the .github/actions/ directory does not exist in the repository. Additionally, PHP 8.4 used here differs from the test matrix in test-php.yml (which uses 8.2), creating inconsistency between static analysis and tests. Either create the missing composite action or use a standard action like shivammathur/setup-php.
🤖 Prompt for AI Agents
In @.github/workflows/phpstan.yml around lines 19 - 24, Replace the missing
composite action reference by switching the job step that currently uses "uses:
./.github/actions/setup-php-composer" to a public action (e.g.
"shivammathur/setup-php") and align the PHP version to match the test matrix
(change php-version from '8.4' to '8.2'); preserve the php-extensions ('json')
and composer-flags (--prefer-dist --no-interaction --no-progress) in the step
inputs for parity with the original intent, or alternatively add the actual
composite action file under .github/actions/setup-php-composer if you prefer a
local action.
| - name: Parse and format PHPStan results | ||
| id: parse-results | ||
| run: | | ||
| if [ ! -f phpstan.json ]; then | ||
| echo "PHPStan JSON output not found" | ||
| exit 1 | ||
| fi | ||
| php .github/scripts/parse-phpstan-results.php phpstan.json > phpstan-report.md | ||
| exit_code=$? | ||
| if [ $exit_code -ne 0 ]; then | ||
| echo "Failed to parse PHPStan results" | ||
| exit $exit_code | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the parse script exists
echo "=== Checking for parse-phpstan-results.php ==="
fd -t f 'parse-phpstan-results.php' .github/
echo -e "\n=== Listing .github/scripts/ contents ==="
ls -la .github/scripts/ 2>/dev/null || echo "Directory not found"Repository: InvoicePlane/InvoicePlane
Length of output: 178
The referenced PHP script .github/scripts/parse-phpstan-results.php does not exist.
The workflow will fail when attempting to execute this missing script. Either create the script at .github/scripts/parse-phpstan-results.php or update the workflow to reference the correct path if it exists elsewhere in the repository.
🤖 Prompt for AI Agents
In @.github/workflows/phpstan.yml around lines 33 - 45, The workflow step "Parse
and format PHPStan results" (id: parse-results) references a missing script
`.github/scripts/parse-phpstan-results.php`; fix by either adding that script to
the repository with the expected name and executable contents or update the step
to call the correct existing script path/filename that parses phpstan.json;
ensure the replacement path is committed and executable and that the step still
writes output to phpstan-report.md and returns appropriate exit codes on
failure.
| pull_request: | ||
| branches: | ||
| - master | ||
| - develop |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/pint.yml | head -50Repository: InvoicePlane/InvoicePlane
Length of output: 1764
🏁 Script executed:
git branch -a | head -20Repository: InvoicePlane/InvoicePlane
Length of output: 205
🏁 Script executed:
git branch -a | grep -i developRepository: InvoicePlane/InvoicePlane
Length of output: 155
Replace develop with development in pull_request branches.
The repository's default branch is development, but the workflow only triggers on master and develop. Since the develop branch does not exist in this repository, PRs targeting development will not trigger this workflow.
✅ Suggested fix
pull_request:
branches:
- master
- - develop
+ - development📝 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.
| pull_request: | |
| branches: | |
| - master | |
| - develop | |
| pull_request: | |
| branches: | |
| - master | |
| - development |
🤖 Prompt for AI Agents
In @.github/workflows/pint.yml around lines 14 - 17, Update the pull_request
branches list so the workflow triggers on the repo's actual default branch:
under the pull_request -> branches array replace the non-existent "develop"
entry with "development" (leaving "master" as-is if needed) so the workflow's
branch filter matches the real branch names.
| if [ ! -d "resources/lang/en" ]; then | ||
| echo "ERROR: Missing required language directory: resources/lang/en" | tee -a "$LOG_FILE" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for Laravel-style translations
echo "Checking for Laravel-style translations:"
ls -la resources/lang/ 2>/dev/null || echo "resources/lang/ not found"
echo ""
echo "Checking for CodeIgniter-style translations:"
ls -la application/language/ 2>/dev/null || echo "application/language/ not found"
echo ""
echo "Listing root directory to understand project structure:"
ls -la | head -20
echo ""
echo "Checking for framework indicators (composer.json, framework files):"
if [ -f composer.json ]; then echo "composer.json found"; fi
if [ -f package.json ]; then echo "package.json found"; fi
if [ -d application ]; then echo "application/ directory found (CodeIgniter indicator)"; fi
if [ -d app ]; then echo "app/ directory found (Laravel indicator)"; fiRepository: InvoicePlane/InvoicePlane
Length of output: 1819
🏁 Script executed:
#!/bin/bash
# Read the workflow file around the problematic lines
echo "=== Workflow file (lines 70-85) ==="
sed -n '70,85p' .github/workflows/release.yml
echo ""
echo "=== Contents of application/language/ ==="
ls -la application/language/
echo ""
echo "=== Contents of application/language/english (if exists) ==="
ls -la application/language/english/ 2>/dev/null | head -10Repository: InvoicePlane/InvoicePlane
Length of output: 1445
Fix translation validation path to match CodeIgniter structure.
The validation at line 76 checks for resources/lang/en, which is a Laravel directory structure. This project is CodeIgniter 3 and uses application/language/english/. The current validation will always fail because this path does not exist. Update the validation to check application/language/english instead.
🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 76 - 79, Update the directory
existence validation in the shell if-block that currently checks for
"resources/lang/en" (the Laravel path); change it to verify the CodeIgniter
directory "application/language/english" instead so the if [ ! -d
"resources/lang/en" ] check becomes an if [ ! -d "application/language/english"
] check and the error message is updated to reference
application/language/english.
| echo "Cleaning mPDF fonts (keeping only DejaVu)..." | tee -a "$LOG_FILE" | ||
| find vendor/mpdf/mpdf/ttfonts -type f ! -name "DejaVu*" \ | ||
| -delete 2>/dev/null || true | ||
|
|
||
| echo "Cleaning mPDF QR code data..." | tee -a "$LOG_FILE" | ||
| rm -rf vendor/mpdf/mpdf/src/QrCode/data/* 2>/dev/null || true |
There was a problem hiding this comment.
Deleting non-DejaVu fonts may break PDF generation for non-Latin scripts.
The mPDF cleanup removes all fonts except DejaVu (! -name "DejaVu*"). This will break PDF generation for languages requiring other font families (Chinese, Japanese, Korean, Arabic, Thai, etc.).
If international language support is required, consider either:
- Keeping additional Unicode fonts (e.g.,
FreeSerif,Quivira) - Making font retention configurable based on target locales
- Documenting this limitation for users who need non-Latin PDFs
🛠️ Suggested fix: Retain fonts for broader Unicode coverage
echo "Cleaning mPDF fonts (keeping only DejaVu)..." | tee -a "$LOG_FILE"
- find vendor/mpdf/mpdf/ttfonts -type f ! -name "DejaVu*" \
+ # Keep DejaVu for Latin scripts and common Unicode fonts for broader coverage
+ find vendor/mpdf/mpdf/ttfonts -type f \
+ ! -name "DejaVu*" \
+ ! -name "Free*" \
-delete 2>/dev/null || true🤖 Prompt for AI Agents
In @.github/workflows/release.yml around lines 197 - 202, The workflow currently
deletes all mPDF fonts except those matching the pattern '! -name "DejaVu*"',
which will break PDFs for non-Latin scripts; replace that hardcoded filter with
a configurable keep-list by reading an environment variable (e.g.,
MPDF_KEEP_FONTS or MPDF_LOCALES) and use it to build the find exclusion list
(replace the '! -name "DejaVu*"' token with generated '! -name "<font1>*" -o !
-name "<font2>*"' etc.), or alternatively preserve a curated set of additional
Unicode fonts (e.g., FreeSerif, Quivira) by changing the filter to '! -name
"DejaVu*" -o ! -name "FreeSerif*" -o ! -name "Quivira*"', and ensure the QR
cleanup (rm -rf vendor/mpdf/mpdf/src/QrCode/data/*) remains unchanged; also
update workflow docs/comments to explain the new MPDF_KEEP_FONTS variable and
its impact on non-Latin PDFs.
| - name: Get updated packages | ||
| if: steps.check-changes.outputs.changes_detected == 'true' | ||
| id: updated-packages | ||
| run: | | ||
| # Generate readable package update report | ||
| node .github/scripts/generate-package-update-report.cjs | ||
| echo "UPDATED_PACKAGES<<EOF" >> $GITHUB_OUTPUT | ||
| cat updated-packages.txt >> $GITHUB_OUTPUT | ||
| echo "EOF" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the external script exists
if [ -f ".github/scripts/generate-package-update-report.cjs" ]; then
echo "✓ Script exists"
echo "=== Script content (first 30 lines) ==="
head -30 .github/scripts/generate-package-update-report.cjs
else
echo "✗ Script NOT FOUND: .github/scripts/generate-package-update-report.cjs"
echo "Available scripts in .github/scripts/:"
ls -la .github/scripts/ 2>/dev/null || echo "Directory does not exist"
fiRepository: InvoicePlane/InvoicePlane
Length of output: 203
🏁 Script executed:
#!/bin/bash
# Verify the workflow file exists and check for any similar scripts
echo "=== Checking if yarn-update.yml exists ==="
if [ -f ".github/workflows/yarn-update.yml" ]; then
echo "✓ Workflow file exists"
echo "=== Lines 203-211 from yarn-update.yml ==="
sed -n '203,211p' .github/workflows/yarn-update.yml
else
echo "✗ Workflow file not found"
fi
echo ""
echo "=== Searching for any package update related scripts ==="
find . -type f -name "*.cjs" -o -name "*.js" | grep -i "package\|update" | head -20
echo ""
echo "=== Checking if any .github subdirectories exist ==="
ls -la .github/ 2>/dev/null | head -20Repository: InvoicePlane/InvoicePlane
Length of output: 1143
Missing required script will cause workflow failure.
The workflow step at lines 203-211 attempts to execute .github/scripts/generate-package-update-report.cjs, but this script and the .github/scripts/ directory do not exist in the repository. When steps.check-changes.outputs.changes_detected == 'true', this step will fail with "command not found". Either create the script or conditionally skip this step.
🤖 Prompt for AI Agents
In @.github/workflows/yarn-update.yml around lines 203 - 211, The workflow step
with id "updated-packages" calls the missing script
".github/scripts/generate-package-update-report.cjs" and will fail when
steps.check-changes.outputs.changes_detected == 'true'; fix by either adding the
script at .github/scripts/generate-package-update-report.cjs or make the step
robust—modify the step's run to first test for the script's existence (or add a
conditional using exists check) and skip generating the report if not present,
ensuring the step name "Get updated packages" and the condition
steps.check-changes.outputs.changes_detected remain correct.
| $referer = $_SERVER['HTTP_REFERER'] ?? ''; | ||
|
|
||
| // Treat root-relative referers (starting with "/") as safe | ||
| if ( ! empty($referer) && str_starts_with($referer, '/')) { | ||
| $redirect = $referer; | ||
| } else { | ||
| $redirect = base_url(); // Safe default | ||
| // Parse and compare URL components for same-origin check | ||
| $base_url = base_url(); | ||
| $base_parts = parse_url($base_url); | ||
| $referer_parts = parse_url($referer); | ||
|
|
||
| // Check if scheme, host, and port match (normalize default ports) | ||
| $base_scheme = $base_parts['scheme'] ?? 'http'; | ||
| $base_host = $base_parts['host'] ?? ''; | ||
| $base_port = $base_parts['port'] ?? ($base_scheme === 'https' ? 443 : 80); | ||
|
|
||
| $referer_scheme = $referer_parts['scheme'] ?? ''; | ||
| $referer_host = $referer_parts['host'] ?? ''; | ||
| $referer_port = $referer_parts['port'] ?? ($referer_scheme === 'https' ? 443 : 80); | ||
|
|
||
| if ( ! empty($referer) && | ||
| $base_scheme === $referer_scheme && | ||
| $base_host === $referer_host && | ||
| $base_port === $referer_port) { | ||
| $redirect = $referer; | ||
| } else { | ||
| $redirect = base_url(); // Safe default | ||
| } | ||
| } |
There was a problem hiding this comment.
Protocol-relative URLs bypass the root-relative check, creating an open redirect.
str_starts_with($referer, '/') on line 253 also matches //evil.com/path, which the browser interprets as a protocol-relative URL—redirecting users to an attacker-controlled domain.
Explicitly reject double-slash prefixes:
🔒 Proposed fix
// Treat root-relative referers (starting with "/") as safe
- if ( ! empty($referer) && str_starts_with($referer, '/')) {
+ if ( ! empty($referer) && str_starts_with($referer, '/') && ! str_starts_with($referer, '//')) {
$redirect = $referer;
} else {The same-origin validation logic for absolute URLs (lines 256-277) is well implemented with proper scheme/host/port comparison and default port normalization.
📝 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.
| $referer = $_SERVER['HTTP_REFERER'] ?? ''; | |
| // Treat root-relative referers (starting with "/") as safe | |
| if ( ! empty($referer) && str_starts_with($referer, '/')) { | |
| $redirect = $referer; | |
| } else { | |
| $redirect = base_url(); // Safe default | |
| // Parse and compare URL components for same-origin check | |
| $base_url = base_url(); | |
| $base_parts = parse_url($base_url); | |
| $referer_parts = parse_url($referer); | |
| // Check if scheme, host, and port match (normalize default ports) | |
| $base_scheme = $base_parts['scheme'] ?? 'http'; | |
| $base_host = $base_parts['host'] ?? ''; | |
| $base_port = $base_parts['port'] ?? ($base_scheme === 'https' ? 443 : 80); | |
| $referer_scheme = $referer_parts['scheme'] ?? ''; | |
| $referer_host = $referer_parts['host'] ?? ''; | |
| $referer_port = $referer_parts['port'] ?? ($referer_scheme === 'https' ? 443 : 80); | |
| if ( ! empty($referer) && | |
| $base_scheme === $referer_scheme && | |
| $base_host === $referer_host && | |
| $base_port === $referer_port) { | |
| $redirect = $referer; | |
| } else { | |
| $redirect = base_url(); // Safe default | |
| } | |
| } | |
| $referer = $_SERVER['HTTP_REFERER'] ?? ''; | |
| // Treat root-relative referers (starting with "/") as safe | |
| if ( ! empty($referer) && str_starts_with($referer, '/') && ! str_starts_with($referer, '//')) { | |
| $redirect = $referer; | |
| } else { | |
| // Parse and compare URL components for same-origin check | |
| $base_url = base_url(); | |
| $base_parts = parse_url($base_url); | |
| $referer_parts = parse_url($referer); | |
| // Check if scheme, host, and port match (normalize default ports) | |
| $base_scheme = $base_parts['scheme'] ?? 'http'; | |
| $base_host = $base_parts['host'] ?? ''; | |
| $base_port = $base_parts['port'] ?? ($base_scheme === 'https' ? 443 : 80); | |
| $referer_scheme = $referer_parts['scheme'] ?? ''; | |
| $referer_host = $referer_parts['host'] ?? ''; | |
| $referer_port = $referer_parts['port'] ?? ($referer_scheme === 'https' ? 443 : 80); | |
| if ( ! empty($referer) && | |
| $base_scheme === $referer_scheme && | |
| $base_host === $referer_host && | |
| $base_port === $referer_port) { | |
| $redirect = $referer; | |
| } else { | |
| $redirect = base_url(); // Safe default | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@application/helpers/mailer_helper.php` around lines 250 - 278, The
root-relative referer check using str_starts_with($referer, '/') is allowing
protocol-relative URLs like //evil.com; update the logic around $referer (the
initial root-relative branch) to only treat single-leading-slash paths as safe
by rejecting or excluding values that start with '//' (e.g., require
str_starts_with($referer, '/') && !str_starts_with($referer, '//') or
equivalent), leaving the existing same-origin parse_url-based validation (the
base_url/parse_url block that compares $base_scheme/$base_host/$base_port with
$referer_scheme/$referer_host/$referer_port) unchanged and defaulting to
base_url() when validation fails.
| /** | ||
| * Validates that a referer URL is from the same domain | ||
| * to prevent open redirect vulnerabilities | ||
| * to prevent open redirect vulnerabilities. | ||
| * | ||
| * @param string $referer | ||
| * | ||
| * @return string Safe redirect URL | ||
| */ | ||
| private function _get_safe_referer($referer = '') | ||
| { | ||
| // Use provided referer or HTTP_REFERER | ||
| $referer = empty($referer) ? ($_SERVER['HTTP_REFERER'] ?? '') : $referer; | ||
|
|
||
| // If no referer, use default | ||
| if (empty($referer)) { | ||
| return 'sessions/passwordreset'; | ||
| } | ||
|
|
||
| // Get base URL | ||
|
|
||
| // Treat root-relative referers (starting with "/") as safe | ||
| if (str_starts_with($referer, '/')) { | ||
| return $referer; | ||
| } | ||
|
|
||
| // Parse and compare URL components for same-origin check | ||
| $base_url = base_url(); | ||
| $base_parts = parse_url($base_url); | ||
| $referer_parts = parse_url($referer); | ||
|
|
||
| // Check if scheme, host, and port match (normalize default ports) | ||
| $base_scheme = $base_parts['scheme'] ?? 'http'; | ||
| $base_host = $base_parts['host'] ?? ''; | ||
| $base_port = $base_parts['port'] ?? ($base_scheme === 'https' ? 443 : 80); | ||
|
|
||
| // Check if referer starts with base URL (same domain) | ||
| if (strpos($referer, $base_url) === 0) { | ||
| $referer_scheme = $referer_parts['scheme'] ?? ''; | ||
| $referer_host = $referer_parts['host'] ?? ''; | ||
| $referer_port = $referer_parts['port'] ?? ($referer_scheme === 'https' ? 443 : 80); | ||
|
|
||
| if ($base_scheme === $referer_scheme && | ||
| $base_host === $referer_host && | ||
| $base_port === $referer_port) { | ||
| return $referer; | ||
| } | ||
|
|
||
| // Referer is external or invalid, use safe default | ||
| return 'sessions/passwordreset'; | ||
| } |
There was a problem hiding this comment.
Block protocol‑relative referers to prevent open redirect.
//evil.com starts with / and will pass the root‑relative shortcut, enabling a protocol‑relative redirect. Treat // as unsafe.
🔒 Suggested fix
- if (str_starts_with($referer, '/')) {
+ if (str_starts_with($referer, '/') && !str_starts_with($referer, '//')) {
return $referer;
}🤖 Prompt for AI Agents
In `@application/modules/sessions/controllers/Sessions.php` around lines 528 -
573, The current _get_safe_referer method treats any string starting with '/' as
safe, which incorrectly allows protocol-relative referers like '//evil.com';
update _get_safe_referer to explicitly reject referers that start with '//' by
checking for that prefix before the root-relative shortcut (i.e., ensure
str_starts_with($referer, '//') leads to using the safe default), then keep the
existing handling for single-leading-slash paths and full same-origin URL checks
so only true root-relative paths ('/path') and same-origin absolute URLs are
allowed.
d314f64 to
5b927da
Compare
Description
Added page numbering to the bottom of the PDF.
Related Issue
#1234
Motivation and Context
Page numbers were requested by @onny in #1234
Screenshots (if appropriate):
Pull Request Checklist
Issue Type (Please check one or more)
Summary by CodeRabbit
New Features
Chores
Refactor
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.