diff --git a/control/Director.php b/control/Director.php index 3ea35b395..1c9373fef 100644 --- a/control/Director.php +++ b/control/Director.php @@ -245,7 +245,7 @@ class Director implements TemplateGlobalProvider { Requirements::set_backend(new Requirements_Backend()); // Handle absolute URLs - if (@parse_url($url, PHP_URL_HOST) != '') { + if (parse_url($url, PHP_URL_HOST)) { $bits = parse_url($url); // If a port is mentioned in the absolute URL, be sure to add that into the // HTTP host diff --git a/dev/Debug.php b/dev/Debug.php index 46a0c430f..65d1ed8e6 100644 --- a/dev/Debug.php +++ b/dev/Debug.php @@ -401,7 +401,12 @@ class Debug { $reporter = self::create_debug_view(); // Coupling alert: This relies on knowledge of how the director gets its URL, it could be improved. - $httpRequest = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : @$_REQUEST['url']; + $httpRequest = null; + if(isset($_SERVER['REQUEST_URI'])) { + $httpRequest = $_SERVER['REQUEST_URI']; + } elseif(isset($_REQUEST['url'])) { + $httpRequest = $_REQUEST['url']; + } if(isset($_SERVER['REQUEST_METHOD'])) $httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest; $reporter->writeHeader($httpRequest); diff --git a/dev/FixtureBlueprint.php b/dev/FixtureBlueprint.php index e74bdee11..532c16b56 100644 --- a/dev/FixtureBlueprint.php +++ b/dev/FixtureBlueprint.php @@ -128,27 +128,48 @@ class FixtureBlueprint { // Populate all relations if($data) foreach($data as $fieldName => $fieldVal) { if($obj->many_many($fieldName) || $obj->has_many($fieldName)) { + $obj->write(); + $parsedItems = array(); - $items = preg_split('/ *, */',trim($fieldVal)); - foreach($items as $item) { - // Check for correct format: =>.. - // Ignore if the item has already been replaced with a numeric DB identifier - if(!is_numeric($item) && !preg_match('/^=>[^\.]+\.[^\.]+/', $item)) { - throw new InvalidArgumentException(sprintf( - 'Invalid format for relation "%s" on class "%s" ("%s")', - $fieldName, - $class, - $item - )); + + if(is_array($fieldVal)) { + // handle lists of many_many relations. Each item can + // specify the many_many_extraFields against each + // related item. + foreach($fieldVal as $relVal) { + $item = key($relVal); + $id = $this->parseValue($item, $fixtures); + $parsedItems[] = $id; + + array_shift($relVal); + + $obj->getManyManyComponents($fieldName)->add( + $id, $relVal + ); + } + } else { + $items = preg_split('/ *, */',trim($fieldVal)); + + foreach($items as $item) { + // Check for correct format: =>.. + // Ignore if the item has already been replaced with a numeric DB identifier + if(!is_numeric($item) && !preg_match('/^=>[^\.]+\.[^\.]+/', $item)) { + throw new InvalidArgumentException(sprintf( + 'Invalid format for relation "%s" on class "%s" ("%s")', + $fieldName, + $class, + $item + )); + } + + $parsedItems[] = $this->parseValue($item, $fixtures); } - $parsedItems[] = $this->parseValue($item, $fixtures); - } - $obj->write(); - if($obj->has_many($fieldName)) { - $obj->getComponents($fieldName)->setByIDList($parsedItems); - } elseif($obj->many_many($fieldName)) { - $obj->getManyManyComponents($fieldName)->setByIDList($parsedItems); + if($obj->has_many($fieldName)) { + $obj->getComponents($fieldName)->setByIDList($parsedItems); + } elseif($obj->many_many($fieldName)) { + $obj->getManyManyComponents($fieldName)->setByIDList($parsedItems); + } } } elseif($obj->has_one($fieldName)) { // Sets has_one with relation name diff --git a/dev/Log.php b/dev/Log.php index ef70dba89..ade21604e 100644 --- a/dev/Log.php +++ b/dev/Log.php @@ -167,8 +167,8 @@ class SS_Log { $message = array( 'errno' => '', 'errstr' => $message, - 'errfile' => @$lastTrace['file'], - 'errline' => @$lastTrace['line'], + 'errfile' => isset($lastTrace['file']) ? $lastTrace['file'] : null, + 'errline' => isset($lastTrace['line']) ? $lastTrace['line'] : null, 'errcontext' => $trace ); } diff --git a/dev/LogErrorEmailFormatter.php b/dev/LogErrorEmailFormatter.php index 370a6d5e5..b9213005b 100644 --- a/dev/LogErrorEmailFormatter.php +++ b/dev/LogErrorEmailFormatter.php @@ -23,6 +23,9 @@ class SS_LogErrorEmailFormatter implements Zend_Log_Formatter_Interface { $errorType = 'Notice'; $colour = 'grey'; break; + default: + $errorType = $event['priorityName']; + $colour = 'grey'; } if(!is_array($event['message'])) { @@ -63,8 +66,8 @@ class SS_LogErrorEmailFormatter implements Zend_Log_Formatter_Interface { $relfile = Director::makeRelative($errfile); if($relfile && $relfile[0] == '/') $relfile = substr($relfile, 1); - $host = @$_SERVER['HTTP_HOST']; - $uri = @$_SERVER['REQUEST_URI']; + $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null; + $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null; $subject = "[$errorType] in $relfile:{$errline} (http://{$host}{$uri})"; diff --git a/dev/LogErrorFileFormatter.php b/dev/LogErrorFileFormatter.php index 9b4f82f9b..5a64d8ca7 100644 --- a/dev/LogErrorFileFormatter.php +++ b/dev/LogErrorFileFormatter.php @@ -15,7 +15,7 @@ class SS_LogErrorFileFormatter implements Zend_Log_Formatter_Interface { $errfile = $event['message']['errfile']; $errline = $event['message']['errline']; $errcontext = $event['message']['errcontext']; - + switch($event['priorityName']) { case 'ERR': $errtype = 'Error'; @@ -26,6 +26,8 @@ class SS_LogErrorFileFormatter implements Zend_Log_Formatter_Interface { case 'NOTICE': $errtype = 'Notice'; break; + default: + $errtype = $event['priorityName']; } $urlSuffix = ''; diff --git a/dev/SapphireTest.php b/dev/SapphireTest.php index 8fe025ffa..81d67f499 100644 --- a/dev/SapphireTest.php +++ b/dev/SapphireTest.php @@ -393,11 +393,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase { return $id; } - + /** * Return all of the IDs in the fixture of a particular class name. * Will collate all IDs form all fixtures if multiple fixtures are provided. - * + * * @param string $className * @return A map of fixture-identifier => object-id */ @@ -407,9 +407,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase { /** * Get an object from the fixture. - * - * @param $className The data class, as specified in your fixture file. Parent classes won't work - * @param $identifier The identifier string, as provided in your fixture file + * + * @param string $className The data class, as specified in your fixture file. Parent classes won't work + * @param string $identifier The identifier string, as provided in your fixture file + * + * @return DataObject */ protected function objFromFixture($className, $identifier) { $obj = $this->getFixtureFactory()->get($className, $identifier); diff --git a/dev/install/install.php5 b/dev/install/install.php5 index 5c1cd8d2f..dd4d125ba 100755 --- a/dev/install/install.php5 +++ b/dev/install/install.php5 @@ -395,11 +395,15 @@ class InstallRequirements { */ function findWebserver() { // Try finding from SERVER_SIGNATURE or SERVER_SOFTWARE - $webserver = @$_SERVER['SERVER_SIGNATURE']; - if(!$webserver) $webserver = @$_SERVER['SERVER_SOFTWARE']; + if(!empty($_SERVER['SERVER_SIGNATURE'])) { + $webserver = $_SERVER['SERVER_SIGNATURE']; + } elseif(!empty($_SERVER['SERVER_SOFTWARE'])) { + $webserver = $_SERVER['SERVER_SOFTWARE']; + } else { + return false; + } - if($webserver) return strip_tags(trim($webserver)); - else return false; + return strip_tags(trim($webserver)); } /** @@ -1125,7 +1129,7 @@ class InstallRequirements { $this->testing($testDetails); return true; } else { - if(!@$result['cannotCreate']) { + if(empty($result['cannotCreate'])) { $testDetails[2] .= ". Please create the database manually."; } else { $testDetails[2] .= " (user '$databaseConfig[username]' doesn't have CREATE DATABASE permissions.)"; @@ -1217,7 +1221,7 @@ class InstallRequirements { $section = $testDetails[0]; $test = $testDetails[1]; - $this->tests[$section][$test] = array("error", @$testDetails[2]); + $this->tests[$section][$test] = array("error", isset($testDetails[2]) ? $testDetails[2] : null); $this->errors[] = $testDetails; } @@ -1225,7 +1229,7 @@ class InstallRequirements { $section = $testDetails[0]; $test = $testDetails[1]; - $this->tests[$section][$test] = array("warning", @$testDetails[2]); + $this->tests[$section][$test] = array("warning", isset($testDetails[2]) ? $testDetails[2] : null); $this->warnings[] = $testDetails; } diff --git a/docs/en/changelogs/3.0.9.md b/docs/en/changelogs/3.0.9.md index 089cde8f8..d734f9f61 100644 --- a/docs/en/changelogs/3.0.9.md +++ b/docs/en/changelogs/3.0.9.md @@ -2,34 +2,11 @@ ## Overview -### Default current Versioned "stage" to "Live" rather than "Stage" + * Security: Require ADMIN for ?flush=1&isDev=1 ([SS-2014-001](http://www.silverstripe.org/ss-2014-001-require-admin-for-flush1-and-isdev1)) + * Security: XSS in third party library (SWFUpload) ([SS-2014-002](http://www.silverstripe.org/ss-2014-002-xss-in-third-party-library-swfupload/)) -Previously only the controllers responsible for page and CMS display -(`LeftAndMain` and `ContentController`) explicitly set a stage through -`Versioned::choose_site_stage()`. Unless this method is called, -the default stage will be "Stage", showing draft content. -Any direct subclasses of `Controller` interacting with "versioned" objects -are vulnerable to exposing unpublished content, unless `choose_site_stage()` -is called explicitly in their own logic. +## Changelog -In order to provide more secure default behaviour, we have changed -`choose_site_stage()` to be called on all requests, defaulting to the "Live" stage. -If your logic relies on querying draft content, use `Versioned::reading_stage('Stage')`. - -Important: The `choose_site_stage()` call only deals with setting the default stage, -and doesn't check if the user is authenticated to view it. As with any other controller logic, -please use `DataObject->canView()` to determine permissions. - - :::php - class MyController extends Controller { - private static $allowed_actions = array('showpage'); - public function showpage($request) { - $page = Page::get()->byID($request->param('ID')); - if(!$page->canView()) return $this->httpError(401); - // continue with authenticated logic... - } - } - -### API Changes - - * 2013-08-03 [0e7231f](https://github.com/silverstripe/sapphire/commit/0e7231f) Disable discontinued Google Spellcheck in TinyMCE (Ingo Schommer) \ No newline at end of file + * [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.9) + * [cms](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.9) + * [installer](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.9) \ No newline at end of file diff --git a/docs/en/changelogs/3.1.3.md b/docs/en/changelogs/3.1.3.md index 7cb25627a..d43eb42fb 100644 --- a/docs/en/changelogs/3.1.3.md +++ b/docs/en/changelogs/3.1.3.md @@ -2,6 +2,9 @@ ## Overview + * Security: Require ADMIN for ?flush=1&isDev=1 ([SS-2014-001](http://www.silverstripe.org/ss-2014-001-require-admin-for-flush1-and-isdev1)) + * Security: XSS in third party library (SWFUpload) ([SS-2014-002](http://www.silverstripe.org/ss-2014-002-xss-in-third-party-library-swfupload/)) + * Security: SiteTree.ExtraMeta allows JavaScript for malicious CMS authors ([SS-2014-003](http://www.silverstripe.org/ss-2014-003-extrameta-allows-javascript-for-malicious-cms-authors-/)) * Better loading performance when using multiple `UploadField` instances * Option for `force_js_to_bottom` on `Requirements` class (ignoring inline `