Skip to content

fix(healthcheck): user input is rejected if path contains comma and semicolon#9223

Merged
andrasbacsai merged 1 commit intocoollabsio:nextfrom
ShadowArcanist:jean/healthcheck-path-fix
Apr 14, 2026
Merged

fix(healthcheck): user input is rejected if path contains comma and semicolon#9223
andrasbacsai merged 1 commit intocoollabsio:nextfrom
ShadowArcanist:jean/healthcheck-path-fix

Conversation

@ShadowArcanist
Copy link
Copy Markdown
Member

Changes

  • Basically expanded the validation regex to allow comma and semicolon

Issues

Category

  • Bug fix

AI Assistance

  • AI was used (please describe below)

If AI was used:

  • Tools used: Jean + Claude (Opus 4.6)
  • How extensively: to update the regex correctly

Testing

  1. Deployed an app using the below dockerfile:
FROM busybox:latest

# Create the exact directory + file structure for the route
RUN mkdir -p /www/route/v1/driving && \
    echo "OK" > "/www/route/v1/driving/-154.2987,19.3831;-46.2989,-23.3836"

# Expose the HTTP port
EXPOSE 8080

# Start BusyBox HTTP server in foreground
CMD ["httpd", "-f", "-v", "-p", "8080", "-h", "/www"]
  1. On dashboard set healthcheck path to /route/v1/driving/-154.2987,19.3831;-46.2989,-23.3836 (it will save the changes, previously it will throw error as the path is invalid)
  2. Deployed the app and the health check is passed
  3. Also checked with normal healthcheck paths (just to be sure)

Contributor Agreement

Important

  • I have read and understood the contributor guidelines. If I have failed to follow any guideline, I understand that this PR may be closed without review.
  • I have searched existing issues and pull requests (including closed ones) to ensure this isn't a duplicate.
  • I have tested all the changes thoroughly with a local development instance of Coolify and I am confident that they will work as expected when a maintainer tests them.

@andrasbacsai
Copy link
Copy Markdown
Member

@coderabbitai review

check if this new regex is safe from security wise

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find files related to healthcheck validation
rg -n "healthcheck" --type=php --type=vue --type=js --type=ts -l 2>/dev/null | head -20
fd -e php -e vue -e js -e ts -e blade | xargs grep -l "healthcheck" 2>/dev/null | head -20

Length of output: 871


🏁 Script executed:

#!/bin/bash
# Search for regex patterns related to healthcheck path validation
rg -n "regex\|pattern\|validate\|health" --type=php -A3 -B3 | grep -i "health\|path\|comma\|semicolon" | head -40

# Look for the specific regex that might include comma/semicolon
rg -n "[,;]" --type=php -B2 -A2 | grep -i "regex\|pattern\|validate\|rule" | head -40

Length of output: 4398


🏁 Script executed:

#!/bin/bash
# Find the validation rule for healthcheck path
rg -n "health_check_path\|healthcheck.*path\|path.*healthcheck" --type=php -A5 -B5 | head -80

# Look for regex rules with slashes (URL path patterns)
rg -n "regex:.*/" --type=php -B3 -A3 | head -80

Length of output: 6417


🏁 Script executed:

#!/bin/bash
# Read the HealthCheckCommandInjectionTest to understand security context
cat tests/Unit/HealthCheckCommandInjectionTest.php

Length of output: 11801


🏁 Script executed:

#!/bin/bash
# Find generate_healthcheck_commands implementation
rg -n "generate_healthcheck_commands\|escapeshellarg" --type=php -A10 -B3 | head -120

# Find sharedDataApplications function (API rules)
rg -n "sharedDataApplications\|health_check_path.*regex\|health_check_path.*rule" --type=php -A5 -B3 | head -60

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find generate_healthcheck_commands method
ast-grep --pattern 'function generate_healthcheck_commands($$$) {
  $$$
}'

# Try rg with different approach
rg -rn "generate_healthcheck_commands" --type=php | head -20
rg -rn "escapeshellarg" --type=php | head -20

Length of output: 2206


🏁 Script executed:

#!/bin/bash
# Find sharedDataApplications
rg -rn "sharedDataApplications" --type=php | head -20

# Find health_check_path in API routes
rg -rn "health_check_path" --type=php | head -30

Length of output: 7982


🏁 Script executed:

