diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index 05d230aa71..73c4e68f00 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -24,9 +24,10 @@ abstract class AphrontResponse extends Phobject { final public function addContentSecurityPolicyURI($kind, $uri) { if ($this->contentSecurityPolicyURIs === null) { $this->contentSecurityPolicyURIs = array( - 'script' => array(), - 'connect' => array(), - 'frame' => array(), + 'script-src' => array(), + 'connect-src' => array(), + 'frame-src' => array(), + 'form-action' => array(), ); } @@ -125,14 +126,14 @@ abstract class AphrontResponse extends Phobject { // On a small number of pages, including the Stripe workflow and the // ReCAPTCHA challenge, we embed external Javascript directly. - $csp[] = $this->newContentSecurityPolicy('script', $default); + $csp[] = $this->newContentSecurityPolicy('script-src', $default); // We need to specify that we can connect to ourself in order for AJAX // requests to work. - $csp[] = $this->newContentSecurityPolicy('connect', "'self'"); + $csp[] = $this->newContentSecurityPolicy('connect-src', "'self'"); // DarkConsole and PHPAST both use frames to render some content. - $csp[] = $this->newContentSecurityPolicy('frame', "'self'"); + $csp[] = $this->newContentSecurityPolicy('frame-src', "'self'"); // This is a more modern flavor of of "X-Frame-Options" and prevents // clickjacking attacks where the page is included in a tiny iframe and @@ -152,7 +153,7 @@ abstract class AphrontResponse extends Phobject { // This can result in some trickiness with file downloads if applications // try to start downloads by submitting a dialog. Redirect to the file's // download URI instead of submitting a form to it. - $csp[] = "form-action 'self'"; + $csp[] = $this->newContentSecurityPolicy('form-action', "'self'"); // Block use of "" to change the origin of relative URIs on the page. $csp[] = "base-uri 'none'"; @@ -177,7 +178,7 @@ abstract class AphrontResponse extends Phobject { } $sources = array_unique($sources); - return "{$type}-src ".implode(' ', $sources); + return $type.' '.implode(' ', $sources); } private function newContentSecurityPolicySource($uri) { diff --git a/src/applications/auth/provider/PhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorAuthProvider.php index d655efd790..0525edad54 100644 --- a/src/applications/auth/provider/PhabricatorAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorAuthProvider.php @@ -447,6 +447,13 @@ abstract class PhabricatorAuthProvider extends Phobject { )); } + $static_response = CelerityAPI::getStaticResourceResponse(); + $static_response->addContentSecurityPolicyURI('form-action', (string)$uri); + + foreach ($this->getContentSecurityPolicyFormActions() as $csp_uri) { + $static_response->addContentSecurityPolicyURI('form-action', $csp_uri); + } + return phabricator_form( $viewer, array( @@ -505,4 +512,8 @@ abstract class PhabricatorAuthProvider extends Phobject { throw new PhutilMethodNotImplementedException(); } + protected function getContentSecurityPolicyFormActions() { + return array(); + } + } diff --git a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php index c62983de2f..2fbc637460 100644 --- a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php +++ b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php @@ -208,6 +208,9 @@ abstract class PhabricatorOAuth1AuthProvider parent::willRenderLinkedAccount($viewer, $item, $account); } + protected function getContentSecurityPolicyFormActions() { + return $this->getAdapter()->getContentSecurityPolicyFormActions(); + } /* -( Temporary Secrets )-------------------------------------------------- */ diff --git a/src/applications/phortune/provider/PhortuneStripePaymentProvider.php b/src/applications/phortune/provider/PhortuneStripePaymentProvider.php index 370f132a1f..bdaa4294b2 100644 --- a/src/applications/phortune/provider/PhortuneStripePaymentProvider.php +++ b/src/applications/phortune/provider/PhortuneStripePaymentProvider.php @@ -285,8 +285,8 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { ->addScript($src); CelerityAPI::getStaticResourceResponse() - ->addContentSecurityPolicyURI('script', $src) - ->addContentSecurityPolicyURI('frame', $src); + ->addContentSecurityPolicyURI('script-src', $src) + ->addContentSecurityPolicyURI('frame-src', $src); Javelin::initBehavior( 'stripe-payment-form', diff --git a/src/view/form/control/AphrontFormRecaptchaControl.php b/src/view/form/control/AphrontFormRecaptchaControl.php index b0559754e9..4152f7d8b7 100644 --- a/src/view/form/control/AphrontFormRecaptchaControl.php +++ b/src/view/form/control/AphrontFormRecaptchaControl.php @@ -43,9 +43,9 @@ final class AphrontFormRecaptchaControl extends AphrontFormControl { $pubkey = PhabricatorEnv::getEnvConfig('recaptcha.public-key'); CelerityAPI::getStaticResourceResponse() - ->addContentSecurityPolicyURI('script', $js) - ->addContentSecurityPolicyURI('script', 'https://www.gstatic.com/') - ->addContentSecurityPolicyURI('frame', 'https://www.google.com/'); + ->addContentSecurityPolicyURI('script-src', $js) + ->addContentSecurityPolicyURI('script-src', 'https://www.gstatic.com/') + ->addContentSecurityPolicyURI('frame-src', 'https://www.google.com/'); return array( phutil_tag( diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 8ee7566969..aca98b7205 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -584,7 +584,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView ) + $this->buildAphlictListenConfigData()); CelerityAPI::getStaticResourceResponse() - ->addContentSecurityPolicyURI('connect', $client_uri); + ->addContentSecurityPolicyURI('connect-src', $client_uri); } }