{"record":{"id":"8d67df8b6606d8a5","repo":"phacility/phabricator","slug":"request-includes-restricted-parameter-s-but-th","errorCode":null,"errorMessage":"Request includes restricted parameter \"%s\", but this controller (\"%s\") does not whitelist it. Refusing to serve this request because it might be part of a redirection attack.","messagePattern":"Request includes restricted parameter \"(.+?)\", but this controller \\(\"(.+?)\"\\) does not whitelist it\\. Refusing to serve this request because it might be part of a redirection attack\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/base/controller/PhabricatorController.php","lineNumber":128,"sourceCode":"\n    if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) {\n      $dark_console = PhabricatorDarkConsoleSetting::SETTINGKEY;\n      if ($user->getUserSetting($dark_console) ||\n         PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {\n        $console = new DarkConsoleCore();\n        $request->getApplicationConfiguration()->setConsole($console);\n      }\n    }\n\n    // NOTE: We want to set up the user first so we can render a real page\n    // here, but fire this before any real logic.\n    $restricted = array(\n      'code',\n    );\n    foreach ($restricted as $parameter) {\n      if ($request->getExists($parameter)) {\n        if (!$this->shouldAllowRestrictedParameter($parameter)) {\n          throw new Exception(\n            pht(\n              'Request includes restricted parameter \"%s\", but this '.\n              'controller (\"%s\") does not whitelist it. Refusing to '.\n              'serve this request because it might be part of a redirection '.\n              'attack.',\n              $parameter,\n              get_class($this)));\n        }\n      }\n    }\n\n    if ($this->shouldRequireEnabledUser()) {\n      if ($user->getIsDisabled()) {\n        $controller = new PhabricatorDisabledUserController();\n        return $this->delegateToController($controller);\n      }\n    }\n","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/base/controller/PhabricatorController.php#L110-L146","documentation":"As an anti-redirection-attack measure, PhabricatorController::handleRequest() checks a restricted parameter list (currently 'code') before any controller logic runs: if the request contains that parameter and the concrete controller does not whitelist it via shouldAllowRestrictedParameter(), the request is refused. The concern is leaked OAuth 'code' parameters being bounced through Phabricator URLs as part of redirection attacks.","triggerScenarios":"Any GET/POST to a Phabricator controller with ?code=... - an OAuth provider redirecting to an arbitrary Phabricator page instead of its callback, a custom controller that expects a 'code' parameter but never overrode shouldAllowRestrictedParameter(), or a crafted link carrying a leaked code.","commonSituations":"Custom/extension controllers accepting verification or OAuth 'code' style parameters; users pasting OAuth callback URLs onto unrelated Phabricator pages; redirect chains or referer preservation that carry ?code onto a Phabricator link.","solutions":["If your controller legitimately uses the parameter, override shouldAllowRestrictedParameter($parameter_name) to return true for 'code'","Otherwise rename your parameter to something not on the restricted list (e.g. 'token', 'id') and update callers","If unexpected, strip the ?code= query from the link that led here - nothing on that page consumes it"],"exampleFix":"// before\nfinal class MyVerifyController extends PhabricatorController {\n  public function handleRequest(AphrontRequest $request) {\n    $code = $request->getStr('code'); // request refused before this runs\n    // ...\n  }\n}\n\n// after\nfinal class MyVerifyController extends PhabricatorController {\n  public function shouldAllowRestrictedParameter($parameter_name) {\n    if ($parameter_name === 'code') {\n      return true;\n    }\n    return parent::shouldAllowRestrictedParameter($parameter_name);\n  }\n\n  public function handleRequest(AphrontRequest $request) {\n    $code = $request->getStr('code');\n    // ...\n  }\n}","handlingStrategy":"validation","validationCode":"// If your controller does not consume 'code', scrub inbound links to it:\n$uri = $request->getRequestURI();\nif (idx($uri->getQueryParams(), 'code') !== null) {\n  // Drop the restricted parameter and redirect to the clean URI\n  // instead of letting the controller prologue refuse the request.\n  $uri->setQueryParam('code', null);\n  return id(new AphrontRedirectResponse())->setURI((string)$uri);\n}","typeGuard":null,"tryCatchPattern":"// No catch: this fires in PhabricatorController::handleRequest() before\n// your code runs. The only real remedies are (a) overriding\n// shouldAllowRestrictedParameter() to whitelist 'code', or\n// (b) renaming your parameter away from the restricted list.","preventionTips":["Do not name request parameters 'code' in custom controllers unless you also override shouldAllowRestrictedParameter()","Configure OAuth providers to redirect only to their real callback endpoints, never arbitrary Phabricator pages","When linking users into Phabricator, build clean URIs without OAuth-style query parameters"],"tags":["phabricator","security","query-parameter","oauth-code","controller"],"backgroundTag":"restricted-query-parameter","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}