fix(validation): add input validation for port exposes and port mappings fields (#9240)
This commit is contained in:
commit
3b96215226
10 changed files with 74 additions and 10 deletions
|
|
@ -153,8 +153,8 @@ protected function rules(): array
|
||||||
'staticImage' => 'required',
|
'staticImage' => 'required',
|
||||||
'baseDirectory' => array_merge(['required'], array_slice(ValidationPatterns::directoryPathRules(), 1)),
|
'baseDirectory' => array_merge(['required'], array_slice(ValidationPatterns::directoryPathRules(), 1)),
|
||||||
'publishDirectory' => ValidationPatterns::directoryPathRules(),
|
'publishDirectory' => ValidationPatterns::directoryPathRules(),
|
||||||
'portsExposes' => 'required',
|
'portsExposes' => ['required', 'string', 'regex:/^(\d+)(,\d+)*$/'],
|
||||||
'portsMappings' => 'nullable',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'customNetworkAliases' => 'nullable',
|
'customNetworkAliases' => 'nullable',
|
||||||
'dockerfile' => 'nullable',
|
'dockerfile' => 'nullable',
|
||||||
'dockerRegistryImageName' => 'nullable',
|
'dockerRegistryImageName' => 'nullable',
|
||||||
|
|
@ -212,6 +212,8 @@ protected function messages(): array
|
||||||
'staticImage.required' => 'The Static Image field is required.',
|
'staticImage.required' => 'The Static Image field is required.',
|
||||||
'baseDirectory.required' => 'The Base Directory field is required.',
|
'baseDirectory.required' => 'The Base Directory field is required.',
|
||||||
'portsExposes.required' => 'The Exposed Ports field is required.',
|
'portsExposes.required' => 'The Exposed Ports field is required.',
|
||||||
|
'portsExposes.regex' => 'Ports exposes must be a comma-separated list of port numbers (e.g. 3000,3001).',
|
||||||
|
...ValidationPatterns::portMappingMessages(),
|
||||||
'isStatic.required' => 'The Static setting is required.',
|
'isStatic.required' => 'The Static setting is required.',
|
||||||
'isStatic.boolean' => 'The Static setting must be true or false.',
|
'isStatic.boolean' => 'The Static setting must be true or false.',
|
||||||
'isSpa.required' => 'The SPA setting is required.',
|
'isSpa.required' => 'The SPA setting is required.',
|
||||||
|
|
@ -756,6 +758,12 @@ public function submit($showToaster = true)
|
||||||
$this->authorize('update', $this->application);
|
$this->authorize('update', $this->application);
|
||||||
|
|
||||||
$this->resetErrorBag();
|
$this->resetErrorBag();
|
||||||
|
|
||||||
|
$this->portsExposes = str($this->portsExposes)->replace(' ', '')->trim()->toString();
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
|
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
|
||||||
$oldPortsExposes = $this->application->ports_exposes;
|
$oldPortsExposes = $this->application->ports_exposes;
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ protected function rules(): array
|
||||||
'clickhouseAdminUser' => 'required|string',
|
'clickhouseAdminUser' => 'required|string',
|
||||||
'clickhouseAdminPassword' => 'required|string',
|
'clickhouseAdminPassword' => 'required|string',
|
||||||
'image' => 'required|string',
|
'image' => 'required|string',
|
||||||
'portsMappings' => 'nullable|string',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -94,6 +94,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'clickhouseAdminUser.required' => 'The Admin User field is required.',
|
'clickhouseAdminUser.required' => 'The Admin User field is required.',
|
||||||
'clickhouseAdminUser.string' => 'The Admin User must be a string.',
|
'clickhouseAdminUser.string' => 'The Admin User must be a string.',
|
||||||
|
|
@ -209,6 +210,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('update', $this->database);
|
$this->authorize('update', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
if (str($this->publicPort)->isEmpty()) {
|
if (str($this->publicPort)->isEmpty()) {
|
||||||
$this->publicPort = null;
|
$this->publicPort = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ protected function rules(): array
|
||||||
'description' => ValidationPatterns::descriptionRules(),
|
'description' => ValidationPatterns::descriptionRules(),
|
||||||
'dragonflyPassword' => 'required|string',
|
'dragonflyPassword' => 'required|string',
|
||||||
'image' => 'required|string',
|
'image' => 'required|string',
|
||||||
'portsMappings' => 'nullable|string',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -106,6 +106,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'dragonflyPassword.required' => 'The Dragonfly Password field is required.',
|
'dragonflyPassword.required' => 'The Dragonfly Password field is required.',
|
||||||
'dragonflyPassword.string' => 'The Dragonfly Password must be a string.',
|
'dragonflyPassword.string' => 'The Dragonfly Password must be a string.',
|
||||||
|
|
@ -219,6 +220,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('update', $this->database);
|
$this->authorize('update', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
if (str($this->publicPort)->isEmpty()) {
|
if (str($this->publicPort)->isEmpty()) {
|
||||||
$this->publicPort = null;
|
$this->publicPort = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ protected function rules(): array
|
||||||
'keydbConf' => 'nullable|string',
|
'keydbConf' => 'nullable|string',
|
||||||
'keydbPassword' => 'required|string',
|
'keydbPassword' => 'required|string',
|
||||||
'image' => 'required|string',
|
'image' => 'required|string',
|
||||||
'portsMappings' => 'nullable|string',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -111,6 +111,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'keydbPassword.required' => 'The KeyDB Password field is required.',
|
'keydbPassword.required' => 'The KeyDB Password field is required.',
|
||||||
'keydbPassword.string' => 'The KeyDB Password must be a string.',
|
'keydbPassword.string' => 'The KeyDB Password must be a string.',
|
||||||
|
|
@ -226,6 +227,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('manageEnvironment', $this->database);
|
$this->authorize('manageEnvironment', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
if (str($this->publicPort)->isEmpty()) {
|
if (str($this->publicPort)->isEmpty()) {
|
||||||
$this->publicPort = null;
|
$this->publicPort = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ protected function rules(): array
|
||||||
'mariadbDatabase' => 'required',
|
'mariadbDatabase' => 'required',
|
||||||
'mariadbConf' => 'nullable',
|
'mariadbConf' => 'nullable',
|
||||||
'image' => 'required',
|
'image' => 'required',
|
||||||
'portsMappings' => 'nullable',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -92,6 +92,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'name.required' => 'The Name field is required.',
|
'name.required' => 'The Name field is required.',
|
||||||
'mariadbRootPassword.required' => 'The Root Password field is required.',
|
'mariadbRootPassword.required' => 'The Root Password field is required.',
|
||||||
|
|
@ -215,6 +216,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('update', $this->database);
|
$this->authorize('update', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
if (str($this->publicPort)->isEmpty()) {
|
if (str($this->publicPort)->isEmpty()) {
|
||||||
$this->publicPort = null;
|
$this->publicPort = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ protected function rules(): array
|
||||||
'mongoInitdbRootPassword' => 'required',
|
'mongoInitdbRootPassword' => 'required',
|
||||||
'mongoInitdbDatabase' => 'required',
|
'mongoInitdbDatabase' => 'required',
|
||||||
'image' => 'required',
|
'image' => 'required',
|
||||||
'portsMappings' => 'nullable',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -92,6 +92,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'name.required' => 'The Name field is required.',
|
'name.required' => 'The Name field is required.',
|
||||||
'mongoInitdbRootUsername.required' => 'The Root Username field is required.',
|
'mongoInitdbRootUsername.required' => 'The Root Username field is required.',
|
||||||
|
|
@ -215,6 +216,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('update', $this->database);
|
$this->authorize('update', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
if (str($this->publicPort)->isEmpty()) {
|
if (str($this->publicPort)->isEmpty()) {
|
||||||
$this->publicPort = null;
|
$this->publicPort = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ protected function rules(): array
|
||||||
'mysqlDatabase' => 'required',
|
'mysqlDatabase' => 'required',
|
||||||
'mysqlConf' => 'nullable',
|
'mysqlConf' => 'nullable',
|
||||||
'image' => 'required',
|
'image' => 'required',
|
||||||
'portsMappings' => 'nullable',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -95,6 +95,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'name.required' => 'The Name field is required.',
|
'name.required' => 'The Name field is required.',
|
||||||
'mysqlRootPassword.required' => 'The Root Password field is required.',
|
'mysqlRootPassword.required' => 'The Root Password field is required.',
|
||||||
|
|
@ -222,6 +223,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('update', $this->database);
|
$this->authorize('update', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
if (str($this->publicPort)->isEmpty()) {
|
if (str($this->publicPort)->isEmpty()) {
|
||||||
$this->publicPort = null;
|
$this->publicPort = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ protected function rules(): array
|
||||||
'postgresConf' => 'nullable',
|
'postgresConf' => 'nullable',
|
||||||
'initScripts' => 'nullable',
|
'initScripts' => 'nullable',
|
||||||
'image' => 'required',
|
'image' => 'required',
|
||||||
'portsMappings' => 'nullable',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -107,6 +107,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'name.required' => 'The Name field is required.',
|
'name.required' => 'The Name field is required.',
|
||||||
'postgresUser.required' => 'The Postgres User field is required.',
|
'postgresUser.required' => 'The Postgres User field is required.',
|
||||||
|
|
@ -469,6 +470,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('update', $this->database);
|
$this->authorize('update', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
if (str($this->publicPort)->isEmpty()) {
|
if (str($this->publicPort)->isEmpty()) {
|
||||||
$this->publicPort = null;
|
$this->publicPort = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ protected function rules(): array
|
||||||
'description' => ValidationPatterns::descriptionRules(),
|
'description' => ValidationPatterns::descriptionRules(),
|
||||||
'redisConf' => 'nullable',
|
'redisConf' => 'nullable',
|
||||||
'image' => 'required',
|
'image' => 'required',
|
||||||
'portsMappings' => 'nullable',
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
||||||
'isPublic' => 'nullable|boolean',
|
'isPublic' => 'nullable|boolean',
|
||||||
'publicPort' => 'nullable|integer|min:1|max:65535',
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
||||||
'publicPortTimeout' => 'nullable|integer|min:1',
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
||||||
|
|
@ -89,6 +89,7 @@ protected function messages(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
ValidationPatterns::combinedMessages(),
|
ValidationPatterns::combinedMessages(),
|
||||||
|
ValidationPatterns::portMappingMessages(),
|
||||||
[
|
[
|
||||||
'name.required' => 'The Name field is required.',
|
'name.required' => 'The Name field is required.',
|
||||||
'image.required' => 'The Docker Image field is required.',
|
'image.required' => 'The Docker Image field is required.',
|
||||||
|
|
@ -203,6 +204,9 @@ public function submit()
|
||||||
try {
|
try {
|
||||||
$this->authorize('manageEnvironment', $this->database);
|
$this->authorize('manageEnvironment', $this->database);
|
||||||
|
|
||||||
|
if ($this->portsMappings) {
|
||||||
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
||||||
|
}
|
||||||
$this->syncData(true);
|
$this->syncData(true);
|
||||||
|
|
||||||
if (version_compare($this->redisVersion, '6.0', '>=')) {
|
if (version_compare($this->redisVersion, '6.0', '>=')) {
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,12 @@ public static function volumeNameMessages(string $field = 'name'): array
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern for port mappings (e.g. 3000:3000, 8080:80, 8000-8010:8000-8010)
|
||||||
|
* Each entry requires host:container format, where each side can be a number or a range (number-number)
|
||||||
|
*/
|
||||||
|
public const PORT_MAPPINGS_PATTERN = '/^(\d+(-\d+)?:\d+(-\d+)?)(,\d+(-\d+)?:\d+(-\d+)?)*$/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get validation rules for container name fields
|
* Get validation rules for container name fields
|
||||||
*/
|
*/
|
||||||
|
|
@ -209,6 +215,24 @@ public static function containerNameRules(int $maxLength = 255): array
|
||||||
return ['string', 'max:'.$maxLength, 'regex:'.self::CONTAINER_NAME_PATTERN];
|
return ['string', 'max:'.$maxLength, 'regex:'.self::CONTAINER_NAME_PATTERN];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get validation rules for port mapping fields
|
||||||
|
*/
|
||||||
|
public static function portMappingRules(): array
|
||||||
|
{
|
||||||
|
return ['nullable', 'string', 'regex:'.self::PORT_MAPPINGS_PATTERN];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get validation messages for port mapping fields
|
||||||
|
*/
|
||||||
|
public static function portMappingMessages(string $field = 'portsMappings'): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"{$field}.regex" => 'Port mappings must be a comma-separated list of port pairs or ranges (e.g. 3000:3000,8080:80,8000-8010:8000-8010).',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a string is a valid Docker container name.
|
* Check if a string is a valid Docker container name.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue