Integration testing of web app for injections

If you've got a web app that you want to perfectly cover with tests, here is what you should have:

  • backend unit-tests — mostly models and isolated classes are covered - code gets isolated (also single responsibility principle is maintained)
  • frontend unit-tests — karma + phantomjs will check all of your angular-services or backbone-models
  • e2e (scenario, system) tests — most likely based on selenium (protractor, selenide). Slowly, entire functionality of working system with UI gets tested, so you tend to think about use cases
  • db/entity tests with migrations — your DB changes are added to base "from zero" dump and result state is compared to entity/record classes. This way your code and your DB schema is in sync
    • testing db-procedures I don't mention because i'm not into PL/SQL, but maybe you should
  • integration tests for external systems/apis - of any type (rest, ftp, soap) and source (social networks, accounting, warehouse, SMS-gateway) is tested for:
    • availability (like pingdom does)
    • structure compatibility (simple GET with json comparison is enough)
    • full write-interaction (usually partner-company sets up a testing machine with their developer)
  • integration tests of controllers/api — are executed without browserm through plain HTTP requests, that emulate  ajax or mobile devices
    • simple get - requests, checking for errors and stack traces
    • post/put requests that change data
    • in messed-up cases (with mobile devices), when e2e tests are impossible to run, but functions require testing, you get sequential scenario requests (not single get-post ones), that save entity and user states (in DB or session)

So lets take a look at last ones

Unit-testing controllers is inconvenient

Testing controllers with unit test is although fast in execution, is terrible at writing and maintenance. Yes, I've heard Uncle Bob saying that you need to cover code entirely, but controller is a place where multiple sources join:

  • configuration (from file includes, yaml, database, constants) — that means that we either need to execute & include all files, or that we need to mock all possibilities with constant-value replacements
  • new instances of models - you've got to mock those too, since you have separate unit tests for models
  • global IO variables and methods - you need to put them in some untestable models and mock them
  • global and/or static variables, factory-instantiation - same as with configuration, its complex
  • aspects - annotations, access, logging, reflection-based logic — you'll need to come up with some magic mocking there as well. For example - template names for methods can be in annotation, how do you unit-test that template engine gets called?

But worse of all ofcourse is not the mocking itself, but its amount. If your controller method has 5 models - thats the least amount of mocks you need to define. In addition, for every model method call, you need to write not one line of its emulation, but several — which and how many times was that method called, with which arguments and what got returned.

Sometimes mocks should return an object (just like PDO can return PDOStatement for example) and call its method. Now you have mocks depending on one another. Often, i get confused because of the order of their registration. Since calling testable method should be at the end of the test, registering mocks is supposed to be before it, so you get test method that follows testable code upside down. To put it shortly — writing unit-tests for controllers is dangerous. (Though some advice looking at phpspec)

How to test controllers

Integration tests are somewhat similar with end-to-end tests, but they don't include UI

  1. So we need a class with CURL functions for HTTP requests (get,post.. if you need delete and put for your API, put them in too)
  2. Write authentication call, if you have one (I use cookie file and thats enough for me)
  3. Add phpunit @depends login , so you don't rape your server if login fails and you've got tons of tests to execute
  4. Simple get-requests with existing DB ids that should tell you if some error got in, that unit tests missed

Now lets see POST/PUT requests. They usually have bigger security vulnerability frequency, because they have more parameters and logic during state change. Adding and changing entities should return some result. Lets say {result:1, id:3} in JSON will mean that object with certain ID got created. Apart from usual tests for saving, we need to push all possible parameters with SQL- and XSS-injections. 

