Skip to content

Commit

Permalink
Merge pull request #1 from j0k3r/test
Browse files Browse the repository at this point in the history
Add some tests
  • Loading branch information
j0k3r committed Oct 30, 2015
2 parents 3e8594a + 1331676 commit f1cee5a
Show file tree
Hide file tree
Showing 27 changed files with 1,415 additions and 652 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_size = 4
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/README.md export-ignore
/phpunit.xml export-ignore
/tests export-ignore
/example export-ignore
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vendor
composer.lock
build
9 changes: 9 additions & 0 deletions .scrutinizer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
tools:
external_code_coverage:
timeout: 600

filter:
excluded_paths:
- 'example/*'
- 'tests/*'
- '*Test.php'
33 changes: 33 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
language: php

php:
- 5.3.3
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm

# cache vendor dirs
cache:
directories:
- vendor
- $HOME/.composer/cache

# faster builds on new travis setup not using sudo
sudo: false

install:
- composer self-update

before_script:
- composer install --prefer-dist --no-interaction

script:
- ./vendor/bin/phpunit --coverage-clover=coverage.clover

after_script:
- |
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage.clover
51 changes: 30 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# SafeCurl

[![Build Status](https://travis-ci.org/j0k3r/safecurl.svg?branch=master)](https://travis-ci.org/j0k3r/safecurl)
[![Code Coverage](https://scrutinizer-ci.com/g/j0k3r/safecurl/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/j0k3r/safecurl/?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/j0k3r/safecurl/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/j0k3r/safecurl/?branch=master)

SafeCurl intends to be a drop-in replacement for the [curl_exec](http://php.net/manual/en/function.curl-exec.php) function in PHP. SafeCurl validates each part of the URL against a white or black list, to help protect against Server-Side Request Forgery attacks.

For more infomation about the project see the blog post ['SafeCurl: SSRF Protection, and a "Capture the Bitcoins"'](http://blog.fin1te.net/post/86235998757/safecurl-ssrf-protection-and-a-capture-the-bitcoins).
Expand All @@ -15,9 +19,9 @@ If you chose to enable "FOLLOWLOCATION", then any redirects are caught, and re-v
SafeCurl can be included in any PHP project using [Composer](https://getcomposer.org). Include the following in your `composer.json` file under `require`.

```
"require": {
"fin1te\safecurl": "~1"
}
"require": {
"fin1te\safecurl": "~1"
}
```

Then update Composer.
Expand All @@ -28,7 +32,7 @@ composer update

## Usage

It's as easy as replacing `curl_exec` with `SafeCurl::execute`, and wrapping it in a `try {} catch {}` block.
It's as easy as replacing `curl_exec` and wrapping it in a `try {} catch {}` block.

```php
use fin1te\SafeCurl\SafeCurl;
Expand All @@ -38,11 +42,13 @@ try {
$url = 'http://www.google.com';

$curlHandle = curl_init();

//Your usual cURL options
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (SafeCurl)');
curl_setopt($curlHandle, CURLOPT_USERAGENT, 'Mozilla/5.0 (SafeCurl)');

//Execute using SafeCurl
$response = SafeCurl::execute($url, $curlHandle);
$safeCurl = new SafeCurl($curlHandle);
$response = $safeCurl->execute($url);
} catch (Exception $e) {
//URL wasn't safe
}
Expand All @@ -56,24 +62,30 @@ If you wish to add your own options (such as to blacklist any requests to domain
Domains are express using regex syntax, whilst IPs, scheme and ports are standard strings (IPs can be specified in [CIDR notation](https://en.wikipedia.org/wiki/Cidr)).

```php
use fin1te\SafeCurl\SafeCurl;
use fin1te\SafeCurl\Options;

$options = new Options();
$options->addToList('blacklist', 'domain', '(.*)\.fin1te\.net');
$options->addToList('whitelist', 'scheme', 'ftp');

$curlHandle = curl_init();

//This will now throw an InvalidDomainException
$response = SafeCurl::execute('http://safecurl.fin1te.net', $curlHandle, $options);
$safeCurl = new SafeCurl($curlHandle, $options);
$response = $safeCurl->execute('http://safecurl.fin1te.net');

//Whilst this will be allowed, and return the response
$response = SafeCurl::execute('ftp://fin1te.net', $curlHandle, $options);
$safeCurl = new SafeCurl($curlHandle, $options);
$response = $safeCurl->execute('ftp://fin1te.net');
```

Since we can't get access to any already set cURL options (see Caveats section), to enable `CURL_FOLLOWREDIRECTS` you must call the `enableFollowRedirects()` method. If you wish to specify a redirect limit, you will need to call `setMaxRedirects()`. Passing in `0` will allow infinite redirects.

```php
$options = new Options();
$options->enableFollowLocation();

//Abort after 10 redirects
$options->setFollowLocationLimit(10);
```
Expand All @@ -84,6 +96,7 @@ The URL checking methods are also public, meaning that you can validate a URL be

```php
use fin1te\SafeCurl\Url;
use fin1te\SafeCurl\Exception;

try {
$url = 'http://www.google.com';
Expand All @@ -95,7 +108,6 @@ try {
}
```


#### Optional Protections

In addition to the standard checks, two more are available.
Expand All @@ -110,24 +122,21 @@ $options->enablePinDns();
The second disables the use of credentials in a URL, since PHP's `parse_url` returns values which differ from ones cURL uses. This is a temporary fix.

```php
use fin1te\SafeCurl\SafeCurl;
use fin1te\SafeCurl\Exception;
use fin1te\SafeCurl\Options;

$options = new Options();
$options->disableSendCredentials();

$curlHandle = curl_init();

//This will throw an InvalidURLException
$response = SafeCurl::execute('http://user:pass@google.com', $curlHandle, $options);
$safeCurl = new SafeCurl($curlHandle, $options);
$response = $safeCurl->execute('http://user:pass@google.com');
```

#### Cavets
Since SafeCurl uses `getaddrbyhostl` to resolve domain names, which isn't IPv6 compatible, the class will only work with IPv4 at the moment. See [Issue #1](https://github.com/fin1te/safecurl/issues/1).
Since SafeCurl uses `gethostbynamel` to resolve domain names, which isn't IPv6 compatible, the class will only work with IPv4 at the moment. See [Issue #1](https://github.com/fin1te/safecurl/issues/1).

As mentioned above, we can't fetch the value of any cURL options set against the provided cURL handle. Because SafeCurl handles redirects itself, it will turn off `CURLOPT_FOLLOWLOCATION` and use the value from the `Options` object. This is also true of `CURLOPT_MAXREDIRECTS`.

## Demo

A live demo is available at [http://safecurl.fin1te.net/#demo](http://safecurl.fin1te.net/#demo). For the site source code (if you're curious), it's hosted at [fin1te/safecurl.fin1te.net](https://github.com/fin1te/safecurl.fin1te.net).

## Bounty

In order to help make SafeCurl secure and ready for production use, [a Bitcoin bounty](http://safecurl.fin1te.net/#bounty) has been setup.

Inside the document root is a [Bitcoin wallet](http://safecurl.fin1te.net/btc.txt), which is only accessible by 127.0.0.1. If you can bypass the protections and grab the file, you're free to take the Bitcoins.
10 changes: 7 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
}
],
"require": {
"php": ">=5.3.0"
"php": ">=5.3.3",
"ext-curl": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.0.0"
},
"autoload": {
"psr-0": {
"fin1te\\SafeCurl": "src/"
"psr-4": {
"fin1te\\SafeCurl\\": "src/"
}
}
}
4 changes: 2 additions & 2 deletions example/default.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
use fin1te\SafeCurl\SafeCurl;

try {
$curlHandle = curl_init();
$result = SafeCurl::execute('https://fin1te.net', $curlHandle);
$safeCurl = new SafeCurl(curl_init());
$result = $safeCurl->execute('https://fin1te.net');
} catch (Exception $e) {
//Handle exception
}
5 changes: 2 additions & 3 deletions example/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
use fin1te\SafeCurl\Options;

try {
$curlHandle = curl_init();

$options = new Options();
//Completely clear the whitelist
$options->setList('whitelist', []);
Expand All @@ -20,7 +18,8 @@
//Set the domain whitelist only
$options->setList('whitelist', ['google.com', 'youtube.com'], 'domain');

$result = SafeCurl::execute('http://www.youtube.com', $curlHandle);
$safeCurl = new SafeCurl(curl_init());
$result = $safeCurl->execute('http://www.youtube.com');
} catch (Exception $e) {
//Handle exception
}
5 changes: 2 additions & 3 deletions example/redirects.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
use fin1te\SafeCurl\Options;

try {
$curlHandle = curl_init();

$options = new Options();
//Follow redirects, but limit to 10
$options->enableFollowLocation()->setFollowLocationLimit(10);

$result = SafeCurl::execute('http://fin1te.net', $curlHandle);
$safeCurl = new SafeCurl(curl_init());
$result = $safeCurl->execute('http://fin1te.net');
} catch (Exception $e) {
//Handle exception
}
28 changes: 28 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="safecurl Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>

<filter>
<whitelist>
<directory>./src/</directory>
</whitelist>
</filter>

<logging>
<log type="coverage-html" target="build/coverage" title="safecurl" charset="UTF-8" yui="true" highlight="true" lowUpperBound="35" highLowerBound="70"/>
</logging>
</phpunit>
7 changes: 7 additions & 0 deletions src/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace fin1te\SafeCurl;

class Exception extends \Exception
{
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php

namespace fin1te\SafeCurl\Exception;

use fin1te\SafeCurl\Exception;

class InvalidOptionException extends Exception { }
class InvalidOptionException extends Exception
{
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php

namespace fin1te\SafeCurl\Exception;

use fin1te\SafeCurl\Exception;

class InvalidURLException extends Exception { }
class InvalidURLException extends Exception
{
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php

namespace fin1te\SafeCurl\Exception\InvalidURLException;

use fin1te\SafeCurl\Exception\InvalidURLException;

class InvalidDomainException extends InvalidURLException { }
class InvalidDomainException extends InvalidURLException
{
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php

namespace fin1te\SafeCurl\Exception\InvalidURLException;

use fin1te\SafeCurl\Exception\InvalidURLException;

class InvalidIPException extends InvalidURLException { }
class InvalidIPException extends InvalidURLException
{
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php

namespace fin1te\SafeCurl\Exception\InvalidURLException;

use fin1te\SafeCurl\Exception\InvalidURLException;

class InvalidPortException extends InvalidURLException { }
class InvalidPortException extends InvalidURLException
{
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php

namespace fin1te\SafeCurl\Exception\InvalidURLException;

use fin1te\SafeCurl\Exception\InvalidURLException;

class InvalidSchemeException extends InvalidURLException { }
class InvalidSchemeException extends InvalidURLException
{
}
Loading

0 comments on commit f1cee5a

Please sign in to comment.