Skip to content

Commit

Permalink
Merge pull request #10 from hashbangcode/9_curl_input_bug
Browse files Browse the repository at this point in the history
9 curl input bug
  • Loading branch information
philipnorton42 authored Nov 19, 2023
2 parents 456d92d + 5bcec7e commit 91cf27c
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 58 deletions.
34 changes: 34 additions & 0 deletions src/CurlParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ class CurlParameters implements CurlParametersInterface
*/
protected $followRedirects = false;

/**
* The proxy data.
*
* @var string
*/
protected $proxy;

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -93,6 +100,15 @@ public function setHeaders(array $headers): CurlParametersInterface
return $this;
}

/**
* {@inheritdoc}
*/
public function addHeader(string $header): CurlParametersInterface
{
$this->headers[] = $header;
return $this;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -216,4 +232,22 @@ public function followRedirects() : bool
{
return $this->followRedirects;
}

/**
* {@inheritdoc}
*/
public function getProxy()
{
return $this->proxy;
}

/**
* {@inheritdoc}
*/
public function setProxy(string $proxy): CurlParametersInterface
{
$this->proxy = $proxy;
return $this;
}

}
32 changes: 32 additions & 0 deletions src/CurlParametersInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public function getUrl(): ?string;
* The URL.
*
* @return CurlParametersInterface
* The current object.
*/
public function setUrl(string $url): CurlParametersInterface;

Expand All @@ -43,9 +44,21 @@ public function getHeaders(): array;
* The headers to set.
*
* @return CurlParametersInterface
* The current object.
*/
public function setHeaders(array $headers): CurlParametersInterface;

/**
* Add a header to the header list.
*
* @param string $header
* The header to add.
*
* @return CurlParametersInterface
* The current object.
*/
public function addHeader(string $header): CurlParametersInterface;

/**
* Get the data payload of the request.
*
Expand Down Expand Up @@ -171,4 +184,23 @@ public function setFollowRedirects(bool $followRedirects): CurlParametersInterfa
* True if redirects are to be followed.
*/
public function followRedirects() : bool;

/**
* Get the proxy value.
*
* @return mixed
* The proxy value.
*/
public function getProxy();