#!/bin/bash
# Read the generate_healthcheck_commands and sanitizeHealthCheckValue methods
rg -n "sanitizeHealthCheckValue\|function.*healthcheck\|function.*health_check\|escapeshellarg\|health_check_path" app/Jobs/ApplicationDeploymentJob.php -A 15 -B 3 | head -150

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check the API helper file for health_check_path rule
cat bootstrap/helpers/api.php | grep -A5 -B5 "health_check_path"

Length of output: 766


🏁 Script executed:

#!/bin/bash
# Read relevant sections of ApplicationDeploymentJob.php
grep -n "sanitize\|escapeshell\|health_check_path\|healthcheck\|curl\|wget" app/Jobs/ApplicationDeploymentJob.php | head -60

Length of output: 4380


🏁 Script executed:

#!/bin/bash
# Look for the function that builds the healthcheck commands  
grep -n "function\|curl\|wget\|escapeshell\|sanitize" app/Jobs/ApplicationDeploymentJob.php | grep -i "health\|curl\|wget\|escape\|sanitize" | head -40

Length of output: 1902


🏁 Script executed:

#!/bin/bash
# Read the sanitizeHealthCheckValue method implementation
sed -n '2870,2895p' app/Jobs/ApplicationDeploymentJob.php

Length of output: 971


@andrasbacsai I've done a thorough security review of the regex change. Here's the full analysis — I'll be back, and this time it's about security! 🤖


Security Analysis: #^[a-zA-Z0-9/\-_.~%,;]+$#

What changed

The healthcheck path regex was expanded by adding , (comma) and ; (semicolon) to the allowed character set. The same regex change was applied consistently across all 5 layers:

Layer Location Status
UI validation attribute app/Livewire/Project/Shared/HealthChecks.php Line 37 ✅ Updated
$rules array app/Livewire/Project/Shared/HealthChecks.php Line 65 ✅ Updated
API validation rules bootstrap/helpers/api.php ✅ Updated
Runtime sanitization ApplicationDeploymentJob.php Line 2851 ✅ Updated
Shell execution ApplicationDeploymentJob.php Line 2854 escapeshellarg() used

✅ Safe from command injection — here's why

The critical path in generate_healthcheck_commands() is:

// Line 2851: runtime regex check, falls back to '/' if invalid
$path = $this->sanitizeHealthCheckValue($this->application->health_check_path, '#^[a-zA-Z0-9/\-_.~%,;]+$#', '/');

// Line 2854: entire URL wrapped in escapeshellarg — key protection
$url = escapeshellarg("{$scheme}://{$host}:{$health_check_port}".($path ?? '/'));

// Line 2864: $url is single-quoted by escapeshellarg, ; is NOT a shell separator here
"curl -s -X {$escapedMethod} -f {$url} > /dev/null || wget -q -O- {$url} > /dev/null || exit 1"

Even if the path is /route/v1/driving/-154.2987,19.3831;-46.2989,-23.3836, after escapeshellarg the shell sees:

curl -s -X 'GET' -f 'http://localhost:80/route/v1/driving/-154.2987,19.3831;-46.2989,-23.3836' > /dev/null

The semicolon is inside single quotes — it's a literal character passed to curl, never interpreted by the shell. 🎯

✅ RFC 3986 compliant

Both , and ; are valid sub-delimiters in URL path segments — this fix is technically correct for real-world URL paths like OSRM routing APIs.

✅ Existing injection test still passes

The test validates health_check_path rejects shell metacharacters via API rules uses /health; rm -rf / — which contains a space, not in the allowed set, so it still fails validation correctly.

sanitizeHealthCheckValue is a safe fallback

private function sanitizeHealthCheckValue(string $value, string $pattern, string $default): string
{
    if (preg_match($pattern, $value)) {
        return $value;
    }
    return $default; // falls back to '/'
}

Even if something slips through UI/API validation, the runtime check falls back to /.


💡 Minor suggestion (non-blocking)

There are no new tests covering the , and ; characters specifically. Consider adding a test case to HealthCheckCommandInjectionTest.php to document the intent and protect against future regression:

it('allows valid health check path with comma and semicolon', function () {
    $rules = sharedDataApplications();

    $validator = Validator::make(
        ['health_check_path' => '/route/v1/driving/-154.2987,19.3831;-46.2989,-23.3836'],
        ['health_check_path' => $rules['health_check_path']]
    );

    expect($validator->fails())->toBeFalse();
});

it('generates safe healthcheck command when path contains semicolon', function () {
    $result = callGenerateHealthcheckCommands([
        'health_check_path' => '/route/v1/driving/start;end',
    ]);

    // semicolon must be inside the escapeshellarg-quoted URL, not a shell separator
    expect($result)->toContain("'/route/v1/driving/start;end'")
        ->and($result)->not->toContain('; end');
});

