{"record":{"id":"4c77907e9f9ac65f","repo":"phacility/phabricator","slug":"mercurial-command-appears-to-contain-unsafe-inject","errorCode":null,"errorMessage":"Mercurial command appears to contain unsafe injected \"--config\" or \"--debugger\": %s","messagePattern":"Mercurial command appears to contain unsafe injected \"--config\" or \"--debugger\": (.+?)","errorType":"exception","errorClass":"DiffusionMercurialFlagInjectionException","httpStatus":null,"severity":"critical","filePath":"src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php","lineNumber":27,"sourceCode":"  }\n\n  protected function newFormattedCommand($pattern, array $argv) {\n    $args = array();\n\n    // Crudely blacklist commands which look like they may contain command\n    // injection via \"--config\" or \"--debugger\". See T13012. To do this, we\n    // print the whole command, parse it using shell rules, then examine each\n    // argument to see if it looks like \"--config\" or \"--debugger\".\n\n    $test_command = call_user_func_array(\n      'csprintf',\n      array_merge(array($pattern), $argv));\n    $test_args = id(new PhutilShellLexer())\n      ->splitArguments($test_command);\n\n    foreach ($test_args as $test_arg) {\n      if (preg_match('/^--(config|debugger)/i', $test_arg)) {\n        throw new DiffusionMercurialFlagInjectionException(\n          pht(\n            'Mercurial command appears to contain unsafe injected \"--config\" '.\n            'or \"--debugger\": %s',\n            $test_command));\n      }\n    }\n\n    // NOTE: Here, and in Git and Subversion, we override the SSH command even\n    // if the repository does not use an SSH remote, since our SSH wrapper\n    // defuses an attack against older versions of Mercurial, Git and\n    // Subversion (see T12961) and it's possible to execute this attack\n    // in indirect ways, like by using an SSH subrepo inside an HTTP repo.\n\n    $pattern = \"hg --config ui.ssh=%s {$pattern}\";\n    $args[] = $this->getSSHWrapper();\n\n    return array($pattern, array_merge($args, $argv));\n  }","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php#L9-L45","documentation":"Phabricator's Mercurial command engine rebuilds the final hg command line from its pattern plus argv (which embeds user-controlled values such as the repository remote URI), re-lexes that finished string with shell rules, and rejects the whole command if any single argument starts with --config or --debugger (case-insensitive). Those flags can make Mercurial execute attacker-controlled hooks or configuration (security task T13012), so the engine aborts by throwing DiffusionMercurialFlagInjectionException before hg ever runs.","triggerScenarios":"Any Mercurial operation (pull, push, clone, discovery) whose constructed command line contains an argument beginning with --config or --debugger. The usual carrier is a repository Remote URI such as '--config=...' or an ssh:// URI path containing '--debugger', because the URI is interpolated into commands like 'hg pull <uri>'.","commonSituations":"A malicious actor attempting the T13012 clone-URI attack; a user pasting a raw hg command line (including its --config flags) into the Diffusion Remote URI field; automation that appends hg flags to stored URIs.","solutions":["Open the repository in Diffusion, go to Manage, then URIs, and remove any '--config' or '--debugger' text from the remote URI, then retry the operation","If you genuinely need custom hg configuration, place it in the server-side repository hgrc (or Phabricator environment config) instead of the URI","If nobody intentionally added the offending flag, audit the URI edit history and treat this as an attempted command-injection attack","Never bypass or patch out this check; upgrade Phabricator so real fixes apply"],"exampleFix":"// before (Remote URI saved in Diffusion):\n--config=hooks.prechangegroup=touch%%20/tmp/pwned\n\n// after:\nhttps://hg.example.com/repo/\n// (plain URI only; put hooks in the repository hgrc on disk)","handlingStrategy":"validation","validationCode":"// before saving or acting on a Mercurial URI, reject flag-like values\nif (preg_match('/--(config|debugger)/i', (string)$uri)) {\n  throw new Exception('Refusing Mercurial URI containing --config/--debugger');\n}","typeGuard":"function isSafeMercurialArgument($arg) {\n  return is_string($arg) && !preg_match('/^--(config|debugger)/i', $arg);\n}","tryCatchPattern":"try {\n  $engine->execute();\n} catch (DiffusionMercurialFlagInjectionException $ex) {\n  // Security event: log the rejected command and the acting user; never retry with the same arguments.\n  phlog($ex);\n}","preventionTips":["Require edit permission on repository URIs so only trusted users can set them","Never build hg command lines by string concatenation: pass values as separate argv entries and pre-screen each for leading dashes","Treat any occurrence of this exception as a probe for T13012 and review URI audit logs"],"tags":["mercurial","security","command-injection","repository-uri","phabricator"],"backgroundTag":"argument-injection","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}