Upload
xsist10
View
742
Download
5
Tags:
Embed Size (px)
DESCRIPTION
Learn about how to write open source libraries people won't (completely) hate.
Citation preview
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