JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr{ gilour
<?php namespace App\Http\Requests; use Closure; use Common\Core\BaseFormRequest; use Common\Domains\Validation\ValidateLinkWithGoogleSafeBrowsing; use Common\Domains\Validation\ValidateLinkWithPhishtank; use Common\Settings\Settings; use Exception; use Illuminate\Database\Query\Builder; use Illuminate\Support\Str; use Illuminate\Validation\Rule; use Illuminate\Validation\Validator; class CrupdateLinkRequest extends BaseFormRequest { public function rules(Settings $settings): array { $ruleRequired = $this->getMethod() === 'POST' ? 'required' : ''; $longUrlRequired = $this->getMethod() === 'POST' ? 'required_without:long_urls' : ''; $except = $this->getMethod() === 'PUT' ? $this->route('link')->id : ''; $aliasMin = $settings->get('links.alias_min', 5); $aliasMax = $settings->get('links.alias_max', 10); $aliasContent = $settings->get('links.alias_content', 'alpha-dash'); $linkMin = $settings->get('links.min_len', 3); $linkMax = $settings->get('links.max_len', 1000); $rules = [ 'alias' => [ 'nullable', 'string', $aliasContent, "min:$aliasMin", "max:$aliasMax", function (string $attribute, mixed $value, Closure $fail) { $blacklist = collect( explode(',', settings('links.blacklist.keywords')), ); $blacklist->transform(fn($item) => trim($item)); if ( $keyword = $blacklist->first( fn($item) => Str::contains($value, $item), ) ) { $fail(__("Alias can't contain '$keyword'.")); } }, Rule::unique('links', 'alias') ->ignore($except) ->where(function (Builder $builder) { if ($domainId = $this->get('domain_id')) { $builder->where('domain_id', $domainId); } else { $builder ->whereNull('domain_id') ->orWhere('domain_id', 0); } }), Rule::unique('link_groups', 'hash')->where(function ( Builder $builder, ) { if ($domainId = $this->get('domain_id')) { $builder->where('domain_id', $domainId); } else { $builder ->whereNull('domain_id') ->orWhere('domain_id', 0); } }), ], 'long_url' => "string|$longUrlRequired|min:$linkMin|max:$linkMax", 'domain_id' => 'nullable|integer', 'password' => 'nullable|string|max:250', 'image' => 'string|nullable|max:250', 'active' => 'nullable|boolean', 'description' => 'nullable|string|max:250', 'expires_at' => 'nullable|date', 'pixels.*' => 'required|int', 'groups' => 'array', 'rules' => 'array', 'rules.*.key' => "$ruleRequired|string|max:250", 'rules.*.value' => "$ruleRequired|string|max:250", 'rules.*.type' => "$ruleRequired|string|max:250", ]; if ($this->getMethod() === 'POST') { $rules['long_urls'] = 'required_without:long_url|array|max:10'; $rules['long_urls.*'] = 'required'; } return $rules; } protected function withValidator(Validator $validator) { return $validator->after(function (Validator $validator) { if ($validator->errors()->has('long_urls.*')) { $validator ->errors() ->add('long_urls', 'One of the urls is not valid.'); // base "url" validation failed, can bail return; } if ($multipleUrls = $this->get('long_urls')) { foreach ($multipleUrls as $url) { $this->runCustomValidations($url, $validator, 'long_urls'); } } elseif ($longUrl = $this->get('long_url')) { $this->runCustomValidations($longUrl, $validator, 'long_url'); } }); } private function runCustomValidations( string $url, Validator $validator, string $errorKey, ): void { $this->validateAgainstBlacklist( $url, $validator, 'keywords', $errorKey, ); $this->validateAgainstBlacklist($url, $validator, 'domains', $errorKey); $this->validateAgainstGoogleSafeBrowsing($url, $validator, $errorKey); $this->validateAgainstPhishtank($url, $validator, $errorKey); $this->validateOriginPolicy($url, $validator, $errorKey); } private function validateOriginPolicy($url, Validator $validator, $errorKey) { $type = $this->get('type') ?: $this->route('link.type'); if ($type !== 'frame' && $type !== 'overlay') { return; } $blacklist = [ 'x-frame-options: deny', 'x-frame-options: sameorigin', 'x-frame-options: allow-from', ]; try { $headers = get_headers($url); } catch (Exception $e) { $headers = []; } $cantIframe = collect($headers)->first(function ($header) use ( $blacklist, ) { $header = strtolower($header); return in_array($header, $blacklist); }); if ($cantIframe) { $start = $errorKey === 'long_url' ? 'This URL' : 'One of the urls'; $validator ->errors() ->add( $errorKey, __( "$start does not allow framing. Please select a different link type.", ), ); } } private function validateAgainstBlacklist( string $url, Validator $validator, string $listName, string $errorKey, ): void { // key specified blacklist (keyword or domain) $list = collect(explode(',', settings("links.blacklist.$listName"))); $list->transform(fn($item) => trim($item)); // check if url matches any blacklist item $match = $list->first(fn($item) => Str::contains($url, $item)); if ($match) { if ($listName === 'keywords') { $validator->errors()->add( $errorKey, __('URLs can\'t contain the word ":word".', [ 'word' => $match, ]), ); } else { $validator->errors()->add( $errorKey, __('URLs from ":domain" domain can\'t be shortened.', [ 'domain' => $match, ]), ); } } } private function validateAgainstGoogleSafeBrowsing( string $url, Validator $validator, string $errorKey, ): void { $valid = (new ValidateLinkWithGoogleSafeBrowsing())->execute($url); if (!$valid) { $start = $errorKey === 'long_url' ? 'This URL' : 'One of the urls'; $validator ->errors() ->add( $errorKey, __("$start can't be shortened, because it is unsafe."), ); } } private function validateAgainstPhishtank( $url, Validator $validator, $errorKey, ): void { $valid = (new ValidateLinkWithPhishtank())->execute($url); if (!$valid) { $start = $errorKey === 'long_url' ? 'This URL' : 'One of the urls'; $validator ->errors() ->add( $errorKey, __( "$start can't be shortened, because it is used for phising.", ), ); } } }