updated core to 7.58, security update

This commit is contained in:
2018-04-24 23:28:54 +02:00
parent 83a28c5dc3
commit dc21b82664
357 changed files with 10830 additions and 2674 deletions
+572 -1
View File
@@ -22,7 +22,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
$modules[] = 'file';
$modules[] = 'file_module_test';
parent::setUp($modules);
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access'));
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access', 'administer fields'));
$this->drupalLogin($this->admin_user);
}
@@ -218,6 +218,30 @@ class FileFieldTestCase extends DrupalWebTestCase {
$message = isset($message) ? $message : format_string('File %file is permanent.', array('%file' => $file->uri));
$this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message);
}
/**
* Creates a temporary file, for a specific user.
*
* @param string $data
* A string containing the contents of the file.
* @param int $uid
* The user ID of the file owner.
*
* @return object
* A file object, or FALSE on error.
*/
function createTemporaryFile($data, $uid = NULL) {
$file = file_save_data($data, NULL, NULL);
if ($file) {
$file->uid = isset($uid) ? $uid : $this->admin_user->uid;
// Change the file status to be temporary.
$file->status = NULL;
return file_save($file);
}
return $file;
}
}
/**
@@ -377,6 +401,18 @@ class FileManagedFileElementTestCase extends FileFieldTestCase {
$this->drupalPost($path, array(), t('Save'));
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), 'Submitted without a file.');
// Submit with a file, but with an invalid form token. Ensure the file
// was not saved.
$last_fid_prior = $this->getLastFileId();
$edit = array(
'files[' . $input_base_name . ']' => drupal_realpath($test_file->uri),
'form_token' => 'invalid token',
);
$this->drupalPost($path, $edit, t('Save'));
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
$last_fid = $this->getLastFileId();
$this->assertEqual($last_fid_prior, $last_fid, 'File was not saved when uploaded with an invalid form token.');
// Submit a new file, without using the Upload button.
$last_fid_prior = $this->getLastFileId();
$edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
@@ -474,6 +510,15 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
$this->assertFileExists($node_file, 'New file saved to disk on node creation.');
// Test that running field_attach_update() leaves the file intact.
$field = new stdClass();
$field->type = $type_name;
$field->nid = $nid;
field_attach_update('node', $field);
$node = node_load($nid);
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
$this->assertFileExists($node_file, 'New file still saved to disk on field update.');
// Ensure the file can be downloaded.
$this->drupalGet(file_create_url($node_file->uri));
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
@@ -505,6 +550,170 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
}
}
/**
* Tests exploiting the temporary file removal of another user using fid.
*/
function testTemporaryFileRemovalExploit() {
// Create a victim user.
$victim_user = $this->drupalCreateUser();
// Create an attacker user.
$attacker_user = $this->drupalCreateUser(array(
'access content',
'create page content',
'edit any page content',
));
// Log in as the attacker user.
$this->drupalLogin($attacker_user);
// Perform tests using the newly created users.
$this->doTestTemporaryFileRemovalExploit($victim_user->uid, $attacker_user->uid);
}
/**
* Tests exploiting the temporary file removal for anonymous users using fid.
*/
public function testTemporaryFileRemovalExploitAnonymous() {
// Set up an anonymous victim user.
$victim_uid = 0;
// Set up an anonymous attacker user.
$attacker_uid = 0;
// Set up permissions for anonymous attacker user.
user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
'access content' => TRUE,
'create page content' => TRUE,
'edit any page content' => TRUE,
));
// In order to simulate being the anonymous attacker user, we need to log
// out here since setUp() has logged in the admin.
$this->drupalLogout();
// Perform tests using the newly set up users.
$this->doTestTemporaryFileRemovalExploit($victim_uid, $attacker_uid);
}
/**
* Tests validation with the Upload button.
*/
function testWidgetValidation() {
$type_name = 'article';
$field_name = strtolower($this->randomName());
$this->createFileField($field_name, $type_name);
$this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
foreach (array('nojs', 'js') as $type) {
// Create node and prepare files for upload.
$node = $this->drupalCreateNode(array('type' => 'article'));
$nid = $node->nid;
$this->drupalGet("node/$nid/edit");
$test_file_text = $this->getTestFile('text');
$test_file_image = $this->getTestFile('image');
$field = field_info_field($field_name);
$name = 'files[' . $field_name . '_' . LANGUAGE_NONE . '_0]';
// Upload file with incorrect extension, check for validation error.
$edit[$name] = drupal_realpath($test_file_image->uri);
switch ($type) {
case 'nojs':
$this->drupalPost(NULL, $edit, t('Upload'));
break;
case 'js':
$button = $this->xpath('//input[@type="submit" and @value="' . t('Upload') . '"]');
$this->drupalPostAJAX(NULL, $edit, array((string) $button[0]['name'] => (string) $button[0]['value']));
break;
}
$error_message = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => 'txt'));
$this->assertRaw($error_message, t('Validation error when file with wrong extension uploaded (JSMode=%type).', array('%type' => $type)));
// Upload file with correct extension, check that error message is removed.
$edit[$name] = drupal_realpath($test_file_text->uri);
switch ($type) {
case 'nojs':
$this->drupalPost(NULL, $edit, t('Upload'));
break;
case 'js':
$button = $this->xpath('//input[@type="submit" and @value="' . t('Upload') . '"]');
$this->drupalPostAJAX(NULL, $edit, array((string) $button[0]['name'] => (string) $button[0]['value']));
break;
}
$this->assertNoRaw($error_message, t('Validation error removed when file with correct extension uploaded (JSMode=%type).', array('%type' => $type)));
}
}
/**
* Helper for testing exploiting the temporary file removal using fid.
*
* @param int $victim_uid
* The victim user ID.
* @param int $attacker_uid
* The attacker user ID.
*/
protected function doTestTemporaryFileRemovalExploit($victim_uid, $attacker_uid) {
// Use 'page' instead of 'article', so that the 'article' image field does
// not conflict with this test. If in the future the 'page' type gets its
// own default file or image field, this test can be made more robust by
// using a custom node type.
$type_name = 'page';
$field_name = 'test_file_field';
$this->createFileField($field_name, $type_name);
$test_file = $this->getTestFile('text');
foreach (array('nojs', 'js') as $type) {
// Create a temporary file owned by the anonymous victim user. This will be
// as if they had uploaded the file, but not saved the node they were
// editing or creating.
$victim_tmp_file = $this->createTemporaryFile('some text', $victim_uid);
$victim_tmp_file = file_load($victim_tmp_file->fid);
$this->assertTrue($victim_tmp_file->status != FILE_STATUS_PERMANENT, 'New file saved to disk is temporary.');
$this->assertFalse(empty($victim_tmp_file->fid), 'New file has a fid');
$this->assertEqual($victim_uid, $victim_tmp_file->uid, 'New file belongs to the victim user');
// Have attacker create a new node with a different uploaded file and
// ensure it got uploaded successfully.
// @todo Can we test AJAX? See https://www.drupal.org/node/2538260
$edit = array(
'title' => $type . '-title',
);
// Attach a file to a node.
$langcode = LANGUAGE_NONE;
$edit['files[' . $field_name . '_' . $langcode . '_0]'] = drupal_realpath($test_file->uri);
$this->drupalPost("node/add/$type_name", $edit, 'Save');
$node = $this->drupalGetNodeByTitle($edit['title']);
$node_file = file_load($node->{$field_name}[$langcode][0]['fid']);
$this->assertFileExists($node_file, 'New file saved to disk on node creation.');
$this->assertEqual($attacker_uid, $node_file->uid, 'New file belongs to the attacker.');
// Ensure the file can be downloaded.
$this->drupalGet(file_create_url($node_file->uri));
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
// "Click" the remove button (emulating either a nojs or js submission).
// In this POST request, the attacker "guesses" the fid of the victim's
// temporary file and uses that to remove this file.
$this->drupalGet('node/' . $node->nid . '/edit');
switch ($type) {
case 'nojs':
$this->drupalPost(NULL, array("{$field_name}[$langcode][0][fid]" => (string) $victim_tmp_file->fid), 'Remove');
break;
case 'js':
$button = $this->xpath('//input[@type="submit" and @value="Remove"]');
$this->drupalPostAJAX(NULL, array("{$field_name}[$langcode][0][fid]" => (string) $victim_tmp_file->fid), array((string) $button[0]['name'] => (string) $button[0]['value']));
break;
}
// The victim's temporary file should not be removed by the attacker's
// POST request.
$this->assertFileExists($victim_tmp_file);
}
}
/**
* Tests upload and remove buttons for multiple multi-valued File fields.
*/
@@ -877,6 +1086,7 @@ class FileFieldDisplayTestCase extends FileFieldTestCase {
$field_settings = array(
'display_field' => '1',
'display_default' => '1',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
);
$instance_settings = array(
'description_field' => '1',
@@ -917,6 +1127,45 @@ class FileFieldDisplayTestCase extends FileFieldTestCase {
$this->assertNoRaw($default_output, 'Field is hidden when "display" option is unchecked.');
// Test that fields appear as expected during the preview.
// Add a second file.
$name = 'files[' . $field_name . '_' . LANGUAGE_NONE . '_1]';
$edit[$name] = drupal_realpath($test_file->uri);
// Uncheck the display checkboxes and go to the preview.
$edit[$field_name . '[' . LANGUAGE_NONE . '][0][display]'] = FALSE;
$edit[$field_name . '[' . LANGUAGE_NONE . '][1][display]'] = FALSE;
$this->drupalPost('node/' . $nid . '/edit', $edit, t('Preview'));
$this->assertRaw($field_name . '[' . LANGUAGE_NONE . '][0][display]', 'First file appears as expected.');
$this->assertRaw($field_name . '[' . LANGUAGE_NONE . '][1][display]', 'Second file appears as expected.');
}
/**
* Tests default display of File Field.
*/
function testDefaultFileFieldDisplay() {
$field_name = strtolower($this->randomName());
$type_name = 'article';
$field_settings = array(
'display_field' => '1',
'display_default' => '0',
);
$instance_settings = array(
'description_field' => '1',
);
$widget_settings = array();
$this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
$field = field_info_field($field_name);
$instance = field_info_instance('node', $field_name, $type_name);
$test_file = $this->getTestFile('text');
// Create a new node with the uploaded file.
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
$this->drupalGet('node/' . $nid . '/edit');
$this->assertFieldByXPath('//input[@type="checkbox" and @name="' . $field_name . '[und][0][display]"]', NULL, 'Default file display checkbox field exists.');
$this->assertFieldByXPath('//input[@type="checkbox" and @name="' . $field_name . '[und][0][display]" and not(@checked)]', NULL, 'Default file display is off.');
}
}
@@ -1302,5 +1551,327 @@ class FilePrivateTestCase extends FileFieldTestCase {
$this->assertNoRaw($node_file->filename, 'File without view field access permission does not appear after attempting to attach it to a new node.');
$this->drupalGet(file_create_url($node_file->uri));
$this->assertResponse(403, 'Confirmed that access is denied for the file without view field access permission after attempting to attach it to a new node.');
// As an anonymous user, create a temporary file with no references and
// confirm that only the session that uploaded it may view it.
$this->drupalLogout();
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(
"create $type_name content",
'access content',
));
$test_file = $this->getTestFile('text');
$this->drupalGet('node/add/' . $type_name);
$edit = array('files[' . $field_name . '_' . LANGUAGE_NONE . '_0]' => drupal_realpath($test_file->uri));
$this->drupalPost(NULL, $edit, t('Upload'));
$files = file_load_multiple(array(), array('uid' => 0));
$this->assertEqual(1, count($files), 'Loaded one anonymous file.');
$file = end($files);
$this->assertNotEqual($file->status, FILE_STATUS_PERMANENT, 'File is temporary.');
$usage = file_usage_list($file);
$this->assertFalse($usage, 'No file usage found.');
$file_url = file_create_url($file->uri);
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the temporary file.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->cookies = array();
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the temporary file.');
// As an anonymous user, create a permanent file that is referenced by a
// published node and confirm that all anonymous users may view it.
$test_file = $this->getTestFile('text');
$this->drupalGet('node/add/' . $type_name);
$edit = array();
$edit['title'] = $this->randomName();
$edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($test_file->uri);
$this->drupalPost(NULL, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title']);
$file = file_load($new_node->{$field_name}[LANGUAGE_NONE][0]['fid']);
$this->assertEqual($file->status, FILE_STATUS_PERMANENT, 'File is permanent.');
$usage = file_usage_list($file);
$this->assertTrue($usage, 'File usage found.');
$file_url = file_create_url($file->uri);
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the permanent file that is referenced by a published node.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->cookies = array();
$this->drupalGet($file_url);
$this->assertResponse(200, 'Confirmed that another anonymous user also has access to the permanent file that is referenced by a published node.');
// As an anonymous user, create a permanent file that is referenced by an
// unpublished node and confirm that no anonymous users may view it (even
// the session that uploaded the file) because they cannot view the
// unpublished node.
$test_file = $this->getTestFile('text');
$this->drupalGet('node/add/' . $type_name);
$edit = array();
$edit['title'] = $this->randomName();
$edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($test_file->uri);
$this->drupalPost(NULL, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title']);
$new_node->status = NODE_NOT_PUBLISHED;
node_save($new_node);
$file = file_load($new_node->{$field_name}[LANGUAGE_NONE][0]['fid']);
$this->assertEqual($file->status, FILE_STATUS_PERMANENT, 'File is permanent.');
$usage = file_usage_list($file);
$this->assertTrue($usage, 'File usage found.');
$file_url = file_create_url($file->uri);
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that the anonymous uploader cannot access the permanent file when it is referenced by an unpublished node.');
// Close the prior connection and remove the session cookie.
$this->curlClose();
$this->cookies = array();
$this->drupalGet($file_url);
$this->assertResponse(403, 'Confirmed that another anonymous user cannot access the permanent file when it is referenced by an unpublished node.');
}
/**
* Tests file access for private nodes when file download access is granted.
*/
function testPrivateFileDownloadAccessGranted() {
// Tell file_module_test to attempt to grant access to all private files,
// and ensure that it is doing so correctly.
$test_file = $this->getTestFile('text');
$uri = file_unmanaged_move($test_file->uri, 'private://');
$file_url = file_create_url($uri);
$this->drupalGet($file_url);
$this->assertResponse(403, 'Access is not granted to an arbitrary private file by default.');
variable_set('file_module_test_grant_download_access', TRUE);
$this->drupalGet($file_url);
$this->assertResponse(200, 'Access is granted to an arbitrary private file after a module grants access to all private files in hook_file_download().');
// Create a public node with a file attached.
$type_name = 'page';
$field_name = strtolower($this->randomName());
$this->createFileField($field_name, $type_name, array('uri_scheme' => 'private'));
$test_file = $this->getTestFile('text');
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => FALSE));
$node = node_load($nid, NULL, TRUE);
$file_url = file_create_url($node->{$field_name}[LANGUAGE_NONE][0]['uri']);
// Unpublish the node and ensure that only administrators (not anonymous
// users) can access the node and download the file; the expectation is
// that the File module's hook_file_download() implementation will deny
// access and thereby override the file_module_test module's access grant.
$node->status = NODE_NOT_PUBLISHED;
node_save($node);
$this->drupalLogin($this->admin_user);
$this->drupalGet("node/$nid");
$this->assertResponse(200, 'Administrator can access the unpublished node.');
$this->drupalGet($file_url);
$this->assertResponse(200, 'Administrator can download the file attached to the unpublished node.');
$this->drupalLogOut();
$this->drupalGet("node/$nid");
$this->assertResponse(403, 'Anonymous user cannot access the unpublished node.');
$this->drupalGet($file_url);
$this->assertResponse(403, 'Anonymous user cannot download the file attached to the unpublished node.');
// Re-publish the node and ensure that the node and file can be accessed by
// everyone.
$node->status = NODE_PUBLISHED;
node_save($node);
$this->drupalLogin($this->admin_user);
$this->drupalGet("node/$nid");
$this->assertResponse(200, 'Administrator can access the published node.');
$this->drupalGet($file_url);
$this->assertResponse(200, 'Administrator can download the file attached to the published node.');
$this->drupalLogOut();
$this->drupalGet("node/$nid");
$this->assertResponse(200, 'Anonymous user can access the published node.');
$this->drupalGet($file_url);
$this->assertResponse(200, 'Anonymous user can download the file attached to the published node.');
// Make the node private via the node access system and test that only
// administrators (not anonymous users) can access the node and download
// the file.
$node->private = TRUE;
node_save($node);
$this->drupalLogin($this->admin_user);
$this->drupalGet("node/$nid");
$this->assertResponse(200, 'Administrator can access the private node.');
$this->drupalGet($file_url);
$this->assertResponse(200, 'Administrator can download the file attached to the private node.');
$this->drupalLogOut();
$this->drupalGet("node/$nid");
$this->assertResponse(403, 'Anonymous user cannot access the private node.');
$this->drupalGet($file_url);
$this->assertResponse(403, 'Anonymous user cannot download the file attached to the private node.');
}
}
/**
* Confirm that file field submissions work correctly for anonymous visitors.
*/
class FileFieldAnonymousSubmission extends FileFieldTestCase {
public static function getInfo() {
return array(
'name' => 'File form anonymous submission',
'description' => 'Test anonymous form submission.',
'group' => 'File',
);
}
function setUp() {
parent::setUp();
// Allow node submissions by anonymous users.
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(
'create article content',
'access content',
));
}
/**
* Tests the basic node submission for an anonymous visitor.
*/
function testAnonymousNode() {
$bundle_label = 'Article';
$node_title = 'Test page';
// Load the node form.
$this->drupalGet('node/add/article');
$this->assertResponse(200, 'Loaded the article node form.');
$this->assertText(strip_tags(t('Create @name', array('@name' => $bundle_label))));
$edit = array(
'title' => $node_title,
'body[und][0][value]' => 'Test article',
'body[und][0][format]' => 'filtered_html',
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertResponse(200);
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$matches = array();
if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) {
$nid = end($matches);
$this->assertNotEqual($nid, 0, 'The node ID was extracted from the URL.');
$node = node_load($nid);
$this->assertNotEqual($node, NULL, 'The node was loaded successfully.');
}
}
/**
* Tests file submission for an anonymous visitor.
*/
function testAnonymousNodeWithFile() {
$bundle_label = 'Article';
$node_title = 'Test page';
// Load the node form.
$this->drupalGet('node/add/article');
$this->assertResponse(200, 'Loaded the article node form.');
$this->assertText(strip_tags(t('Create @name', array('@name' => $bundle_label))));
// Generate an image file.
$image = $this->getTestImage();
// Submit the form.
$edit = array(
'title' => $node_title,
'body[und][0][value]' => 'Test article',
'body[und][0][format]' => 'filtered_html',
'files[field_image_und_0]' => drupal_realpath($image->uri),
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertResponse(200);
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$matches = array();
if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) {
$nid = end($matches);
$this->assertNotEqual($nid, 0, 'The node ID was extracted from the URL.');
$node = node_load($nid);
$this->assertNotEqual($node, NULL, 'The node was loaded successfully.');
$this->assertEqual($node->field_image[LANGUAGE_NONE][0]['filename'], $image->filename, 'The image was uploaded successfully.');
}
}
/**
* Tests file submission for an anonymous visitor with a missing node title.
*/
function testAnonymousNodeWithFileWithoutTitle() {
$this->drupalLogout();
$this->_testNodeWithFileWithoutTitle();
}
/**
* Tests file submission for an authenticated user with a missing node title.
*/
function testAuthenticatedNodeWithFileWithoutTitle() {
$admin_user = $this->drupalCreateUser(array(
'bypass node access',
'access content overview',
'administer nodes',
));
$this->drupalLogin($admin_user);
$this->_testNodeWithFileWithoutTitle();
}
/**
* Helper method to test file submissions with missing node titles.
*/
protected function _testNodeWithFileWithoutTitle() {
$bundle_label = 'Article';
$node_title = 'Test page';
// Load the node form.
$this->drupalGet('node/add/article');
$this->assertResponse(200, 'Loaded the article node form.');
$this->assertText(strip_tags(t('Create @name', array('@name' => $bundle_label))));
// Generate an image file.
$image = $this->getTestImage();
// Submit the form but exclude the title field.
$edit = array(
'body[und][0][value]' => 'Test article',
'body[und][0][format]' => 'filtered_html',
'files[field_image_und_0]' => drupal_realpath($image->uri),
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertResponse(200);
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertNoText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$this->assertText(t('!name field is required.', array('!name' => t('Title'))));
// Submit the form again but this time with the missing title field. This
// should still work.
$edit = array(
'title' => $node_title,
);
$this->drupalPost(NULL, $edit, t('Save'));
// Confirm the final submission actually worked.
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$matches = array();
if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) {
$nid = end($matches);
$this->assertNotEqual($nid, 0, 'The node ID was extracted from the URL.');
$node = node_load($nid);
$this->assertNotEqual($node, NULL, 'The node was loaded successfully.');
$this->assertEqual($node->field_image[LANGUAGE_NONE][0]['filename'], $image->filename, 'The image was uploaded successfully.');
}
}
/**
* Generates a test image.
*
* @return stdClass
* A file object.
*/
function getTestImage() {
// Get a file to upload.
$file = current($this->drupalGetTestFiles('image'));
// Add a filesize property to files as would be read by file_load().
$file->filesize = filesize($file->uri);
return $file;
}
}