Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions phalcon/Filter/Validation/Validator/File.zep
Original file line number Diff line number Diff line change
Expand Up @@ -111,22 +111,23 @@ class File extends AbstractValidatorComposite
* 'maxResolution' => '1000x1000',
* 'messageMaxResolution' => '',
* 'includedMaxResolution' => false,
* 'minResolution => '500x500',
* 'minResolution' => '500x500',
* 'includedMinResolution' => false,
* 'messageMinResolution' => '',
* 'equalResolution' => '1000x1000',
* 'messageEqualResolution' => '',
* 'allowEmpty' => false,
* 'messageFileEmpty' => '',
* 'messageIniSize' => '',
* 'messageValid' => ''
* 'messageValid' => '',
* 'ignoreCheckUploadedFile' => false
* ]
*/
public function __construct(array! options = [])
{
var helper, included = null, key, message = null,
messageFileEmpty = null, messageIniSize = null, messageValid = null,
validator, value;
validator, value, ignoreFlag;

let helper = new Get();

Expand All @@ -145,7 +146,15 @@ class File extends AbstractValidatorComposite
unset options["messageValid"];
}

// create individual validators
/**
* Store ignoreCheckUploadedFile option internally
*/
if isset options["ignoreCheckUploadedFile"] {
let this->options["ignoreCheckUploadedFile"] = options["ignoreCheckUploadedFile"];
unset options["ignoreCheckUploadedFile"];
}

// Create individual validators
for key, value in options {
// min file size
if strcasecmp(key, "minSize") === 0 {
Expand Down Expand Up @@ -280,6 +289,15 @@ class File extends AbstractValidatorComposite
let this->validators[] = validator;
}

/**
* Propagate ignoreCheckUploadedFile option to all sub-validators
*/
if fetch ignoreFlag, this->options["ignoreCheckUploadedFile"] {
for validator in this->validators {
validator->setOption("ignoreCheckUploadedFile", ignoreFlag);
}
}

parent::__construct(options);
}
}
71 changes: 59 additions & 12 deletions phalcon/Filter/Validation/Validator/File/AbstractFile.zep
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/**
* This file is part of the Phalcon Framework.
*
Expand Down Expand Up @@ -87,9 +86,14 @@ abstract class AbstractFile extends AbstractValidator
*/
public function checkUpload(<Validation> validation, string field) -> bool
{
return this->checkUploadMaxSize(validation, field) &&
this->checkUploadIsEmpty(validation, field) &&
this->checkUploadIsValid(validation, field);
if this->getOption("ignoreCheckUploadedFile") {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think this is necessary. The check in the protected method is enough.

return this->checkUploadMaxSize(validation, field)
&& this->checkUploadIsValid(validation, field);
}

return this->checkUploadMaxSize(validation, field)
&& this->checkUploadIsEmpty(validation, field)
&& this->checkUploadIsValid(validation, field);
}

/**
Expand All @@ -105,18 +109,44 @@ abstract class AbstractFile extends AbstractValidator
<Validation> validation,
string field
) -> bool {
var label, replacePairs, value;
var value, label, replacePairs;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This broadens the scope and has the side effect of not checking the empty/UPLOAD* stuff. It needs to be removed.


let value = validation->getValue(field);

// If the value is not an array, we consider it valid (no file provided)
if !is_array(value) {
return true;
}

// Case: ignoreCheckUploadedFile = true
if this->getOption("ignoreCheckUploadedFile") {
// Only check for minimum required keys
if isset(value["name"]) && isset(value["tmp_name"]) && value["error"] === 0 {
return true;
}

// Missing required keys -> append error message
let label = this->prepareLabel(validation, field);
let replacePairs = [":field" : label];

validation->appendMessage(
new Message(
strtr(this->getMessageFileEmpty(), replacePairs),
field,
get_class(this),
this->prepareCode(field)
)
);

return false;
}

// Case: Normal strict validation
if (
is_array(value) &&
(
true !== isset(value["error"]) ||
true !== isset(value["tmp_name"]) ||
value["error"] !== UPLOAD_ERR_OK ||
true !== this->checkIsUploadedFile(value["tmp_name"])
)
!isset(value["error"]) ||
!isset(value["tmp_name"]) ||
value["error"] !== UPLOAD_ERR_OK ||
!this->checkIsUploadedFile(value["tmp_name"])
) {
let label = this->prepareLabel(validation, field);
let replacePairs = [
Expand Down Expand Up @@ -205,6 +235,11 @@ abstract class AbstractFile extends AbstractValidator
let method = "GET";
let length = 0;

// Skip all max size and POST-size checks when ignoreCheckUploadedFile is true
if this->getOption("ignoreCheckUploadedFile") {
return true;
}

if _SERVER {
let server = _SERVER;
}
Expand Down Expand Up @@ -388,6 +423,18 @@ abstract class AbstractFile extends AbstractValidator
*/
protected function checkIsUploadedFile(string name) -> bool
{
/**
* When ignoreCheckUploadedFile = true,
* skip the system-level check (is_uploaded_file)
* but still ensure that the file physically exists.
*
* This allows CLI / test environments to validate
* fake uploads safely.
*/
if this->getOption("ignoreCheckUploadedFile") {
return file_exists(name);
}

return is_uploaded_file(name);
}
}
68 changes: 68 additions & 0 deletions tests/unit/Filter/Validation/Validator/FileCest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we change this to a PHPUnit test? Apologies, we have changed the testing framework after your PR and it did take a long time to review this :/


/**
* This file is part of the Phalcon Framework.
*
* (c) Phalcon Team <team@phalcon.io>
*
* For the full copyright and license information, please view the
* LICENSE.txt file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Phalcon\Tests\Unit\Filter\Validation\Validator;

use Phalcon\Filter\Validation;
use Phalcon\Filter\Validation\Validator\File;
use UnitTester;

class FileCest
{
/**
* Tests File validator with `ignoreCheckUploadedFile = true`
*
* This test creates a temporary file instead of relying on
* a static file under `tests/_data` to make it fully
* self-contained and environment independent.
*
* @param UnitTester $I
*/
public function fileIgnoreCheckUploadedFile(UnitTester $I): void
{
$I->wantToTest('Filter\Validation\Validator\File - ignoreCheckUploadedFile works');

$validation = new Validation();
$validation->add(
'file',
new File([
'maxSize' => '2M',
'ignoreCheckUploadedFile' => true,
'allowEmpty' => false,
'types' => ['text/plain'],
])
);

// Create a temporary file to simulate an uploaded file
$tmpFile = tempnam(sys_get_temp_dir(), 'phalcon_test_');
file_put_contents($tmpFile, 'dummy content');

// Fake uploaded file array
$fakeFile = [
'name' => 'test.txt',
'type' => 'text/plain',
'tmp_name' => $tmpFile,
'error' => 0,
'size' => filesize($tmpFile),
];

$messages = $validation->validate([
'file' => $fakeFile,
]);

$I->assertEmpty($messages);

// Clean up the temporary file
@unlink($tmpFile);
}
}
Loading