/**
* Set the proxy value.
*
* @param mixed $proxy
* The proxy value.
*
* @return CurlParametersInterface
* The current object.
*/
public function setProxy(string $proxy): CurlParametersInterface;
}
193 changes: 143 additions & 50 deletions src/Input/CurlInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,74 +20,126 @@ public function extract(string $data): CurlParametersInterface
{
$data = trim($data);
if (strpos($data, 'curl ') !== 0) {
// @todo : refactor this into a method and a throwable error
die('not a curl command');
throw new \InvalidArgumentException('Not a CURL command.');
}

$headers = [];

$curlParameters = new CurlParameters();

// Remove the curl command from the front.
$data = substr(trim($data), 5);

// Match parameters.
$regex = '';
$regex .= '(-[a-zA-Z\-]+? \'?.*[\s]\'?)|'; // Single letter flags with values.
$regex .= '(--[a-zA-Z\-]+ \'?.*\'?[\s]?)|'; // Multi letter flags with values.
$regex .= '("?[a-z]+:\/\/.*?"+?)|'; // Address matching with double quotes.
$regex .= '(\'?[a-z]+:\/\/.*?\'+?)|'; // Address matching with single quotes.
$regex .= '(--[a-zA-Z\-]+)|'; // Multi letter flags with no value.
$regex .= '(-[a-aA-Z]+)'; // Single letter flags with no value.
preg_match_all('/' . $regex . '/', $data, $parameters);

$parameters = $parameters[0];
$parameterCount = count($parameters);
for ($i = 0; $i < $parameterCount; ++$i) {
if (preg_match('/(?:--?)([A-Za-z\-]+)(?:\s?)(.*)?/', $parameters[$i], $flagMatch) === 1) {
$flag = $flagMatch[1];
$contents = trim($flagMatch[2]);
switch ($flag) {
case 'url':
$curlParameters->setUrl($contents);
break;
case 'H':
case 'header':
case 'headers':
$contents = trim(str_replace(["'", '\\'], '', $contents));
$headers[] = $contents;
// Remove the "curl" command part from the front.
$data = substr(trim($data), 4);
$data = str_replace(" \\\n", '', $data);

$splitCommand = array_filter(preg_split('/\s--?/U', $data));

foreach ($splitCommand as $flag) {
// If a '-' is present in the first character then remove it.
if (substr_compare($flag, '-', 0, 1) === 0) {
$flag = substr($flag, 1);
}
$flag = trim($flag);

if (strpos($flag, ' ') === false) {
// If the flag contains no strings it is a single flag.
switch (strtolower($flag)) {
case 'insecure':
$curlParameters->setIsInsecure(true);
$data = str_replace($flag, '', $data);
break;
case 'X':
$curlParameters->setHttpVerb(strtoupper($contents));
case 'L':
$curlParameters->setFollowRedirects(true);
$data = str_replace($flag, '', $data);
break;
}
continue;
}

// Simple flag with a single, unquoted, parameter.
$explodedFlag = explode(' ', $flag);
if ($explodedFlag[0] !== '') {
switch ($explodedFlag[0]) {
case 'u':
case 'user':
$credentials = explode(':', $contents);
$credentials = explode(':', $explodedFlag[1]);
$curlParameters->setUsername($credentials[0]);
$curlParameters->setPassword($credentials[1]);
$data = str_replace($explodedFlag[0] . ' ' . $explodedFlag[1], '', $data);
break;
case 'data':
case 'data-ascii':
case 'd':
case 'data-raw':
case 'data-urlencode':
case 'data-binary':
$curlParameters->setHttpVerb('POST');
$contents = trim(str_replace(["'", '\\'], '', $contents));
$curlParameters->setData($contents);
case 'X':
case 'request':
$curlParameters->setHttpVerb(strtoupper($explodedFlag[1]));
$data = str_replace($explodedFlag[0] . ' ' . $explodedFlag[1], '', $data);
break;
case 'insecure':
$curlParameters->setIsInsecure(true);
case 'url':
$curlParameters->setUrl($explodedFlag[1]);
$data = str_replace($explodedFlag[0] . ' ' . $explodedFlag[1], '', $data);
break;
case 'L':
$curlParameters->setFollowRedirects(true);
case 'proxy':
$curlParameters->setProxy($explodedFlag[1]);
$data = str_replace($explodedFlag[0] . ' ' . $explodedFlag[1], '', $data);
break;
}

if (preg_match('/X(.*)/', $explodedFlag[0], $httpVerb) === 1) {
if ($httpVerb[1] !== '') {
$curlParameters->setHttpVerb(strtoupper($httpVerb[1]));
$data = str_replace($httpVerb[0], '', $data);
}
$explodedFlag[0] = str_replace($httpVerb[0], '', $explodedFlag[0]);
}

foreach (str_split($explodedFlag[0]) as $arg) {
switch ($arg) {
case 's':
// Silent.
$data = str_replace($explodedFlag[0], '', $data);
break;
case 'S':
// Show error.
$data = str_replace($explodedFlag[0], '', $data);
break;
case 'L':
// Location.
$curlParameters->setFollowRedirects(true);
$data = str_replace($explodedFlag[0], '', $data);
break;
case 'I':
// Head.
if ($curlParameters->getHttpVerb() === 'GET') {
$curlParameters->setHttpVerb('HEAD');
}
$data = str_replace($explodedFlag[0], '', $data);
break;
}
}
}

if (substr_compare($flag, '"', -1) === 0) {
// Flag contents has double quotes.
preg_match('/"([^"]+)\"/U', $flag, $flagContents);
$flagKey = trim(str_replace($flagContents[0], '', $flag));
$flagContents = $flagContents[1];

$this->extractQuotedParams($curlParameters, $flagKey, $flagContents);

$data = str_replace($flagKey . ' "' . $flagContents . '"', '', $data);
continue;
}

if (substr_compare($flag, '\'', -1) === 0) {
// Flag contents has single quotes.
preg_match('/\'([^\']+)\'/U', $flag, $flagContents);
$flagKey = trim(str_replace($flagContents[0], '', $flag));
$flagContents = $flagContents[1];

$this->extractQuotedParams($curlParameters, $flagKey, $flagContents);

$data = str_replace($flagKey . ' \'' . $flagContents . '\'', '', $data);
}
}

if (empty($curlParameters->getUrl())) {
// Find url.
$data = trim($data);
preg_match_all('/(?:[\'|"]?)https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)(?:[\'|"]?)/im', $data, $url);

if (isset($url[0])) {
Expand All @@ -96,9 +148,50 @@ public function extract(string $data): CurlParametersInterface
}
}

$curlParameters->setHeaders($headers);

return $curlParameters;
}

/**
* Extract the arguments from a quoted parameter.
*
* @param CurlParametersInterface $curlParameters
* The current object that implements CurlParametersInterface.
* @param string $flagKey
* The flag key.
* @param string $flagContents
* The flag contents.
*/
protected function extractQuotedParams($curlParameters, $flagKey, $flagContents) {
switch ($flagKey) {
case 'H':
case 'header':
case 'headers':
$curlParameters->addHeader($flagContents);
break;
case 'u':
case 'user':
$credentials = explode(':', $flagContents);
$curlParameters->setUsername($credentials[0]);
$curlParameters->setPassword($credentials[1]);
break;
case 'data':
case 'data-ascii':
case 'd':
case 'data-raw':
case 'data-urlencode':
case 'data-binary':
if ($curlParameters->getHttpVerb() === 'GET') {
$curlParameters->setHttpVerb('POST');
}
$curlParameters->setData($flagContents);
break;
case 'proxy':
$curlParameters->setProxy($flagContents);
break;
case 'url':
$curlParameters->setUrl($flagContents);
break;
}
}

}
Loading

0 comments on commit 91cf27c

Please sign in to comment.