Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
alecpl committed Aug 4, 2024
1 parent c99dcac commit e12e273
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 58 deletions.
43 changes: 0 additions & 43 deletions program/include/rcmail_output.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,47 +136,4 @@ public function add_handlers($handlers)
{
$this->object_handlers = array_merge($this->object_handlers, $handlers);
}

/**
* A wrapper for header() function, so it can be replaced for automated tests
*
* @param string $header The header string
* @param bool $replace Replace previously set header?
*/
public function header($header, $replace = true)
{
header($header, $replace);
}

/**
* A helper to send output to the browser and exit
*
* @param string $body The output body
* @param array $headers Headers
*
* @return never
*/
public function sendExit($body = '', $headers = [])
{
foreach ($headers as $header) {
header($header);
}

echo $body;
exit;
}

/**
* A helper to send HTTP error code and message to the browser, and exit.
*
* @param int $code The HTTP error code
* @param string $message The HTTP error message
*
* @return never
*/
public function sendExitError($code, $message = '')
{
http_response_code($code);
exit($message);
}
}
73 changes: 58 additions & 15 deletions program/lib/Roundcube/rcube_output.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,16 @@ public function nocacheing_headers()
return;
}

header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
$this->header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
$this->header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');

// We need to set the following headers to make downloads work using IE in HTTPS mode.
if ($this->browser->ie && rcube_utils::https_check()) {
header('Pragma: private');
header('Cache-Control: private, must-revalidate');
$this->header('Pragma: private');
$this->header('Cache-Control: private, must-revalidate');
} else {
header('Cache-Control: private, no-cache, no-store, must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');
$this->header('Cache-Control: private, no-cache, no-store, must-revalidate, post-check=0, pre-check=0');
$this->header('Pragma: no-cache');
}
}

Expand All @@ -163,9 +163,9 @@ public function future_expire_header($offset = 2600000)
return;
}

header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $offset) . ' GMT');
header("Cache-Control: max-age={$offset}");
header('Pragma: ');
$this->header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $offset) . ' GMT');
$this->header("Cache-Control: max-age={$offset}");
$this->header('Pragma: ');
}

/**
Expand Down Expand Up @@ -202,7 +202,7 @@ public function common_headers($privacy = true)
$plugin = $this->app->plugins->exec_hook('common_headers', ['headers' => $headers, 'privacy' => $privacy]);

foreach ($plugin['headers'] as $header => $value) {
header("{$header}: {$value}");
$this->header("{$header}: {$value}");
}
}

Expand Down Expand Up @@ -269,19 +269,19 @@ public function download_headers($filename, $params = [])
}
}

header("Content-Disposition: {$disposition}");
header("Content-Type: {$ctype}");
$this->header("Content-Disposition: {$disposition}");
$this->header("Content-Type: {$ctype}");

if ($params['disposition'] == 'attachment' && $this->browser->ie) {
header('Content-Type: application/force-download');
$this->header('Content-Type: application/force-download');
}

if (isset($params['length'])) {
header('Content-Length: ' . $params['length']);
$this->header('Content-Length: ' . $params['length']);
}

// Use strict security policy to make sure no javascript content is executed
header("Content-Security-Policy: default-src 'none'");
$this->header("Content-Security-Policy: default-src 'none'");

// don't kill the connection if download takes more than 30 sec.
if (!array_key_exists('time_limit', $params)) {
Expand Down Expand Up @@ -391,4 +391,47 @@ public static function json_serialize($input, $pretty = false, $inline = true)

return json_encode($input, $options);
}

/**
* A wrapper for header() function, so it can be replaced for automated tests
*
* @param string $header The header string
* @param bool $replace Replace previously set header?
*/
public function header($header, $replace = true)
{
header($header, $replace);
}

/**
* A helper to send output to the browser and exit
*
* @param string $body The output body
* @param array $headers Headers
*
* @return never
*/
public function sendExit($body = '', $headers = [])
{
foreach ($headers as $header) {
$this->header($header);
}

echo $body;
exit;
}

/**
* A helper to send HTTP error code and message to the browser, and exit.
*
* @param int $code The HTTP error code
* @param string $message The HTTP error message
*
* @return never
*/
public function sendExitError($code, $message = '')
{
http_response_code($code);
exit($message);
}
}
60 changes: 60 additions & 0 deletions tests/Framework/OutputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,72 @@
namespace Roundcube\Tests\Framework;

use PHPUnit\Framework\TestCase;
use Roundcube\Tests\OutputHtmlMock;

/**
* Test class to test rcube_output class
*/
class OutputTest extends TestCase
{
/**
* Test download_headers()
*/
public function test_download_headers()
{
$output = new OutputHtmlMock();

// Basic (empty) case
$output->reset();
$output->download_headers('test');

$this->assertCount(3, $output->headers);
$this->assertContains('Content-Disposition: attachment; filename="test"', $output->headers);
$this->assertContains('Content-Type: application/octet-stream', $output->headers);
$this->assertContains('Content-Security-Policy: default-src \'none\'', $output->headers);

// Invalid content type
$output->reset();
$params = ['type' => 'invalid'];
$output->download_headers('test', $params);

$this->assertCount(3, $output->headers);
$this->assertContains('Content-Type: application/octet-stream', $output->headers);

// Test inline disposition with type_charset
$output->reset();
$params = ['disposition' => 'inline', 'type' => 'text/plain', 'type_charset' => 'ISO-8859-1'];
$output->download_headers('test', $params);

$this->assertCount(3, $output->headers);
$this->assertContains('Content-Disposition: inline; filename="test"', $output->headers);
$this->assertContains('Content-Type: text/plain; charset=ISO-8859-1', $output->headers);

// Insecure content-type elimination for inline mode
$types = [
'application/ecmascript',
'application/javascript',
'application/javascript-1.2',
'application/x-javascript',
'application/x-jscript',
'application/xml',
'application/xhtml+xml',
'text/javascript',
'text/xml',
'text/unknown'
];

foreach ($types as $type) {
$output->reset();
$params = ['type' => $type, 'disposition' => 'inline'];
$output->download_headers('test', $params);

$this->assertContains('Content-Type: text/plain; charset=' . RCUBE_CHARSET, $output->headers, "Case:$type");
}

// TODO: More test cases
$this->markTestIncomplete();
}

/**
* Test get_edit_field()
*/
Expand Down

0 comments on commit e12e273

Please sign in to comment.