diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ 'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.js' => '15191c65', 'core.pkg.css' => '2fa91e14', - 'core.pkg.js' => 'dc13d4b7', + 'core.pkg.js' => '7aa5bd92', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '113e692c', 'differential.pkg.js' => 'f6d809c0', @@ -211,12 +211,12 @@ 'rsrc/externals/font/lato/lato-regular.woff' => '13d39fe2', 'rsrc/externals/font/lato/lato-regular.woff2' => '57a9f742', 'rsrc/externals/javelin/core/Event.js' => '2ee659ce', - 'rsrc/externals/javelin/core/Stratcom.js' => '6ad39b6f', + 'rsrc/externals/javelin/core/Stratcom.js' => '327f418a', 'rsrc/externals/javelin/core/__tests__/event-stop-and-kill.js' => '717554e4', 'rsrc/externals/javelin/core/__tests__/install.js' => 'c432ee85', 'rsrc/externals/javelin/core/__tests__/stratcom.js' => '88bf7313', 'rsrc/externals/javelin/core/__tests__/util.js' => 'e251703d', - 'rsrc/externals/javelin/core/init.js' => '3010e992', + 'rsrc/externals/javelin/core/init.js' => '638a4e2b', 'rsrc/externals/javelin/core/init_node.js' => 'c234aded', 'rsrc/externals/javelin/core/install.js' => '05270951', 'rsrc/externals/javelin/core/util.js' => '93cc50d6', @@ -722,7 +722,7 @@ 'javelin-install' => '05270951', 'javelin-json' => '69adf288', 'javelin-leader' => '7f243deb', - 'javelin-magical-init' => '3010e992', + 'javelin-magical-init' => '638a4e2b', 'javelin-mask' => '8a41885b', 'javelin-quicksand' => '6b8ef10b', 'javelin-reactor' => '2b8de964', @@ -735,7 +735,7 @@ 'javelin-router' => '29274e2b', 'javelin-scrollbar' => '9065f639', 'javelin-sound' => '949c0fe5', - 'javelin-stratcom' => '6ad39b6f', + 'javelin-stratcom' => '327f418a', 'javelin-tokenizer' => '8d3bc1b2', 'javelin-typeahead' => '70baed2f', 'javelin-typeahead-composite-source' => '503e17fd', @@ -1131,6 +1131,12 @@ 'javelin-dom', 'javelin-workflow', ), + '327f418a' => array( + 'javelin-install', + 'javelin-event', + 'javelin-util', + 'javelin-magical-init', + ), '358b8c04' => array( 'javelin-install', 'javelin-util', @@ -1446,12 +1452,6 @@ '69adf288' => array( 'javelin-install', ), - '6ad39b6f' => array( - 'javelin-install', - 'javelin-event', - 'javelin-util', - 'javelin-magical-init', - ), '6b8ef10b' => array( 'javelin-install', ), diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -7,9 +7,11 @@ private $canCDN; private $responseCode = 200; private $lastModified = null; - + private $contentSecurityPolicyURIs; + private $disableContentSecurityPolicy; protected $frameable; + public function setRequest($request) { $this->request = $request; return $this; @@ -19,6 +21,32 @@ return $this->request; } + final public function addContentSecurityPolicyURI($kind, $uri) { + if ($this->contentSecurityPolicyURIs === null) { + $this->contentSecurityPolicyURIs = array( + 'script' => array(), + 'connect' => array(), + 'frame' => array(), + ); + } + + if (!isset($this->contentSecurityPolicyURIs[$kind])) { + throw new Exception( + pht( + 'Unknown Content-Security-Policy URI kind "%s".', + $kind)); + } + + $this->contentSecurityPolicyURIs[$kind][] = (string)$uri; + + return $this; + } + + final public function setDisableContentSecurityPolicy($disable) { + $this->disableContentSecurityPolicy = $disable; + return $this; + } + /* -( Content )------------------------------------------------------------ */ @@ -59,9 +87,106 @@ ); } + $csp = $this->newContentSecurityPolicyHeader(); + if ($csp !== null) { + $headers[] = array('Content-Security-Policy', $csp); + } + return $headers; } + private function newContentSecurityPolicyHeader() { + if ($this->disableContentSecurityPolicy) { + return null; + } + + $csp = array(); + + $cdn = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); + if ($cdn) { + $default = $this->newContentSecurityPolicySource($cdn); + } else { + $default = "'self'"; + } + + $csp[] = "default-src {$default}"; + + // We use "data:" URIs to inline small images into CSS. This policy allows + // "data:" URIs to be used anywhere, but there doesn't appear to be a way + // to say that "data:" URIs are okay in CSS files but not in the document. + $csp[] = "img-src {$default} data:"; + + // We use inline style="..." attributes in various places, many of which + // are legitimate. We also currently use a