SQL-injections shoule either give us error right away, or after we get/read entity, passed value (in our case 1' OR 1=1) will differ from stored in DB (in this case, possibly "1"). Sometimes, values get type-casted, and so string will become int value and we might need to treat as ok.

With XSS its a little harder. We need a browser to see if injected JS is executed. I solve this by running e2e tests after integration tests and since DB is not reset, system tests should encounter injected alerts that will stop e2e tests. A list of XSS attack tokens is on OWASP.

For automation, i wrote trait for PHPUnit-tests, because its easier to reuse same code in different places. 

trait SQLinjection {

	private $AttackTokens = [ 
		'1" OR 1=1'
	];

	public function checkInfectedUpdate(
		$saveURL,
		$readURL,
		$fields,
		callable $comparisonFn){
		
		foreach($this->attackTokens as $injection){
			$data = $fields;
			foreach($fields as $k=>$v){
				$data[$k]=($v=='*' ? $injection : $v);			
			}
			
			$saveResult = $this->post($saveURL, $data);
			$getResult = $this->get($getURL);
		
			$comparisonFn($injection, $getResult, $saveResult);
		}
	}
	
	public function checkInfectedInsert(..){..}

}

Each integration test inherits IntegrationBaseTest -that includes CURL-wrappers and URL of the server. Test method should know how to compare injection with results from saving and getting, because in each entity of API get() calls and formats differ - somewhere its a mere array, while others include hierarchies that you need to iterate (and get a last version)

InvoiceControllerTest extends IntegrationBaseTest {
	private $phpErrorDetection = 'error';
	
	use SQLinjection;
	
	/**
	 * @test
	 */
	function login() {
		$result = parent::login();
	}

	/**
	 * @test
	 * @depends login
	 * @group security
	 */
	function postSave_AddingInjection() {
		$self = $this;

		$this->checkInfectedUpdate(
			$this->baseURL . 'invoice/save',
			$this->baseURL . 'invoice/get?id=3',
			[
				'company_id'  => '1',
				'title'        => '*',
				'description'  => '*'
			],
			function ($injection, $getResponse, $insertResponse) use ($self) {
				$this->assertEquals($injection, $getResponse['result']['title']);
				$this->assertEquals($injection, json_decode($getResponse['result']['description']));
			}
		);
	}
}
 

As you write controller tests, it seems that you also need to

  1. test for privileges (who should get response?)
  2. refactor controller code and move complex logic to models (because its hard to understand fat controllers)
  3. get rid of stacktrace printing - because client should see sensitive info
  4. fix cases with DB integrity violation, when you're trying to add an entity that refers to other entities, without checking in DB if they exist at all

My suggestion doesn't solve questions with CSRF, redirects, unsalted passwords, SSL, sessions, configuration errors, but it does improve application functional structure in security and logic, even if you use PDO with bindParam() everywhere. 

Google Adwords - a messy flagship

Google makes tonns of money on advertising and Adwords is its main flagship. You buy visitors from keyword auctions, and it might be 2-3 times cheaper than Facebook. 

I did however face a huge problem in it.

So back in 2011 Estonia switched to euro. What would you as engineer or a project lead do if you had users with that kind of money transition? Well, a good company would not care - your money would be transferred automatically based on some fixed ratio (that Estonian bank has up to this day and as a law).

What did Google do? Well, they softly blocked all users from that country. So first they sent emails with notifications, so that everyone could spend money on their campaigns.. and then you would get a errors that you dont have money.

google_adwords1

Well that expected, right? Yes, but now if you try to follow their links, you will get to 404 or billing configuration which is not editable. In addition billing view seems to load a different language than navigation.

google_adwords1_payment google_adwords1_changebilling

So now that you have account with billing that is not editable what do you do? Right. Read FAQ and write to the support, and well.. I got email in the end in estonian, to quote it..

Kui konto on kinni pandud, siis seda enam kasutada ei saa. Selleks, et vana maili edasi kasutada, võid teha järgmist:
1. Lisa vanale EEKides AdWordsi kontole veel üks administraatorkasutaja. Selleks vali mail, mida sa edaspidi kasutada ei soovi, ja mis jääb vana AdWordsiga seotuks.
2. Kui sui nüüd vanas kontos 2 administraatorit on, saad selle kasutaja, kelle maili sa uues kontos tahad kasutada, vanast kontost ära kustutada.
3. Nüüd on vana mail vaba ning võis seda uues kontos kasutada

Veidi ebamugav see tõesti on, aga kahjuks on AdWordsi süsteem nii üles ehitatud. Tänu sellele on võimalik kord kustutatud konto uuesti taastada (mida meilt sageli palutakse).

Juhised administraatori lisamiseks/kustutamiseks leiad siit: http://support.google.com/adwords/bin/answer.py?hl=en&answer=1235276

So basically they suggest making a new google user, tie it to adwords then remove old email.. and re-register again. Sounds like a lifehack because some engineer was too lazy to make "Delete adwords account" button.

Ok, so now I invite myself with yandex email, register, confirm email, confirm adding admin permissions, try to login with yandex email to adwords and boom, I get locked for no apparent reason (saying that this email is used in another Adwords account, though I just registered!). So I'm furious, spamming the login button.. and boom, I broke it. Argh.

google_adwords2 google_adwords_down

Shut up and take my money! So I try again and get in, fast, before more bugs show up... Oh noes, they are already here.. quick, remove my old account.. Yeaah!

google_adwords2_loggedin google_adwords2_success adwords3_register

To conclude - I got Adsense working again, but it took me several errors screens, a new email account, a lot of hassle and roughly 2 years until I finally got the patience to go through all of this just to be able to pay Google for their main service that makes money.

I realize that it was probably easier for them to just block users than to do the migration, and its not that often countries change currencies, and that they probably forgot about the system that still shows those errors to poor guys.. Yet I have a feeling this is a bad sign for investors that Google is more bureaucratic, like Microsoft which probably still has mixed systems showing design from the 90'es. 

Jenkins must-have plugins

This tuesday, Juri Timoshin talked about his practice with Jenkins and builds, showing mainly matrix plugin as most complex feature that Zeroturnaround uses for running builds on different environments.

But for a web-application in php, there is no reason to setup jenkins along with whole LAMP stack from ground up. Instead, I'd advice having a staging environment running all the time separately from jenkins itself. You can't do local file-based test in this case (like checking if mail was actually sent from the system, by re-routing postfix server to dump it into /tmp folder), but then you have centralized jenkins machine for all projects.

  • Disk Usage 
  • Compact Columns & Green Balls - just to have better layout
  • Extra Columns  - to see test dynamics from dashboard 
  • Jenkins Throttle - to avoid concurrent building of projects that share same resource (DB for example)
  • JobConfigurationHistory - in case you forget how you managed a job before
  • Doxygen - for better documentation of php projects, instead of old phpdocumentor
  • Performance - for load testing and Jmeter
  • Clover Plugin - for looking after unit test code coverage
  • Violations - to see how code acquires technical debt. I think this one comes installed already with PMD

Explicit content microformat

Since I develop my blog, naturally i get interested around formatting its contents.

Problem being explicit

Explicit content differs in a way that impacts readers psyche. If not changing his world views, then changing subjective opinion. Not every reader is ready for this, not everyone wants to see it. Thats why warnings, discretion, content notices are needed. 

Another problem is that rating should not depend on viewers age. Filtering if it exists, should be contextual, based on county legislation like COPPA. Ofcourse, if warnings are present, then automatic censorship by browser or search engines is possible. Editors should be ready for this, since this is totally voluntary.

Its clear that censorship of content is targeted for young audience, but "children" and "rate of violence" might greatly vary. You don't expect forbidding playing Mario games to everyone who is not yet 21 years old just because hero jumps on turtles, crushing them, still PETA may find that you do.

Existing standards

1. SafeSurf - dead

2. RTA - probably most used standard on modern adult-websites. It uses either header or as meta data on page.

header("Rating: RTA-5042-1996-1400-1577-RTA"); <meta name="RATING" content="RTA-5042-1996-1400-1577-RTA" />

3. ICRA - got old, used page context and used rather large table of ICRA and RSAC codes.

<meta http-equiv="pics-label" content='(PICS-1.1 entries)' />

4. PICS - migrated into W3C POWDER. Last one is based on rdf, which means its hard to manage and understand

Thats it! Nothing real, no microformat for average webmaster. What we need is a level-based rating of any content on the web, like MPAA does for movies or ESRB and PEGI do for games.

New microformat / xrate 1.1

I see proper way to format this data very clearly - author just has to mark any html element with attribute, based on rating. HTML5 gives attribute data- in free use, so i suggest using namespace xrate with integer values (0-100) which represent danger to viewer (the higher - the bigger), which will also ease filtering. Note that its NOT VIEWER'S AGE. 

For simplicity, you can use general param, let say data-xrate="20", but its more semantically proper to choose its area:

data-xrate-lang  Obscene language
data-xrate-sex Romantic, erotic, pornographic
data-xrate-nude Level of nudity
data-xrate-disgust Might cause disgust (shit, larvae, decomposition)
data-xrate-violence Violence and its results - weapons, wounds, dead bodies, blood
data-xrate-asocial Smoking, alcohol, drugs, gambling, prostitution
data-xrate-faith Might offend religious people, added in 1.1
data-xrate-empathy Might contain empathic content, like psychological suffering, added in 1.1

Some techincal content warnings

data-xrate-blink Blinking animation that might cause eplilepsy
data-xrate-spoiler Story is retold
data-xrate-camera If application (flash/applet?) gains access to videocamera
data-xrate-malware If resource can cause infection (viruses, trojans etc.) on viewer's machine

Sex(xrate-sex)

Obviously sex and nudity are separate things. Naturally they mostly do follow each other, but paintings or chilling nude old dudes mostly don't carry as much erotic energy.

Romance. Kissing, wish Erotica. Breasts, will Porn. Genitals, action
0-30 30-70 70-100
romance-creative-family-beach-vacation.jpeg Nudity 30, sex 30

Nudity(xrate-nude)

Dressed Partially nude  Fully nude
0-30  30-70 70-100 

forest_mystery_by_vvola-d4adnj9.jpeg   Nudity 50 / Photo by Maria Vetrova, mvetrova.com Nudity 50   Nudity 80
<img src="http://www.tema.ru/jjj/tits/renuar.jpg" data-xrate-nude="60" data-xrate-sex="0" />

Violence(xrate-violence)

Violence also differs

<a href="http://meatvideo.com/" data-xrate-violence="100">omg</a>
fantasy heroes fighting, wounds blood, dead bodies, grevious wounds
0-30 30-70  70-100
Jerryscousin1.jpeg Бокс Longfin pilot whales, Faroe Islands / GEORGIA MANNION The Passion of the Christ / Violence = 70

Antisocial behaviour (xrate-asocial)

What does scare most parents is not someone swearing, viewing erotic movies or violent games. Parents are afraid of spiritual fall of their kids which begins with misunderstanding and indifference. This attribute should fight against smoking, drinking, drugs, prostitution, so that it wouldn't be considered ok.

Kids smoking. Keystone/Epa Yuri Kadobnov Тайланд / fresher.ru Взлом банкомата Алкоголики

Obscene language (xrate-lang)

Language directly influences thinking. Obscene language cannot be limited with usual word filters, because its an emotion, which can take form of drawings or hand gestures.

Хуй на Литейном мосту, акция арт-группы "Война".

Insulting faith (xrate-faith)

Quite a big issue in modern politics currently

Darth Vader с нимбом Запретное искусство - 2006 Акция Femen в Киеве

Empathy (xrate-empathy)

Empathy is what makes humans a social kind

Disgust (xrate-disgust)

Analysis

Filtering html if these attributes were added is easy. For example if i have paragraph with data-xrate-asocial=30.. then i can easily find it with jquery, and based on user's preferences either hide, replace, increase opacity or add warning to it.

$('#show_sensitive').click(function(){   $.each($('p[data-xrate-asocial]'), function(i,v){     if($(v).data('xrate-asocial')>10 ) $(v).show();   }) });

Styling is a bit more complex, since there is only comparison css selector. But you can use fixed values, like 30 and 70 - this way you have fixed diapasons:

p[data-xrate-asocial='30']:before{ content: "Warning - stupid behaviour detected"; }

Social rating

Obviously xrate- is subjective rating that author has decided upon. But if you have complex content and lots of viewers, then members can rate it themselves. Voting is like usual karma, except that you dont vote on agreeing/disagreeing with author, but on how dangerous content is.

Its a microformat. Its not complex enought to warn about scary part of the movie at some defined point, it also doesnt have any auditor or certificate of proof, and its not centralized - one user might think that bible has danger rating 10, another - that 100. Hope its enough.

Skyrim

Skyrim is already the fifth role playing game in epic TES series from Bethesda. For those who are not familiar withit, may I remind you that we are on a planet called Nirn with main continent Tamriel with lots of animals, races, languages; at a time, pretty much similar to the end of Roman empire and the beginning of early medieval ages. But first, a bit of cosmogony..

A little history

First, there was order (Anu) and chaos (Sithis), which gave birth to eight high spirits (aedra) and lots of low spirits (daedra). First of them (Akatosh) created time, while Lorkhan created this planet and binded himself to it through his heart as an active Red mountain vulcano in the Morrowind. That is how age of dawn ended and new time, consisting of three epochs began (that lasted 3 thousand, 900 and 430 years respectively). 

Полярное сияние с видом на Массер, один из спутников Нирна

One of most important changes in this world history becomes the emergence of human empire with the capital in Cyrodiil at the end of second epoch by first imperor Tiber Septim, which later becomes worshipped equally as other aedra, with new name Talos - spirit of war and rule.

First two games (Arena and Daggerfall) were done in pseudo 3D with randomly generated content and are not worth much attention. First breakthrough was Morrowind, that stunning graphics, thought through world and innovative location loading in the background. In Morrowind main story ended up with main battle of hero with nemesis partially immortal Dagoth Ur, that kept living by gaining energy from vulcano's heart.

The plot of Oblivion takes place several years after Morrowind, at the end of third era, when cultists of Mythic Dawn kill the emperor and most of his heirs, opening gates to lower worlds. Protagonist fights with this cult and daedra-princes. As a result Mehrunes Dagon is banished by new incarnation of Akatosh and the gates of oblivion are closed.

↑