Date post: | 10-May-2015 |
Category: |
Technology |
Upload: | xsist10 |
View: | 742 times |
Download: | 5 times |
Releasing Your Open Source Project
How to write libraries people won't (completely) hate
By @thomas_shone
WARNING
● Opinionated● Rambling● Foul-Mouthed● Ugly Slides● Don't feed after midnight
00. Introduction
● No formal education in PHP● Learnt PHP the hard way● This talk is about the process● Connect with personalities
00. Introduction
00. Introduction
<?php
function isUrlSafe($api_key, $url) { if (!filter_var($url, FILTER_VALIDATE_URL)) { throw new Exception('Invalid URL specified.') } $api_uri = 'https://sb-ssl.google.com/' . 'safebrowsing/api/lookup?client=api&apikey=' . $api_key . '&appver=1.0&pver=3.0&url=' . urlencode($url);
$result = file_get_contents($api_uri); return strpos($result, 'malware') === false && strpos($result, 'phishing') === false;}
When you've finished reading this code, touch your nose so I know when everyone is done.
10. Security
● Shouldn't be left till last● Unsure of how to do it properly● Worst thing that can happen to insecure open
source code is it becomes popular
10. Security
● Shouldn't be left till last● Unsure of how to do it properly● Worst thing that can happen to insecure open
source code is it becomes popular
01. Security
● Shouldn't be left till last● Unsure of how to do it properly● Worst thing that can happen to insecure open
source code is it becomes popular
01. Security
● Joomla!– 5/133 versions secure– 85 vulnerabilities– 0/322 of scanned sites secured
● WordPress– 2/322 versions secure– 54 vulnerabilities– 313/589 of scanned sites secure
01. Secure Communication
What questions do we need to ask to ensure our communication is secure?
01. Secure Communication
1. Can C overhear what A and B are saying?
BA
C
01. Secure Communication
2. Is A sure s/he is talking to B and C isn't standing in the middle?
BA C
01. Secure Communication
3. Does A trust B not to tell C?
BA C
01. Secure Communication
● Certificate file sourced from https://github.com/guzzle/guzzle/blob/master/src/cacert.pem● disable_compression only available in PHP 5.4.13+ (prevents CRIME/BREACH attacks)● Not required with PHP 5.6+ thanks to this guy...
$context = stream_context_create([ 'ssl' => [ 'verify_peer' => true, 'verify_depth' => 5, 'cafile' => 'cacert.pem', 'CN_match' => 'sb-ssl.google.com', 'disable_compression' => true, ]]);$result = file_get_contents($api_uri, false, $context);
01. Personality
Daniel Lowrey (@rdlowrey)PHP SSL/TLS ContributorSaving us from ourselves
I totally got per mission to use this ph oto
02. Hosting
● phpclasses.org● sourceforge.net● pear.php.net● bitbucket.org● github.com
02. Hosting
● phpclasses.org - FUCK NO!● sourceforge.net - HELL NO!● pear.php.net - No● bitbucket.org - No● github.com - Yes
03. Managing your source
● Source control (already determined)● Version● License
03. Credentials
● .gitignore to exclude sensitive data● If you've pushed sensitive data to github,
change your credentials asaphttps://help.github.com/articles/remove-sensitive-data
Don't be that guy
03. Licensing
● MIT– Do whatever you want with it– Must attribute– Don't blame me if it causes a zombie outbreak
● Apache– Same as MIT– contributors grants patent rights to users
● GPL– Must release any changes or improvements– Can't change license– Ditto with the zombies
http://choosealicense.com/
03. Versioning
MAJOR.MINOR.PATCH-STABILITY
● Breaking backward compatibility? Increase MAJOR● Adding backwards compatible feature? Increase MINOR● Adding bugfix? Increase PATCH● Not production ready? Add stability value (alpha, beta,
preview)
http://semver.org/
04. Package Management
04. Package Management
● PEAR– No space for alternatives– High requirement levels– Package signing
● Composer– Easy to install/update dependencies– Version locking– Autoloading– Your package becomes smaller– Package signing (almost)– Doubles as a distribution platform (https://packagist.org )
04. Package Management
● PEAR - NO– No space for alternatives– High requirement levels– Package signing
● Composer - YES– Easy to install/update dependencies– Version locking– Autoloading– Your package becomes smaller– Package signing (almost)– Doubles as a distribution platform (https://packagist.org )
04. Composer
$ mkdir safebrowser && cd safebrowser$ curl -s http://getcomposer.org/installer | php#!/usr/bin/env phpAll settings correct for using ComposerDownloading...
Composer successfully installed to: /home/project/composer.pharUse it: php composer.phar
04. Composer
$ php composer.phar init
Welcome to the Composer config generator
This command will guide you through creating your composer.json config.
Package name (<vendor>/<name>) [thomas/project]:xsist10/SafeBrowsing Description []: Google Safe Browsing ClientAuthor [Thomas Shone <[email protected]>]: Minimum Stability []: License []: MIT
04. Composer
{ "name": "xsist10/safebrowser", "description": "Google SafeBrowser Client", "license": "MIT", "authors": [ { "name": "Thomas Shone", "email": "[email protected]" } ], "require": { }, "autoload": { "psr-4": { "xsist10\\SafeBrowsing\\": "src/" } }}
04. Composer
$ php composer.phar installLoading composer repositories with package informationInstalling dependencies (including require-dev)Nothing to install or updateGenerating autoload files$ vi index.php
<?php
require 'vendor/autoload.php';
04. Don't commit vendor/
# Your codesrc/[Your Library]vendor/[Dependencies]
$ echo "vendor" >> .gitignore
# Someone using your librarysrc/[Their Project Code]vendor/xsist10/SafeBrowsing/[Your Library]vendor/[Your Library Dependencies]vendor/[Their Dependencies]
Some of these might be the same
# You don't want thisvendor/xsist10/SafeBrowsing/[Your Library]/vendor/
04. Composer
$ mkdir src$ vi src/SafeBrowsing.php
<?php
namespace xsist10\SafeBrowsing;
class SafeBrowsing {public function __construct($api_key) {
$this->api_key = $api_key;}
public function isUrlSafe($url) {// ...
}}
04. Composer
<?php
require 'vendor/autoload.php';
use xsist10\SafeBrowsing\SafeBrowsing;
$safeBrowsing = new SafeBrowsing($api_key);$safeBrowsing->isUrlSafe('www.example.com');
04. List on Packagist
04. List on Packagist
$ php composer.phar require xsist10/safebrowser=dev-master
04. Setup Webhook
04. Setup Webhook
04. Setup Webhook
04. Setup Webhook
04. Package Signing
● Currently being implemented in Composer– https://github.com/composer/composer/pull/2814
● Ensure that the package you're installing hasn't been tampered with, like:– Ruby Gem installs– PEAR libraries– Linux packages (deb, rpm, yum)– Windows binaries
04. Package Signing# When you first setup your project$ php composer.phar create-keys --directory /path/ --prefix=mykey --passphrase passphrase to encrypt the private key:
$ php composer.phar add-dev-key /path/mykey-private.pem
$ php composer.phar sign-dev-keys /path/mykey-private.pem
# Last thing you do before you release a new version$ php composer.phar sign /path/mykey-private.pemEnter a passphrase if the private key is encrypted:
$ git commit -m “Updated keys” keys.json manifest.json$ git push# Tag you release immediately
04. Version
04. Personality
Pádraic Brady (@padraicb)Zend Framework / Composer contributorWorking on the signing code
This gu y is so aw
esome that the inte rnet ca n't
contain picture s of him.
05. Design Patterns
● Increase flexibility without having to modify the library code
● Provide rules on how to extend
05. Strategy
05. Strategy
<?php
namespace xsist10\SafeBrowsing\Strategy;
interface Strategy{ public function execute($url, $param);}
https://en.wikipedia.org/wiki/Strategy_pattern
05. Strategy<?php
namespace xsist10\SafeBrowsing\Strategy;
class Get implements Strategy { public function execute($url, $param) { $context = stream_context_create([ 'ssl' => [ 'verify_peer' => true, 'cafile' => 'path/to/cafile', 'CN_match' => 'sb-ssl.google.com' ] ]); $query = $url . '?' . http_build_query($param); return file_get_contents($query, false, $context); }}
05. Strategy<?php
namespace xsist10\SafeBrowsing\Strategy;
class Post implements Strategy { public function execute($url, $param) { // Do some curl init stuff ... // Do your security! curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($curl, CURLOPT_CAINFO, 'path/to/cafile');
$result = curl_exec($curl); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl);
// check some result checks first ... return $result; }}
05. Strategy
<?php
require 'vendor/autoload.php';
use xsist10\SafeBrowsing\SafeBrowsing;use xsist10\SafeBrowsing\Strategy\Get;
$sb = new SafeBrowsing($api_key, new Get());$sb->isUrlSafe('www.example.com');
use xsist10\SafeBrowsing\Strategy\Post;
$sb = new SafeBrowsing($api_key, new Post());$sb->isUrlSafe('www.example.com');
05. Chain of Responsibility
05. Chain of Responsibility
<?php
namespace xsist10\SafeBrowsing\Strategy;
use \Exception;
class UnavailableException extends Exception {}
https://en.wikipedia.org/wiki/Strategy_pattern
05. Chain of Responsibility<?php
namespace xsist10\SafeBrowsing\Strategy;
class Get implements Strategy {
public function execute($url, $param) { if (!ini_get('allow_url_fopen')) { throw new UnavailableException(); }
// ... }}
05. Chain of Responsibility<?php
namespace xsist10\SafeBrowsing\Strategy;
class Post implements Strategy {
public function execute($url, $param) { if (!function_exists('curl_init')) { throw new UnavailableException(); }
// ... }}
05. Chain of Responsibility<?phpnamespace xsist10\SafeBrowsing;use xsist10\SafeBrowsing\Strategy\Strategy;
class Chain implements Strategy { public function append(Strategy $strat) { $this->chain[] = $strat; }
public function execute($url, $param) { foreach ($this->chain as $strategy) { try { return $strategy->get($url, $param); } catch (UnavailableException $exception) { // We can ignore and move to the next } } throw new Exception('No available strategy.'); }}
05. Put the chain links together
<?php
// ...use xsist10\SafeBrowsing\Chain;
$chain = new Chain();$chain->append(new Post());$chain->append(new Get());
$sb = new SafeBrowsing($api_key, $chain);$sb->isUrlSafe('www.example.com');
// This still works$sb = new SafeBrowsing($api_key, new Get());$sb->isUrlSafe('www.example.com');
05. The start of something beautiful
<?php
// ...use SomeOtherGuy\SomeOtherPackage\Cache;
$chain = new Chain();$chain->append(new Cache());$chain->append(new Post());$chain->append(new Get());
$sb = new SafeBrowsing($api_key, $chain);$sb->isUrlSafe('www.example.com');
05. Personality
Martin Fowler (@martinfowler)Design Pattern Tamerhttp://www.martinfowler.com/
He gets super pow
ers from h is hat
So what next?
Shamelessly copied from http://thephpleague.com/
● Follow PSR-2, we use League as our PSR-0 namespace.● List on Packagist, we list with league as the vendor namespace.● Shove code in a src folder.● Write unit tests. Aim for at least 80% coverage for v1.0.● DocBlock all the things.● Semantic versioning must be used to manage version numbers.● Use Travis-CI to automatically check coding standards and run tests.● Have an extensive README.
06. Why Tests?
● You will always find bugs● Confidence in libraries● Prevent regressions● Ensure new features have been thoroughly
vetted
06. PHPUnit
$ php composer.phar require --dev phpunit/phpunit=4.0.*@dev./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev)
...
- Installing phpunit/phpunit (4.0.x-dev fca5bc6) Cloning fca5bc6a50d09b26db280c5cc3c84978c9cace3f
phpunit/phpunit suggests installing phpunit/php-invoker (~1.1)Writing lock fileGenerating autoload files
06. PHPUnit
$ vi phpunit.xml
<?xml version="1.0" encoding="UTF-8"?><phpunit backupGlobals="false" convertErrorsToExceptions="true" convertWarningsToExceptions="true" convertNoticesToExceptions="true" mapTestClassNameToCoveredClassName="true" bootstrap="vendor/autoload.php" strict="true" verbose="true" colors="true"> <testsuites> <testsuite> <directory>./tests</directory> </testsuite> </testsuites></phpunit>
$ mkdir tests$ vi phpunit.xml
06. PHPUnit
$ vi phpunit.xml$ ./vendor/bin/phpunitPHPUnit 4.0.13 by Sebastian Bergmann.
Configuration read from /path/to/project/phpunit.xml
Time: 116 ms, Memory: 2.00Mb
No tests executed!
06. First Tests
use xsist10\SafeBrowsing\SafeBrowsing;use xsist10\SafeBrowsing\Strategy\Chain;
class SafeBrowsingTest extends \PHPUnit_Framework_TestCase{ public function testInvalidUrl() { $chain = new Chain(); $safeBrowsing = new SafeBrowsing('', $chain);
$message = 'Invalid URL specified.'; $this->setExpectedException('Exception', $message); $safeBrowsing->isUrlSafe('invalid-url'); }}
$ vi tests/SafeBrowsingTest.php
06. First Tests
public function testSecure(){ $mock = $this->getMockBuilder( 'xsist10\SafeBrowsing\Strategy\Chain', ['execute'] )->getMock();
// API returns an empty result if the site is secure $mock->expects($this->once()) ->method('execute') ->will($this->returnValue(''));
$safeBrowsing = new SafeBrowsing('', $mock); $url = 'http://www.google.com'; $response = $safeBrowsing->isUrlSafe($url); $this->assertTrue($response);}
06. First Tests
$ vi phpunit.xml$ ./vendor/bin/phpunitPHPUnit 4.0.13 by Sebastian Bergmann.
Configuration read from /path/to/project/phpunit.xml
.....
Time: 568 ms, Memory: 4.00Mb
OK (5 tests, 9 assertions)
06. Testing Resources
● Can't mock out resources● Wrap resources in class and mock the class● Wait! Don't write from scratch. Use your
package manager!
06. cURL wrapper
$ ./composer.phar search curlext-curl The curl PHP extensionlib-curl The curl PHP librarykdyby/curl Curl wrapper for Nette Frameworkshuber/curl PHP Wrapper for Curlcomvi/curl Work with remote servers via cURL much easier than using the native PHP bindings.anlutro/curl Simple OOP cURL wrapper.jyggen/curl A simple and lightweight cURL library with support for multiple requests in parallel.bca/curl cURL wrapper for PHP applications.unikent/curl Laravel Curl Helper Library.mogetutu/curl Simple Curl PHP Helper Librarysweelix/curl PHP 5.3+ simple curl requestorlib/curl A simple cURL wrapper for PHPdvelopment/curl PHP OOP wrapper for cURL requestsphp-curl-class/php-curl-class PHP Curl Class is an object-oriented wrapper of the PHP cURL extension.
06. cURL wrapper
$ php composer.phar require shuber/curl=dev-master./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev) - Installing shuber/curl (dev-master 6624992) Cloning 6624992df201f9fd7262080117385dd09b0ecd2b
Writing lock fileGenerating autoload files
06. Personality
Chris Hartjes (@grmpyprogrammer)Testing advocateBeing grumpy... so we don't have to
I totally didn't g et permission to use t his pho to
06. PersonalityH
ow he reacts to a lac k of tes ts.
07. Code Coverage
● Ensure you test all use cases● Useful to spot code smell● Helpful in identifying dead/unreachable code● Improves confidence in library
07. Coverage Report
<phpunit ...> ... <logging> <log type="coverage-html" target="build/report" charset="UTF-8" highlight="false" LowUpperBound="35" HighLowerBound="70" /> </logging>
<filter> <whitelist> <directory>src</directory> </whitelist> </filter></phpunit>
$ echo “build” >> .gitignore$ vi phpunit.xml
07. Coverage Report
07. Coverage Report
07. Coverage Report
07. Ignore coverage
● Ignore whole class/function– @codeCoverageIgnore
● Ignore certain lines of code– // @codeCoverageIgnoreStart– // @codeCoverageIgnoreEnd
● Use responsibly
08. Continuous Integration
● Make sure your development branch is always in a deployable state.
● Ingredients: Tests, High Coverage, Automation
08. Travis-CI
language: phpbefore_script: - wget http://getcomposer.org/composer.phar - php composer.phar install --devphp: - 5.5 - 5.4 - hhvmscript: phpunit
$ vi .travis.yml
08. CLI Tools
● Copy paste detector● Code Sniffer● Mess Detector● And lots more at http://phpqatools.org/
07 – Copy/paste detector
$ php composer.phar require --dev sebastian/phpcpd=dev-master./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev) - Installing sebastian/phpcpd (dev-master a946215) Cloning a9462153f2dd90466a010179901d31fbff598365
Writing lock fileGenerating autoload files
$ ./vendor/bin/phpcpd src/phpcpd 2.0.1 by Sebastian Bergmann.
0.00% duplicated lines out of 195 total lines of code.
Time: 32 ms, Memory: 2.75Mb
08. Code Sniffer
$ php composer.phar require --dev squizlabs/php_codesniffer=dev-master./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev) - Installing squizlabs/php_codesniffer (dev-master 623905c) Cloning 623905ce571d64a8cb873826d47b81321cd55011
Writing lock fileGenerating autoload files
$ ./vendor/bin/phpcs -iThe installed coding standards are PSR1, PHPCS, Squiz, PEAR, Zend, MySource and PSR2
$ ./vendor/bin/phpcs --standard=PSR2 src/[WALL OF TEXT]
08. Select a Standard
$ ./vendor/bin/phpcs --standard=PSR2 src/
FILE: /path/to/project/SafeBrowsing/src/SafeBrowsing.php-------------------------------------------------------------------FOUND 3 ERROR(S) AFFECTING 2 LINE(S)------------------------------------------------------------------- 17 | ERROR | Opening brace should be on a new line 22 | ERROR | Visibility must be declared on method "isUrlSafe" 22 | ERROR | Opening brace should be on a new line-------------------------------------------------------------------
[WALL OF TEXT OMITTED]
08. Custom Standard
$ ./vendor/bin/phpcs --standard=/path/to/own/standard src/
FILE: /path/to/project/SafeBrowsing/src/SafeBrowsing.php-------------------------------------------------------------------FOUND 1 ERROR(S) AFFECTING 1 LINE(S)------------------------------------------------------------------- 1 | ERROR | Homage to Cthulhu missing from doc header-------------------------------------------------------------------
08. Mess Detector
$ php composer.phar require --dev phpmd/phpmd=1.4.*./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev) - Installing phpmd/phpmd (1.4.1) Downloading: 100%
Writing lock fileGenerating autoload files
$ ./vendor/bin/phpmd src text codesize,unusedcode,naming,design
Strategy/Get.php:9 Classes should not have a constructor method with the same name as the class
08. Taking it further
● Jenkins – http://jenkins-ci.org/– Automate all the things
● Behat– http://behat.org/– Cucumber syntax– Mink extension for website testing– Write human-readable use cases
08. Behat and Mink
Feature: Test the login page In order to ensure that customer can use our system I need to make sure that they can log in successfully
Scenario: Can I log in with valid details Given I am on the “www.mywebsite.com” When I click on “Login” And I fill “username” with “bob” And I fill “password” with “Password1” And I press “Login” Then I should see “Login Successful”
09. Flair
● General badges (versions, license, etc)– https://poser.pugx.org/
● Build status– https://travis-ci.org
● Code Coverage– https://coveralls.io
● Code Analysis– https://insight.sensiolabs.com/analyses– https://scrutinizer-ci.com/
10. Engage
● Write a useful README.md– First thing you see on Github– How to install– How to use
10. Engage with developers
● Encourage fork/pull requests– Make sure they add tests– Make sure the old tests still pass– Travis-CI makes this simple
10. Engage with developers
● Promote your library– Twitter?– Google Plus?– I have no idea. I'm still figuring this out. I'm a
developer dammit!
Homework
● Docblocks● phing/ant to automate CLI tools● Git pre-commit hooks to run tests
Questions?
Twitter: @thomas_shone
Github: https://github.com/xsist10
https://github.com/xsist10/SafeBrowsing