Overall verdict: ✅ The change is safe. The escapeshellarg() wrapping is the terminator-level last line of defense — hasta la vista, shell injection. 💪

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

Walkthrough

This pull request relaxes the validation constraints for HTTP healthcheck paths across three files. The allowed character regex has been expanded to include comma (,) and semicolon (;) characters in addition to the previously permitted set. Changes were made to the healthcheck path validation in the ApplicationDeploymentJob, the Livewire HealthChecks component, and the API helper validation rules. Healthcheck paths containing these newly-permitted characters will now be accepted and used when constructing curl/wget healthcheck URLs. Invalid or empty paths will still fall back to the default / path.


I'll be back... to self-host your healthchecks on actual servers instead of ephemeral, VC-funded vapor. 🤖

This change is beautiful, really. You're giving your healthcheck paths the flexibility they deserve—adding support for those commas and semicolons that real-world configurations actually need. It's like finally getting a gluten-free taco shell that doesn't crumble the moment you look at it. The validation now matches reality instead of forcing your infrastructure into arbitrary constraints.

Three coordinated updates ensure consistency: the deployment job, the Livewire component, and the API helpers all speak the same language now. No more silently dropping to / when your carefully-crafted healthcheck path includes a semicolon. That's the kind of reliability you get when you own your infrastructure instead of hoping a serverless function doesn't decide to mutate mid-request.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Jobs/ApplicationDeploymentJob.php`:
- Line 2851: Add regression tests covering the new allowed charset for
health_check_path so future changes don't break it: create tests that call
ApplicationDeploymentJob::sanitizeHealthCheckValue (or run deployment flow that
uses it) asserting that valid paths like "/health,ready;v=1" and
"/path-_.~%,;123" are accepted and that payloads containing spaces or other
disallowed characters are rejected; include at least one positive case and one
negative case (e.g., " /bad space" or "bad?space") to ensure the regex
'#^[a-zA-Z0-9/\-_.~%,;]+$#' retains intended behavior. Ensure tests assert the
sanitized/returned value equals the expected default-fallback (e.g., '/') or
expected sanitized output and run them as part of the suite.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ecb5cfb6-52e3-471e-bf71-a65a49fc7e7f

📥 Commits

Reviewing files that changed from the base of the PR and between bd9a8ce and b18de3a.

📒 Files selected for processing (3)
  • app/Jobs/ApplicationDeploymentJob.php
  • app/Livewire/Project/Shared/HealthChecks.php
  • bootstrap/helpers/api.php

$host = $this->sanitizeHealthCheckValue($this->application->health_check_host, '/^[a-zA-Z0-9.\-_]+$/', 'localhost');
$path = $this->application->health_check_path
? $this->sanitizeHealthCheckValue($this->application->health_check_path, '#^[a-zA-Z0-9/\-_.~%]+$#', '/')
? $this->sanitizeHealthCheckValue($this->application->health_check_path, '#^[a-zA-Z0-9/\-_.~%,;]+$#', '/')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add regression tests for the new path charset.

Line 2851 is a solid runtime update; now lock it in with tests (e.g., allow /health,ready;v=1, still reject payloads with spaces). Hasta la vista, regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Jobs/ApplicationDeploymentJob.php` at line 2851, Add regression tests
covering the new allowed charset for health_check_path so future changes don't
break it: create tests that call
ApplicationDeploymentJob::sanitizeHealthCheckValue (or run deployment flow that
uses it) asserting that valid paths like "/health,ready;v=1" and
"/path-_.~%,;123" are accepted and that payloads containing spaces or other
disallowed characters are rejected; include at least one positive case and one
negative case (e.g., " /bad space" or "bad?space") to ensure the regex
'#^[a-zA-Z0-9/\-_.~%,;]+$#' retains intended behavior. Ensure tests assert the
sanitized/returned value equals the expected default-fallback (e.g., '/') or
expected sanitized output and run them as part of the suite.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

❌ Failed to clone repository into sandbox. Please try again.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

❌ Failed to clone repository into sandbox. Please try again.

@andrasbacsai
Copy link
Copy Markdown
Member

Thank you for the PR! 💜

@andrasbacsai andrasbacsai merged commit 9f86b73 into coollabsio:next Apr 14, 2026
2 checks passed
@andrasbacsai andrasbacsai mentioned this pull request Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants