Ensure that the uuid field is preserved during clone operations for persistent
volumes across all clone methods (CloneMe, ResourceOperations, and the clone_application
helper). This prevents UUID conflicts and ensures cloned volumes receive new unique
identifiers as intended.
Adds test coverage validating that cloned persistent volumes receive new UUIDs
distinct from the original volumes.
Restrict mass-assignable attributes across user/team/redis models and
switch privileged root/team creation paths to forceFill/forceCreate.
Encrypt legacy ClickHouse admin passwords via migration and cast the
correct ClickHouse password field as encrypted.
Tighten API and runtime exposure by removing sensitive team fields from
responses and sanitizing Git/compose error messages.
Expand security-focused feature coverage for command-injection and mass
assignment protections.
Assign the selected redirect option before validation so valid changes are saved.
Add feature tests to verify redirect persistence and rejection when no www domain exists.
Replace shared `changePasswordFieldType` JS with component-local Alpine logic
across input, textarea, and env-var-input components. This keeps toggle
behavior consistent, resets visibility on `success` events, and preserves
`truncate` styling only when showing plaintext on enabled fields.
Also adds `PasswordVisibilityComponentTest` to verify Alpine bindings are
rendered and legacy handler references are removed.
Use firstOrFail() for team-scoped project and environment lookups across
new-project Livewire flows so missing or cross-team UUIDs fail closed.
Also dispatch an error when boarding selects a non-owned project, and
update IDOR feature tests for the new error/exception behavior.
Add support for configuring public port timeout on databases via API:
- Add public_port_timeout field to schema documentation with 3600s default
- Add validation rules (integer|nullable|min:1)
- Update all database type configurations to support the field
- Add comprehensive test coverage for the feature
Replace $guarded = [] with explicit $fillable whitelists across all
models. Update controllers to use request->only($allowedFields) when
assigning request data. Switch Livewire components to forceFill() for
explicit mass assignment. Add integration tests for mass assignment
protection.
Ensure Server and Project lookups in Livewire components and API
controllers use team-scoped queries (ownedByCurrentTeam / whereTeamId)
instead of unscoped find/where calls. This enforces consistent
multi-tenant isolation across all user-facing code paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add #[Locked] attributes to security-sensitive properties (resource, servicesubtype,
server, container) to prevent client-side modification via Livewire wire protocol.
Add container name validation using ValidationPatterns::isValidContainerName() and
server ownership authorization via Server::ownedByCurrentTeam() in both getLogs()
and downloadAllLogs() methods.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add shellSafeCommandRules() validation to install_command, build_command,
and start_command fields in both the Livewire UI and REST API layers.
These fields previously accepted arbitrary strings without validation,
unlike other shell-adjacent fields which already used this pattern.
Also adds comprehensive tests for rejection of dangerous input and
acceptance of legitimate build commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace mt_rand/rand with random_int for stronger randomness guarantees
in verification code generation and Blade component keying.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the admin panel route into the existing auth middleware group and
replace client-side redirects with server-side abort calls in the
Livewire component. Extract shared authorization logic into reusable
private methods.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delegate host validation to parent class instead of custom implementation
- Update base_url() helper to use config('app.url') instead of url('/')
- Add test for APP_URL fallback when no FQDN or public IPs configured
- Remove dedicated TrustHostsMiddlewareTest (logic now tested via integration tests)
Extract and return the billing interval (month/year) from subscription pricing
data in fetchPricePreview. Update the view to dynamically display the correct
billing period based on the preview response instead of using static PHP logic.
- Add support for bracketed IPv6 addresses when FQDN is not configured
- Harden password reset URL generation against X-Forwarded-Host header poisoning
- Add test coverage for IPv6-only configurations with malicious headers
- Update imports and clean up exception syntax in shared helpers
- Fix circular cache dependency in TrustHosts where handle() checked cache
before hosts() could populate it, causing host validation to never activate
- Validate both Host and X-Forwarded-Host headers against trusted hosts list
(X-Forwarded-Host is checked before TrustProxies applies it to the request)
- Use base_url() instead of url() for password reset link generation so the
URL is derived from server-side config (FQDN / public IP) instead of the
request context
- Strip port from X-Forwarded-Host before matching (e.g. host:443 → host)
- Add tests for host validation, cache population, and reset URL generation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Escape dynamic error messages with htmlspecialchars() before
concatenating into HTML strings stored in validation_logs. Add a
Purify-based mutator on Server model as defense-in-depth, with a
dedicated HTMLPurifier config that allows only safe structural tags.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactor the invitation acceptance flow to use a landing page pattern:
- GET shows invitation details (team name, role, confirmation button)
- POST processes the acceptance with proper form submission
- Remove unused revoke GET route (handled by Livewire component)
- Add Blade view for the invitation landing page
- Add feature tests for the new invitation flow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Inline PrepareCoolifyTask and CoolifyTaskArgs into remote_process(),
removing two single-consumer abstraction layers
- Add #[Locked] attribute to ActivityMonitor $activityId property
- Add team ownership verification in ActivityMonitor.hydrateActivity()
with server_uuid fallback and fail-closed default
- Store team_id in activity properties for proper scoping
- Update CLAUDE.md to remove stale reference
- Add comprehensive tests for activity monitor authorization
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, the SHELL_SAFE_COMMAND_PATTERN was overly restrictive and blocked
legitimate characters needed for common Docker operations:
- Allow & for command chaining with && in multi-step build commands
- Allow " for build arguments with spaces (e.g., --build-arg KEY="value")
Update validation messages to reflect the new allowed operators and refactor
code to use imports instead of full class paths for better readability.
- Add #[Locked] to server-set properties on Import component (resourceId,
resourceType, serverId, resourceUuid, resourceDbType, container) to
prevent client-side modification via Livewire wire protocol
- Add container name validation in runImport() and restoreFromS3()
using shared ValidationPatterns::isValidContainerName()
- Scope server lookup to current team via ownedByCurrentTeam()
- Consolidate duplicate container name regex from Import,
ExecuteContainerCommand, and Terminal into shared
ValidationPatterns::isValidContainerName() static helper
- Add tests for container name validation, locked attributes, and
team-scoped server lookup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply the existing `can.access.terminal` middleware to `POST /terminal/auth`
and `POST /terminal/auth/ips` routes, consistent with the `GET /terminal` route.
Adds regression tests covering unauthenticated, member, admin, and owner roles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add team-scoped server validation to domains_by_server API endpoint
- Filter applications and services to only those on the requested server
- Scope ActivityMonitor activity lookups to the current team
- Fix query param disambiguation (query vs route param) in domains endpoint
- Fix undefined $ip variable in services domain collection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When preview environment variables are configured, fall back to production
variables for keys not overridden by preview values. This ensures variables
like DB_PASSWORD that exist only in production are available in the preview
.env file, enabling proper ${VAR} interpolation in docker-compose YAML.
Fallback only applies when preview variables are configured, preventing
unintended leakage of production values when previews aren't in use.
Also improves UI by hiding the Domains section when only database services
are present, and simplifies the logs view by removing status checks.
- Replace manual subscription create/update logic with updateOrCreate() and firstOrCreate() to eliminate race conditions
- Add validation in PricingPlans to prevent subscribing if team already has active subscription
- Improve error handling for missing team_id in customer.subscription.updated event
- Add tests verifying subscriptions are updated rather than duplicated
Allow serverLimit() and serverLimitReached() to accept an optional team
parameter instead of relying solely on the current session. This improves
testability and makes the methods more flexible by allowing them to work
without session state.
Add comprehensive tests covering various scenarios including no session,
team at limit, and team under limit.
Add validation in manual and normal webhook handlers to reject GitHub
event types other than 'push' and 'pull_request'. Unsupported events
now return a graceful response instead of potentially causing
downstream errors. Includes tests for ping events, unsupported event
types, and unknown events.
- Add storage endpoints (list, create, update, delete) to DatabasesController
- Add storage endpoints (list, create, update, delete) to ServicesController
- Add UUID field and migration for local_persistent_volumes table
- Update LocalPersistentVolume model to extend BaseModel
- Support UUID-based storage identification in ApplicationsController
- Update OpenAPI documentation with new storage endpoints and schemas
- Fix application name generation to extract repo name from full git path
- Add comprehensive tests for storage API operations
Extract the shouldRunNow() method from ScheduledJobManager and ServerManagerJob into
a reusable shouldRunCronNow() helper function. This centralizes cron scheduling logic
and enables consistent deduplication behavior across all scheduled job types.
- Create shouldRunCronNow() helper in bootstrap/helpers/shared.php with timezone
and dedup support
- Refactor ScheduledJobManager and ServerManagerJob to use the shared helper
- Add ScheduledJobDiagnostics command for inspecting cache state and scheduling
decisions across all scheduled jobs
- Simplify shouldRunNow tests to directly test the helper function
- Add DockerCleanupJob test for error handling and execution tracking
- Increase scheduled log retention from 1 to 7 days
Implements getPreviousRunDate() + cache-based tracking in shouldRunNow()
to prevent duplicate dispatch of scheduled jobs when queue delays push
execution past the cron minute. This resilience ensures jobs catch missed
windows without double-dispatching within the same cron window.
Updated scheduled job dispatches to include dedupKey parameter:
- Docker cleanup operations
- Server connection checks
- Sentinel restart checks
- Server storage checks
- Server patch checks
DockerCleanupJob now dispatches on the 'high' queue for faster processing.
Includes comprehensive test coverage for dedup behavior across different
cron schedules and delay scenarios.
Add support for optional comment field on environment variables created or
updated through the bulk API endpoints. Comments are validated to a maximum
of 256 characters and are nullable. Updates preserve existing comments when
not provided in the request.
Extract resource UUIDs from route parameters instead of request body
in ApplicationsController and ServicesController environment variable
endpoints. This prevents UUID parameters from being spoofed in the
request body.
- Replace $request->uuid with $request->route('uuid')
- Replace $request->env_uuid with $request->route('env_uuid')
- Add tests verifying route parameters are used and body UUIDs ignored
Add new Resources tab to storage show page displaying backup schedules using
that storage. Refactor storage show layout with navigation tabs for General
and Resources sections. Move delete action from form to show component.
Implement cascade deletion in S3Storage model to automatically disable S3
backups when storage is deleted. Improve error handling in DatabaseBackupJob
to throw exception when S3 storage is missing instead of silently returning.
- New Storage/Resources Livewire component
- Add resources.blade.php view
- Add storage.resources route
- Move delete() method from Form to Show component
- Add deleting event listener to S3Storage model
- Track backup count and current route in Show component
- Add #[On('submitStorage')] attribute to form submission
- Record refunds immediately before cancellation to prevent retry issues if cancel fails
- Wrap Stripe API calls in try-catch for refunds and quantity reverts with internal notifications
- Add null check in Team.subscriptionEnded() to prevent NPE when subscription doesn't exist
- Fix control flow bug in StripeProcessJob (add missing break statement)
- Cap dynamic server limit with MAX_SERVER_LIMIT in subscription updates
- Add comprehensive tests for refund failures, event handling, and null safety
Add current_period_end to refund eligibility checks and display next billing
date and billing interval in the subscription overview. Refactor the plan
overview layout to show subscription status more prominently.