1 Commits

Author SHA1 Message Date
rugk
79c0ad1670 Add Siftleft scan
It seems [to cover](https://slscan.io/en/latest/#supported-languages-frameworks) PHP including license check in addition to dependency scanning.
2021-06-05 00:21:48 +02:00
42 changed files with 1237 additions and 1704 deletions

2
.gitattributes vendored
View File

@@ -19,8 +19,6 @@ js/test/ export-ignore
.scrutinizer.yml export-ignore
.styleci.yml export-ignore
.travis.yml export-ignore
codacy-analysis.yml export-ignore
crowdin.yml export-ignore
composer.json export-ignore
composer.lock export-ignore
BADGES.md export-ignore

View File

@@ -0,0 +1,35 @@
# This workflow integrates Scan with GitHub's code scanning feature
# Scan is a free open-source security tool for modern DevOps teams from ShiftLeft
# Visit https://slscan.io/en/latest/integrations/code-scan for help
name: SL Scan
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '16 22 * * 4'
jobs:
Scan-Build:
# Scan runs on ubuntu, mac and windows
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# potentially add composer install steo here
- name: Perform Scan
uses: ShiftLeftSecurity/scan-action@master
env:
WORKSPACE: ""
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SCAN_AUTO_BUILD: true
with:
output: reports
# Scan auto-detects the languages.
- name: Upload report
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: reports

View File

@@ -1,29 +0,0 @@
# This is a basic workflow to help you get started with Actions
name: Snyk scan
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
# https://github.com/snyk/actions/tree/master/php
snyk-php:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Install Google Cloud Storage
run: composer require --no-update google/cloud-storage && composer update --no-dev
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/php@master
continue-on-error: true # To make sure that SARIF upload gets called
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --sarif-file-output=snyk.sarif
- name: Upload result to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: snyk.sarif

View File

@@ -9,9 +9,6 @@
* ADDED: Google Cloud Storage backend support (#795)
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
* CHANGED: Upgrading libraries to: random_compat 2.0.20
* CHANGED: Removed automatic `.ini` configuration file migration (#808)
* CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419)
* CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419)
* **1.3.5 (2021-04-05)**
* ADDED: Translation for Hebrew, Lithuanian, Indonesian and Catalan
* ADDED: Make the project info configurable (#681)

View File

@@ -2,7 +2,7 @@
CURRENT_VERSION = 1.3.5
VERSION ?= 1.3.6
VERSION_FILES = index.php cfg/ *.md css/ i18n/ img/ js/package.json js/privatebin.js lib/ Makefile tpl/ tst/
VERSION_FILES = index.php cfg/ *.md css/ i18n/ img/ js/privatebin.js lib/ Makefile tpl/ tst/
REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g")
REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g")

View File

@@ -87,7 +87,7 @@ languageselection = false
; async functions and display an error if not and for Chrome to enable
; webassembly support (used for zlib compression). You can remove it if Chrome
; doesn't need to be supported and old browsers don't need to be warned.
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' resource:; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval' resource:; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
; stay compatible with PrivateBin Alpha 0.19, less secure
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of
@@ -143,6 +143,9 @@ limit = 10
; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR
; header = "X_FORWARDED_FOR"
; directory to store the traffic limits in
dir = PATH "data"
[purge]
; minimum time limit between two purgings of expired pastes, it is only
; triggered when pastes are created
@@ -154,6 +157,9 @@ limit = 300
; site
batchsize = 10
; directory to store the purge limit in
dir = PATH "data"
[model]
; name of data model class to load and directory for storage
; the default model "Filesystem" stores everything in the filesystem

View File

@@ -8,32 +8,32 @@
"%s requires php %s or above to work. Sorry.": "%s requereix php %s o superior per funcionar. Ho sento.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requereix que la secció de configuració [%s] sigui present al fitxer de configuració.",
"Please wait %d seconds between each post.": [
"Espereu %d segon entre cada entrada. (singular)",
"Espereu %d segons entre cada entrada. (1r plural)",
"Espereu %d segons entre cada entrada. (2n plural)",
"Please wait %d seconds between each post. (3er plural)"
"Please wait %d second between each post. (singular)",
"Please wait %d seconds between each post. (1st plural)",
"Please wait %d seconds between each post. (2nd plural)",
"Please wait %d seconds between each post. (3rd plural)"
],
"Paste is limited to %s of encrypted data.": "L'enganxat està limitat a %s de dades encriptades.",
"Invalid data.": "Dades no vàlides.",
"You are unlucky. Try again.": "Mala sort. Torna-ho a provar.",
"Error saving comment. Sorry.": "S'ha produït un error en desar el comentari. Ho sento.",
"Error saving paste. Sorry.": "S'ha produït un error en desar l'enganxat. Ho sento.",
"Invalid paste ID.": "Identificador d'enganxament no vàlid.",
"Paste is limited to %s of encrypted data.": "Paste is limited to %s of encrypted data.",
"Invalid data.": "Invalid data.",
"You are unlucky. Try again.": "You are unlucky. Try again.",
"Error saving comment. Sorry.": "Error saving comment. Sorry.",
"Error saving paste. Sorry.": "Error saving paste. Sorry.",
"Invalid paste ID.": "Invalid paste ID.",
"Paste is not of burn-after-reading type.": "Paste is not of burn-after-reading type.",
"Wrong deletion token. Paste was not deleted.": "Wrong deletion token. Paste was not deleted.",
"Paste was properly deleted.": "Paste was properly deleted.",
"JavaScript is required for %s to work. Sorry for the inconvenience.": "Cal JavaScript perquè %s funcioni. Em sap greu les molèsties.",
"%s requires a modern browser to work.": "%s requereix un navegador modern per funcionar.",
"New": "Nou",
"Send": "Enviar",
"Clone": "Clona",
"Raw text": "Text sense processar",
"Expires": "Caducitat",
"Burn after reading": "Esborra després de ser llegit",
"Open discussion": "Discussió oberta",
"Password (recommended)": "Contrasenya (recomanat)",
"Discussion": "Discussió",
"Toggle navigation": "Alternar navegació",
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript is required for %s to work. Sorry for the inconvenience.",
"%s requires a modern browser to work.": "%s requires a modern browser to work.",
"New": "New",
"Send": "Send",
"Clone": "Clone",
"Raw text": "Raw text",
"Expires": "Expires",
"Burn after reading": "Burn after reading",
"Open discussion": "Open discussion",
"Password (recommended)": "Password (recommended)",
"Discussion": "Discussion",
"Toggle navigation": "Toggle navigation",
"%d seconds": [
"%d second (singular)",
"%d seconds (1st plural)",

View File

@@ -1,6 +1,6 @@
{
"name": "privatebin",
"version": "1.3.5",
"version": "1.3.0",
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
"main": "privatebin.js",
"directories": {

View File

@@ -1,6 +1,6 @@
'use strict';
(function () {
(function() {
let ret;
async function initialize() {
@@ -23,7 +23,16 @@
_abort: errno => { console.error(`Error: ${errno}`) },
_grow: () => { },
};
const ins = await WebAssembly.instantiateStreaming(fetch('js/zlib-1.2.11.wasm'), { env });
let buff;
if (typeof fetch === 'undefined') {
buff = fs.readFileSync('zlib-1.2.11.wasm');
} else {
const resp = await fetch('js/zlib-1.2.11.wasm');
buff = await resp.arrayBuffer();
}
const module = await WebAssembly.compile(buff);
const ins = await WebAssembly.instantiate(module, { env });
const srcPtr = ins.exports._malloc(CHUNK_SIZE);
const dstPtr = ins.exports._malloc(CHUNK_SIZE);

View File

@@ -14,6 +14,7 @@ namespace PrivateBin;
use Exception;
use PDO;
use PrivateBin\Persistence\DataStore;
/**
* Configuration
@@ -54,7 +55,7 @@ class Configuration
'urlshortener' => '',
'qrcode' => true,
'icon' => 'identicon',
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' resource:; style-src \'self\'; font-src \'self\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\' resource:; style-src \'self\'; font-src \'self\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
'zerobincompatibility' => false,
'httpwarning' => true,
'compression' => 'zlib',
@@ -80,11 +81,13 @@ class Configuration
'traffic' => array(
'limit' => 10,
'header' => null,
'dir' => 'data',
'exemptedIp' => null,
),
'purge' => array(
'limit' => 300,
'batchsize' => 10,
'dir' => 'data',
),
'model' => array(
'class' => 'Filesystem',
@@ -103,8 +106,20 @@ class Configuration
{
$config = array();
$basePath = (getenv('CONFIG_PATH') !== false ? getenv('CONFIG_PATH') : PATH . 'cfg') . DIRECTORY_SEPARATOR;
$configIni = $basePath . 'conf.ini';
$configFile = $basePath . 'conf.php';
// rename INI files to avoid configuration leakage
if (is_readable($configIni)) {
DataStore::prependRename($configIni, $configFile, ';');
// cleanup sample, too
$configIniSample = $configIni . '.sample';
if (is_readable($configIniSample)) {
DataStore::prependRename($configIniSample, $basePath . 'conf.sample.php', ';');
}
}
if (is_readable($configFile)) {
$config = parse_ini_file($configFile, true);
foreach (array('main', 'model', 'model_options') as $section) {

View File

@@ -162,6 +162,7 @@ class Controller
$this->_model = new Model($this->_conf);
$this->_request = new Request;
$this->_urlBase = $this->_request->getRequestUri();
ServerSalt::setPath($this->_conf->getKey('dir', 'traffic'));
// set default language
$lang = $this->_conf->getKey('languagedefault');
@@ -195,10 +196,9 @@ class Controller
*/
private function _create()
{
try {
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
ServerSalt::setStore($this->_model->getStore());
TrafficLimiter::setConfiguration($this->_conf);
TrafficLimiter::setStore($this->_model->getStore());
if (!TrafficLimiter::canPass()) {
$this->_return_message(
1, I18n::_(
@@ -208,6 +208,10 @@ class Controller
);
return;
}
} catch (Exception $e) {
$this->_return_message(1, I18n::_($e->getMessage()));
return;
}
$data = $this->_request->getData();
$isComment = array_key_exists('pasteid', $data) &&

View File

@@ -15,12 +15,12 @@ namespace PrivateBin\Data;
/**
* AbstractData
*
* Abstract model for data access, implemented as a singleton.
* Abstract model for PrivateBin data access, implemented as a singleton.
*/
abstract class AbstractData
{
/**
* Singleton instance
* singleton instance
*
* @access protected
* @static
@@ -29,18 +29,9 @@ abstract class AbstractData
protected static $_instance = null;
/**
* cache for the traffic limiter
* enforce singleton, disable constructor
*
* @access private
* @static
* @var array
*/
protected static $_last_cache = array();
/**
* Enforce singleton, disable constructor
*
* Instantiate using {@link getInstance()}, this object implements the singleton pattern.
* Instantiate using {@link getInstance()}, privatebin is a singleton object.
*
* @access protected
*/
@@ -49,9 +40,9 @@ abstract class AbstractData
}
/**
* Enforce singleton, disable cloning
* enforce singleton, disable cloning
*
* Instantiate using {@link getInstance()}, this object implements the singleton pattern.
* Instantiate using {@link getInstance()}, privatebin is a singleton object.
*
* @access private
*/
@@ -60,7 +51,7 @@ abstract class AbstractData
}
/**
* Get instance of singleton
* get instance of singleton
*
* @access public
* @static
@@ -139,46 +130,6 @@ abstract class AbstractData
*/
abstract public function existsComment($pasteid, $parentid, $commentid);
/**
* Purge outdated entries.
*
* @access public
* @param string $namespace
* @param int $time
* @return void
*/
public function purgeValues($namespace, $time)
{
if ($namespace === 'traffic_limiter') {
foreach (self::$_last_cache as $key => $last_submission) {
if ($last_submission <= $time) {
unset(self::$_last_cache[$key]);
}
}
}
}
/**
* Save a value.
*
* @access public
* @param string $value
* @param string $namespace
* @param string $key
* @return bool
*/
abstract public function setValue($value, $namespace, $key = '');
/**
* Load a value.
*
* @access public
* @param string $namespace
* @param string $key
* @return string
*/
abstract public function getValue($namespace, $key = '');
/**
* Returns up to batch size number of paste ids that have expired
*

View File

@@ -198,7 +198,6 @@ class Database extends AbstractData
$opendiscussion = $paste['adata'][2];
$burnafterreading = $paste['adata'][3];
}
try {
return self::_exec(
'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
' VALUES(?,?,?,?,?,?,?,?,?)',
@@ -214,9 +213,6 @@ class Database extends AbstractData
$attachmentname,
)
);
} catch (Exception $e) {
return false;
}
}
/**
@@ -233,14 +229,11 @@ class Database extends AbstractData
}
self::$_cache[$pasteid] = false;
try {
$paste = self::_select(
'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
' WHERE dataid = ?', array($pasteid), true
);
} catch (Exception $e) {
$paste = false;
}
if ($paste === false) {
return false;
}
@@ -355,7 +348,6 @@ class Database extends AbstractData
$meta[$key] = null;
}
}
try {
return self::_exec(
'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
' VALUES(?,?,?,?,?,?,?)',
@@ -369,9 +361,6 @@ class Database extends AbstractData
$meta[$createdKey],
)
);
} catch (Exception $e) {
return false;
}
}
/**
@@ -427,83 +416,11 @@ class Database extends AbstractData
*/
public function existsComment($pasteid, $parentid, $commentid)
{
try {
return (bool) self::_select(
'SELECT dataid FROM ' . self::_sanitizeIdentifier('comment') .
' WHERE pasteid = ? AND parentid = ? AND dataid = ?',
array($pasteid, $parentid, $commentid), true
);
} catch (Exception $e) {
return false;
}
}
/**
* Save a value.
*
* @access public
* @param string $value
* @param string $namespace
* @param string $key
* @return bool
*/
public function setValue($value, $namespace, $key = '')
{
if ($namespace === 'traffic_limiter') {
self::$_last_cache[$key] = $value;
try {
$value = Json::encode(self::$_last_cache);
} catch (Exception $e) {
return false;
}
}
return self::_exec(
'UPDATE ' . self::_sanitizeIdentifier('config') .
' SET value = ? WHERE id = ?',
array($value, strtoupper($namespace))
);
}
/**
* Load a value.
*
* @access public
* @param string $namespace
* @param string $key
* @return string
*/
public function getValue($namespace, $key = '')
{
$configKey = strtoupper($namespace);
$value = $this->_getConfig($configKey);
if ($value === '') {
// initialize the row, so that setValue can rely on UPDATE queries
self::_exec(
'INSERT INTO ' . self::_sanitizeIdentifier('config') .
' VALUES(?,?)',
array($configKey, '')
);
// migrate filesystem based salt into database
$file = 'data' . DIRECTORY_SEPARATOR . 'salt.php';
if ($namespace === 'salt' && is_readable($file)) {
$value = Filesystem::getInstance(array('dir' => 'data'))->getValue('salt');
$this->setValue($value, 'salt');
@unlink($file);
return $value;
}
}
if ($value && $namespace === 'traffic_limiter') {
try {
self::$_last_cache = Json::decode($value);
} catch (Exception $e) {
self::$_last_cache = array();
}
if (array_key_exists($key, self::$_last_cache)) {
return self::$_last_cache[$key];
}
}
return (string) $value;
}
/**
@@ -646,19 +563,16 @@ class Database extends AbstractData
* @access private
* @static
* @param string $key
* @throws PDOException
* @return string
*/
private static function _getConfig($key)
{
try {
$row = self::_select(
'SELECT value FROM ' . self::_sanitizeIdentifier('config') .
' WHERE id = ?', array($key), true
);
} catch (PDOException $e) {
return '';
}
return $row ? $row['value'] : '';
return $row['value'];
}
/**

View File

@@ -12,8 +12,7 @@
namespace PrivateBin\Data;
use Exception;
use PrivateBin\Json;
use PrivateBin\Persistence\DataStore;
/**
* Filesystem
@@ -22,29 +21,6 @@ use PrivateBin\Json;
*/
class Filesystem extends AbstractData
{
/**
* first line in paste or comment files, to protect their contents from browsing exposed data directories
*
* @const string
*/
const PROTECTION_LINE = '<?php http_response_code(403); /*';
/**
* line in generated .htaccess files, to protect exposed directories from being browsable on apache web servers
*
* @const string
*/
const HTACCESS_LINE = 'Require all denied';
/**
* path in which to persist something
*
* @access private
* @static
* @var string
*/
private static $_path = 'data';
/**
* get instance of singleton
*
@@ -64,7 +40,7 @@ class Filesystem extends AbstractData
is_array($options) &&
array_key_exists('dir', $options)
) {
self::$_path = $options['dir'];
DataStore::setPath($options['dir']);
}
return self::$_instance;
}
@@ -87,7 +63,7 @@ class Filesystem extends AbstractData
if (!is_dir($storagedir)) {
mkdir($storagedir, 0700, true);
}
return self::_store($file, $paste);
return DataStore::store($file, $paste);
}
/**
@@ -99,13 +75,12 @@ class Filesystem extends AbstractData
*/
public function read($pasteid)
{
if (
!$this->exists($pasteid) ||
!$paste = self::_get(self::_dataid2path($pasteid) . $pasteid . '.php')
) {
if (!$this->exists($pasteid)) {
return false;
}
return self::upgradePreV1Format($paste);
return self::upgradePreV1Format(
DataStore::get(self::_dataid2path($pasteid) . $pasteid . '.php')
);
}
/**
@@ -152,7 +127,7 @@ class Filesystem extends AbstractData
$pastePath = $basePath . '.php';
// convert to PHP protected files if needed
if (is_readable($basePath)) {
self::_prependRename($basePath, $pastePath);
DataStore::prependRename($basePath, $pastePath);
// convert comments, too
$discdir = self::_dataid2discussionpath($pasteid);
@@ -161,7 +136,7 @@ class Filesystem extends AbstractData
while (false !== ($filename = $dir->read())) {
if (substr($filename, -4) !== '.php' && strlen($filename) >= 16) {
$commentFilename = $discdir . $filename . '.php';
self::_prependRename($discdir . $filename, $commentFilename);
DataStore::prependRename($discdir . $filename, $commentFilename);
}
}
$dir->close();
@@ -190,7 +165,7 @@ class Filesystem extends AbstractData
if (!is_dir($storagedir)) {
mkdir($storagedir, 0700, true);
}
return self::_store($file, $comment);
return DataStore::store($file, $comment);
}
/**
@@ -212,7 +187,7 @@ class Filesystem extends AbstractData
// - commentid is the comment identifier itself.
// - parentid is the comment this comment replies to (It can be pasteid)
if (is_file($discdir . $filename)) {
$comment = self::_get($discdir . $filename);
$comment = DataStore::get($discdir . $filename);
$items = explode('.', $filename);
// Add some meta information not contained in file.
$comment['id'] = $items[1];
@@ -248,97 +223,6 @@ class Filesystem extends AbstractData
);
}
/**
* Save a value.
*
* @access public
* @param string $value
* @param string $namespace
* @param string $key
* @return bool
*/
public function setValue($value, $namespace, $key = '')
{
switch ($namespace) {
case 'purge_limiter':
return self::_storeString(
self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php',
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $value . ';'
);
case 'salt':
return self::_storeString(
self::$_path . DIRECTORY_SEPARATOR . 'salt.php',
'<?php # |' . $value . '|'
);
case 'traffic_limiter':
self::$_last_cache[$key] = $value;
return self::_storeString(
self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php',
'<?php' . PHP_EOL . '$GLOBALS[\'traffic_limiter\'] = ' . var_export(self::$_last_cache, true) . ';'
);
}
return false;
}
/**
* Load a value.
*
* @access public
* @param string $namespace
* @param string $key
* @return string
*/
public function getValue($namespace, $key = '')
{
switch ($namespace) {
case 'purge_limiter':
$file = self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php';
if (is_readable($file)) {
require $file;
return $GLOBALS['purge_limiter'];
}
break;
case 'salt':
$file = self::$_path . DIRECTORY_SEPARATOR . 'salt.php';
if (is_readable($file)) {
$items = explode('|', file_get_contents($file));
if (is_array($items) && count($items) == 3) {
return $items[1];
}
}
break;
case 'traffic_limiter':
$file = self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php';
if (is_readable($file)) {
require $file;
self::$_last_cache = $GLOBALS['traffic_limiter'];
if (array_key_exists($key, self::$_last_cache)) {
return self::$_last_cache[$key];
}
}
break;
}
return '';
}
/**
* get the data
*
* @access public
* @static
* @param string $filename
* @return array|false $data
*/
private static function _get($filename)
{
return Json::decode(
substr(
file_get_contents($filename),
strlen(self::PROTECTION_LINE . PHP_EOL)
)
);
}
/**
* Returns up to batch size number of paste ids that have expired
*
@@ -349,8 +233,9 @@ class Filesystem extends AbstractData
protected function _getExpiredPastes($batchsize)
{
$pastes = array();
$mainpath = DataStore::getPath();
$firstLevel = array_filter(
scandir(self::$_path),
scandir($mainpath),
'self::_isFirstLevelDir'
);
if (count($firstLevel) > 0) {
@@ -358,7 +243,7 @@ class Filesystem extends AbstractData
for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) {
$firstKey = array_rand($firstLevel);
$secondLevel = array_filter(
scandir(self::$_path . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
scandir($mainpath . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
'self::_isSecondLevelDir'
);
@@ -369,7 +254,7 @@ class Filesystem extends AbstractData
}
$secondKey = array_rand($secondLevel);
$path = self::$_path . DIRECTORY_SEPARATOR .
$path = $mainpath . DIRECTORY_SEPARATOR .
$firstLevel[$firstKey] . DIRECTORY_SEPARATOR .
$secondLevel[$secondKey];
if (!is_dir($path)) {
@@ -429,9 +314,10 @@ class Filesystem extends AbstractData
*/
private static function _dataid2path($dataid)
{
return self::$_path . DIRECTORY_SEPARATOR .
return DataStore::getPath(
substr($dataid, 0, 2) . DIRECTORY_SEPARATOR .
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR;
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR
);
}
/**
@@ -461,7 +347,7 @@ class Filesystem extends AbstractData
private static function _isFirstLevelDir($element)
{
return self::_isSecondLevelDir($element) &&
is_dir(self::$_path . DIRECTORY_SEPARATOR . $element);
is_dir(DataStore::getPath($element));
}
/**
@@ -476,97 +362,4 @@ class Filesystem extends AbstractData
{
return (bool) preg_match('/^[a-f0-9]{2}$/', $element);
}
/**
* store the data
*
* @access public
* @static
* @param string $filename
* @param array $data
* @return bool
*/
private static function _store($filename, array $data)
{
try {
return self::_storeString(
$filename,
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
);
} catch (Exception $e) {
return false;
}
}
/**
* store a string
*
* @access public
* @static
* @param string $filename
* @param string $data
* @return bool
*/
private static function _storeString($filename, $data)
{
// Create storage directory if it does not exist.
if (!is_dir(self::$_path)) {
if (!@mkdir(self::$_path, 0700)) {
return false;
}
}
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
if (!is_file($file)) {
$writtenBytes = 0;
if ($fileCreated = @touch($file)) {
$writtenBytes = @file_put_contents(
$file,
self::HTACCESS_LINE . PHP_EOL,
LOCK_EX
);
}
if (
$fileCreated === false ||
$writtenBytes === false ||
$writtenBytes < strlen(self::HTACCESS_LINE . PHP_EOL)
) {
return false;
}
}
$fileCreated = true;
$writtenBytes = 0;
if (!is_file($filename)) {
$fileCreated = @touch($filename);
}
if ($fileCreated) {
$writtenBytes = @file_put_contents($filename, $data, LOCK_EX);
}
if ($fileCreated === false || $writtenBytes === false || $writtenBytes < strlen($data)) {
return false;
}
@chmod($filename, 0640); // protect file from access by other users on the host
return true;
}
/**
* rename a file, prepending the protection line at the beginning
*
* @access public
* @static
* @param string $srcFile
* @param string $destFile
* @return void
*/
private static function _prependRename($srcFile, $destFile)
{
// don't overwrite already converted file
if (!is_readable($destFile)) {
$handle = fopen($srcFile, 'r', false, stream_context_create());
file_put_contents($destFile, self::PROTECTION_LINE . PHP_EOL);
file_put_contents($destFile, $handle, FILE_APPEND);
fclose($handle);
}
unlink($srcFile);
}
}

View File

@@ -4,39 +4,11 @@ namespace PrivateBin\Data;
use Exception;
use Google\Cloud\Core\Exception\NotFoundException;
use Google\Cloud\Storage\Bucket;
use Google\Cloud\Storage\StorageClient;
use PrivateBin\Json;
class GoogleCloudStorage extends AbstractData
{
/**
* GCS client
*
* @access private
* @static
* @var StorageClient
*/
private static $_client = null;
/**
* GCS bucket
*
* @access private
* @static
* @var Bucket
*/
private static $_bucket = null;
/**
* object prefix
*
* @access private
* @static
* @var string
*/
private static $_prefix = 'pastes';
/**
* returns a Google Cloud Storage data backend.
*
@@ -47,12 +19,10 @@ class GoogleCloudStorage extends AbstractData
*/
public static function getInstance(array $options)
{
// if needed initialize the singleton
if (!(self::$_instance instanceof self)) {
self::$_instance = new self;
}
$client = null;
$bucket = null;
$prefix = 'pastes';
if (getenv('PRIVATEBIN_GCS_BUCKET')) {
$bucket = getenv('PRIVATEBIN_GCS_BUCKET');
}
@@ -60,36 +30,53 @@ class GoogleCloudStorage extends AbstractData
$bucket = $options['bucket'];
}
if (is_array($options) && array_key_exists('prefix', $options)) {
self::$_prefix = $options['prefix'];
$prefix = $options['prefix'];
}
if (is_array($options) && array_key_exists('client', $options)) {
$client = $options['client'];
}
if (empty(self::$_client)) {
self::$_client = class_exists('StorageClientStub', false) ?
new \StorageClientStub(array()) :
new StorageClient(array('suppressKeyFileNotice' => true));
if (!(self::$_instance instanceof self)) {
self::$_instance = new self($bucket, $prefix, $client);
}
self::$_bucket = self::$_client->bucket($bucket);
return self::$_instance;
}
protected $_client = null;
protected $_bucket = null;
protected $_prefix = 'pastes';
public function __construct($bucket, $prefix, $client = null)
{
parent::__construct();
if ($client == null) {
$this->_client = new StorageClient(array('suppressKeyFileNotice' => true));
} else {
// use given client for test purposes
$this->_client = $client;
}
$this->_bucket = $this->_client->bucket($bucket);
if ($prefix != null) {
$this->_prefix = $prefix;
}
}
/**
* returns the google storage object key for $pasteid in self::$_bucket.
*
* @access private
* returns the google storage object key for $pasteid in $this->_bucket.
* @param $pasteid string to get the key for
* @return string
*/
private function _getKey($pasteid)
{
if (self::$_prefix != '') {
return self::$_prefix . '/' . $pasteid;
if ($this->_prefix != '') {
return $this->_prefix . '/' . $pasteid;
}
return $pasteid;
}
/**
* Uploads the payload in the self::$_bucket under the specified key.
* Uploads the payload in the $this->_bucket under the specified key.
* The entire payload is stored as a JSON document. The metadata is replicated
* as the GCS object's metadata except for the fields attachment, attachmentname
* and salt.
@@ -98,7 +85,7 @@ class GoogleCloudStorage extends AbstractData
* @param $payload array to store
* @return bool true if successful, otherwise false.
*/
private function _upload($key, $payload)
private function upload($key, $payload)
{
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
@@ -106,7 +93,7 @@ class GoogleCloudStorage extends AbstractData
$metadata[$k] = strval($v);
}
try {
self::$_bucket->upload(Json::encode($payload), array(
$this->_bucket->upload(Json::encode($payload), array(
'name' => $key,
'chunkSize' => 262144,
'predefinedAcl' => 'private',
@@ -116,7 +103,7 @@ class GoogleCloudStorage extends AbstractData
),
));
} catch (Exception $e) {
error_log('failed to upload ' . $key . ' to ' . self::$_bucket->name() . ', ' .
error_log('failed to upload ' . $key . ' to ' . $this->_bucket->name() . ', ' .
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
return false;
}
@@ -132,7 +119,7 @@ class GoogleCloudStorage extends AbstractData
return false;
}
return $this->_upload($this->_getKey($pasteid), $paste);
return $this->upload($this->_getKey($pasteid), $paste);
}
/**
@@ -141,13 +128,13 @@ class GoogleCloudStorage extends AbstractData
public function read($pasteid)
{
try {
$o = self::$_bucket->object($this->_getKey($pasteid));
$o = $this->_bucket->object($this->_getKey($pasteid));
$data = $o->downloadAsString();
return Json::decode($data);
} catch (NotFoundException $e) {
return false;
} catch (Exception $e) {
error_log('failed to read ' . $pasteid . ' from ' . self::$_bucket->name() . ', ' .
error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket->name() . ', ' .
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
return false;
}
@@ -161,9 +148,9 @@ class GoogleCloudStorage extends AbstractData
$name = $this->_getKey($pasteid);
try {
foreach (self::$_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) {
foreach ($this->_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) {
try {
self::$_bucket->object($comment->name())->delete();
$this->_bucket->object($comment->name())->delete();
} catch (NotFoundException $e) {
// ignore if already deleted.
}
@@ -173,7 +160,7 @@ class GoogleCloudStorage extends AbstractData
}
try {
self::$_bucket->object($name)->delete();
$this->_bucket->object($name)->delete();
} catch (NotFoundException $e) {
// ignore if already deleted
}
@@ -184,7 +171,7 @@ class GoogleCloudStorage extends AbstractData
*/
public function exists($pasteid)
{
$o = self::$_bucket->object($this->_getKey($pasteid));
$o = $this->_bucket->object($this->_getKey($pasteid));
return $o->exists();
}
@@ -197,7 +184,7 @@ class GoogleCloudStorage extends AbstractData
return false;
}
$key = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
return $this->_upload($key, $comment);
return $this->upload($key, $comment);
}
/**
@@ -208,8 +195,8 @@ class GoogleCloudStorage extends AbstractData
$comments = array();
$prefix = $this->_getKey($pasteid) . '/discussion/';
try {
foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $key) {
$comment = JSON::decode(self::$_bucket->object($key->name())->downloadAsString());
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
$comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString());
$comment['id'] = basename($key->name());
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
$comments[$slot] = $comment;
@@ -226,92 +213,10 @@ class GoogleCloudStorage extends AbstractData
public function existsComment($pasteid, $parentid, $commentid)
{
$name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
$o = self::$_bucket->object($name);
$o = $this->_bucket->object($name);
return $o->exists();
}
/**
* @inheritDoc
*/
public function purgeValues($namespace, $time)
{
$path = 'config/' . $namespace;
try {
foreach (self::$_bucket->objects(array('prefix' => $path)) as $object) {
$name = $object->name();
if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') {
continue;
}
$info = $object->info();
if (key_exists('metadata', $info) && key_exists('value', $info['metadata'])) {
$value = $info['metadata']['value'];
if (is_numeric($value) && intval($value) < $time) {
try {
$object->delete();
} catch (NotFoundException $e) {
// deleted by another instance.
}
}
}
}
} catch (NotFoundException $e) {
// no objects in the bucket yet
}
}
/**
* For GoogleCloudStorage, the value will also be stored in the metadata for the
* namespaces traffic_limiter and purge_limiter.
* @inheritDoc
*/
public function setValue($value, $namespace, $key = '')
{
if ($key === '') {
$key = 'config/' . $namespace;
} else {
$key = 'config/' . $namespace . '/' . $key;
}
$metadata = array('namespace' => $namespace);
if ($namespace != 'salt') {
$metadata['value'] = strval($value);
}
try {
self::$_bucket->upload($value, array(
'name' => $key,
'chunkSize' => 262144,
'predefinedAcl' => 'private',
'metadata' => array(
'content-type' => 'application/json',
'metadata' => $metadata,
),
));
} catch (Exception $e) {
error_log('failed to set key ' . $key . ' to ' . self::$_bucket->name() . ', ' .
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
return false;
}
return true;
}
/**
* @inheritDoc
*/
public function getValue($namespace, $key = '')
{
if ($key === '') {
$key = 'config/' . $namespace;
} else {
$key = 'config/' . $namespace . '/' . $key;
}
try {
$o = self::$_bucket->object($key);
return $o->downloadAsString();
} catch (NotFoundException $e) {
return '';
}
}
/**
* @inheritDoc
*/
@@ -320,12 +225,12 @@ class GoogleCloudStorage extends AbstractData
$expired = array();
$now = time();
$prefix = self::$_prefix;
$prefix = $this->_prefix;
if ($prefix != '') {
$prefix .= '/';
$prefix = $prefix . '/';
}
try {
foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $object) {
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) {
$metadata = $object->info()['metadata'];
if ($metadata != null && array_key_exists('expire_date', $metadata)) {
$expire_at = intval($metadata['expire_date']);

View File

@@ -52,11 +52,6 @@ class FormatV2
}
}
// Make sure adata is an array.
if (!is_array($message['adata'])) {
return false;
}
$cipherParams = $isComment ? $message['adata'] : $message['adata'][0];
// Make sure some fields are base64 data:

View File

@@ -44,13 +44,13 @@ class Json
* @static
* @param string $input
* @throws Exception
* @return mixed
* @return array
*/
public static function decode($input)
{
$output = json_decode($input, true);
$array = json_decode($input, true);
self::_detectError();
return $output;
return $array;
}
/**

View File

@@ -54,7 +54,7 @@ class Model
*/
public function getPaste($pasteId = null)
{
$paste = new Paste($this->_conf, $this->getStore());
$paste = new Paste($this->_conf, $this->_getStore());
if ($pasteId !== null) {
$paste->setId($pasteId);
}
@@ -67,9 +67,8 @@ class Model
public function purge()
{
PurgeLimiter::setConfiguration($this->_conf);
PurgeLimiter::setStore($this->getStore());
if (PurgeLimiter::canPurge()) {
$this->getStore()->purge($this->_conf->getKey('batchsize', 'purge'));
$this->_getStore()->purge($this->_conf->getKey('batchsize', 'purge'));
}
}
@@ -78,7 +77,7 @@ class Model
*
* @return Data\AbstractData
*/
public function getStore()
private function _getStore()
{
if ($this->_store === null) {
$this->_store = forward_static_call(

View File

@@ -93,7 +93,7 @@ class Paste extends AbstractModel
}
$this->_data['meta']['created'] = time();
$this->_data['meta']['salt'] = ServerSalt::generate();
$this->_data['meta']['salt'] = serversalt::generate();
// store paste
if (

View File

@@ -12,7 +12,7 @@
namespace PrivateBin\Persistence;
use PrivateBin\Data\AbstractData;
use Exception;
/**
* AbstractPersistence
@@ -22,23 +22,104 @@ use PrivateBin\Data\AbstractData;
abstract class AbstractPersistence
{
/**
* data storage to use to persist something
* path in which to persist something
*
* @access private
* @static
* @var AbstractData
* @var string
*/
protected static $_store;
private static $_path = 'data';
/**
* set the path
*
* @access public
* @static
* @param AbstractData $store
* @param string $path
*/
public static function setStore(AbstractData $store)
public static function setPath($path)
{
self::$_store = $store;
self::$_path = $path;
}
/**
* get the path
*
* @access public
* @static
* @param string $filename
* @return string
*/
public static function getPath($filename = null)
{
if (strlen($filename)) {
return self::$_path . DIRECTORY_SEPARATOR . $filename;
} else {
return self::$_path;
}
}
/**
* checks if the file exists
*
* @access protected
* @static
* @param string $filename
* @return bool
*/
protected static function _exists($filename)
{
self::_initialize();
return is_file(self::$_path . DIRECTORY_SEPARATOR . $filename);
}
/**
* prepares path for storage
*
* @access protected
* @static
* @throws Exception
*/
protected static function _initialize()
{
// Create storage directory if it does not exist.
if (!is_dir(self::$_path)) {
if (!@mkdir(self::$_path, 0700)) {
throw new Exception('unable to create directory ' . self::$_path, 10);
}
}
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
if (!is_file($file)) {
$writtenBytes = @file_put_contents(
$file,
'Require all denied' . PHP_EOL,
LOCK_EX
);
if ($writtenBytes === false || $writtenBytes < 19) {
throw new Exception('unable to write to file ' . $file, 11);
}
}
}
/**
* store the data
*
* @access protected
* @static
* @param string $filename
* @param string $data
* @throws Exception
* @return string
*/
protected static function _store($filename, $data)
{
self::_initialize();
$file = self::$_path . DIRECTORY_SEPARATOR . $filename;
$writtenBytes = @file_put_contents($file, $data, LOCK_EX);
if ($writtenBytes === false || $writtenBytes < strlen($data)) {
throw new Exception('unable to write to file ' . $file, 13);
}
@chmod($file, 0640); // protect file access
return $file;
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* PrivateBin
*
* a zero-knowledge paste bin
*
* @link https://github.com/PrivateBin/PrivateBin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 1.3.5
*/
namespace PrivateBin\Persistence;
use Exception;
use PrivateBin\Json;
/**
* DataStore
*
* Handles data storage for Data\Filesystem.
*/
class DataStore extends AbstractPersistence
{
/**
* first line in file, to protect its contents
*
* @const string
*/
const PROTECTION_LINE = '<?php http_response_code(403); /*';
/**
* store the data
*
* @access public
* @static
* @param string $filename
* @param array $data
* @return bool
*/
public static function store($filename, $data)
{
$path = self::getPath();
if (strpos($filename, $path) === 0) {
$filename = substr($filename, strlen($path));
}
try {
self::_store(
$filename,
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
);
return true;
} catch (Exception $e) {
return false;
}
}
/**
* get the data
*
* @access public
* @static
* @param string $filename
* @return array|false $data
*/
public static function get($filename)
{
return Json::decode(
substr(
file_get_contents($filename),
strlen(self::PROTECTION_LINE . PHP_EOL)
)
);
}
/**
* rename a file, prepending the protection line at the beginning
*
* @access public
* @static
* @param string $srcFile
* @param string $destFile
* @param string $prefix (optional)
* @return void
*/
public static function prependRename($srcFile, $destFile, $prefix = '')
{
// don't overwrite already converted file
if (!is_readable($destFile)) {
$handle = fopen($srcFile, 'r', false, stream_context_create());
file_put_contents($destFile, $prefix . self::PROTECTION_LINE . PHP_EOL);
file_put_contents($destFile, $handle, FILE_APPEND);
fclose($handle);
}
unlink($srcFile);
}
}

View File

@@ -52,6 +52,7 @@ class PurgeLimiter extends AbstractPersistence
public static function setConfiguration(Configuration $conf)
{
self::setLimit($conf->getKey('limit', 'purge'));
self::setPath($conf->getKey('dir', 'purge'));
}
/**
@@ -59,6 +60,7 @@ class PurgeLimiter extends AbstractPersistence
*
* @access public
* @static
* @throws \Exception
* @return bool
*/
public static function canPurge()
@@ -69,14 +71,17 @@ class PurgeLimiter extends AbstractPersistence
}
$now = time();
$pl = (int) self::$_store->getValue('purge_limiter');
$file = 'purge_limiter.php';
if (self::_exists($file)) {
require self::getPath($file);
$pl = $GLOBALS['purge_limiter'];
if ($pl + self::$_limit >= $now) {
return false;
}
$hasStored = self::$_store->setValue((string) $now, 'purge_limiter');
if (!$hasStored) {
error_log('failed to store the purge limiter, skipping purge cycle to avoid getting stuck in a purge loop');
}
return $hasStored;
$content = '<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $now . ';';
self::_store($file, $content);
return true;
}
}

View File

@@ -12,7 +12,7 @@
namespace PrivateBin\Persistence;
use PrivateBin\Data\AbstractData;
use Exception;
/**
* ServerSalt
@@ -26,6 +26,15 @@ use PrivateBin\Data\AbstractData;
*/
class ServerSalt extends AbstractPersistence
{
/**
* file where salt is saved to
*
* @access private
* @static
* @var string
*/
private static $_file = 'salt.php';
/**
* generated salt
*
@@ -44,7 +53,8 @@ class ServerSalt extends AbstractPersistence
*/
public static function generate()
{
return bin2hex(random_bytes(256));
$randomSalt = bin2hex(random_bytes(256));
return $randomSalt;
}
/**
@@ -52,6 +62,7 @@ class ServerSalt extends AbstractPersistence
*
* @access public
* @static
* @throws Exception
* @return string
*/
public static function get()
@@ -60,14 +71,20 @@ class ServerSalt extends AbstractPersistence
return self::$_salt;
}
$salt = self::$_store->getValue('salt');
if ($salt) {
self::$_salt = $salt;
if (self::_exists(self::$_file)) {
if (is_readable(self::getPath(self::$_file))) {
$items = explode('|', file_get_contents(self::getPath(self::$_file)));
}
if (!isset($items) || !is_array($items) || count($items) != 3) {
throw new Exception('unable to read file ' . self::getPath(self::$_file), 20);
}
self::$_salt = $items[1];
} else {
self::$_salt = self::generate();
if (!self::$_store->setValue(self::$_salt, 'salt')) {
error_log('failed to store the server salt, delete tokens, traffic limiter and user icons won\'t work');
}
self::_store(
self::$_file,
'<?php # |' . self::$_salt . '|'
);
}
return self::$_salt;
}
@@ -77,11 +94,11 @@ class ServerSalt extends AbstractPersistence
*
* @access public
* @static
* @param AbstractData $store
* @param string $path
*/
public static function setStore(AbstractData $store)
public static function setPath($path)
{
self::$_salt = '';
parent::setStore($store);
parent::setPath($path);
}
}

View File

@@ -13,6 +13,7 @@
namespace PrivateBin\Persistence;
use Exception;
use IPLib\Factory;
use PrivateBin\Configuration;
@@ -84,6 +85,7 @@ class TrafficLimiter extends AbstractPersistence
public static function setConfiguration(Configuration $conf)
{
self::setLimit($conf->getKey('limit', 'traffic'));
self::setPath($conf->getKey('dir', 'traffic'));
self::setExemptedIp($conf->getKey('exemptedIp', 'traffic'));
if (($option = $conf->getKey('header', 'traffic')) !== null) {
@@ -132,7 +134,13 @@ class TrafficLimiter extends AbstractPersistence
return false;
}
// Ip-lib throws an exception when something goes wrong, if so we want to catch it and set contained to false
try {
return $address->matches($range);
} catch (Exception $e) {
// If something is wrong with matching the ip, we assume it doesn't match
return false;
}
}
/**
@@ -142,6 +150,7 @@ class TrafficLimiter extends AbstractPersistence
*
* @access public
* @static
* @throws Exception
* @return bool
*/
public static function canPass()
@@ -161,20 +170,35 @@ class TrafficLimiter extends AbstractPersistence
}
}
$file = 'traffic_limiter.php';
if (self::_exists($file)) {
require self::getPath($file);
$tl = $GLOBALS['traffic_limiter'];
} else {
$tl = array();
}
// purge file of expired hashes to keep it small
$now = time();
foreach ($tl as $key => $time) {
if ($time + self::$_limit < $now) {
unset($tl[$key]);
}
}
// this hash is used as an array key, hence a shorter algo is used
$hash = self::getHash('sha256');
$now = time();
$tl = (int) self::$_store->getValue('traffic_limiter', $hash);
self::$_store->purgeValues('traffic_limiter', $now - self::$_limit);
if ($tl > 0 && ($tl + self::$_limit >= $now)) {
if (array_key_exists($hash, $tl) && ($tl[$hash] + self::$_limit >= $now)) {
$result = false;
} else {
$tl = time();
$tl[$hash] = time();
$result = true;
}
if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) {
error_log('failed to store the traffic limiter, it probably contains outdated information');
}
self::_store(
$file,
'<?php' . PHP_EOL .
'$GLOBALS[\'traffic_limiter\'] = ' . var_export($tl, true) . ';'
);
return $result;
}
}

View File

@@ -108,8 +108,6 @@ class Request
case 'DELETE':
case 'PUT':
case 'POST':
// it might be a creation or a deletion, the latter is detected below
$this->_operation = 'create';
$this->_params = Json::decode(
file_get_contents(self::$_inputStream)
);
@@ -127,10 +125,15 @@ class Request
}
// prepare operation, depending on current parameters
if (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
if (
array_key_exists('ct', $this->_params) &&
!empty($this->_params['ct'])
) {
$this->_operation = 'create';
} elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken'])) {
$this->_operation = 'delete';
} elseif ($this->_operation != 'create') {
} else {
$this->_operation = 'read';
}
} elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld'])) {
@@ -169,7 +172,7 @@ class Request
$data['meta'] = $meta;
}
foreach ($required_keys as $key) {
$data[$key] = $this->getParam($key, $key == 'v' ? 1 : '');
$data[$key] = $this->getParam($key);
}
// forcing a cast to int or float
$data['v'] = $data['v'] + 0;
@@ -285,7 +288,7 @@ class Request
}
krsort($mediaTypes);
foreach ($mediaTypes as $acceptedQuality => $acceptedValues) {
if ($acceptedQuality === '0.0') {
if ($acceptedQuality === 0.0) {
continue;
}
foreach ($acceptedValues as $acceptedValue) {

View File

@@ -54,7 +54,7 @@ if ($ZEROBINCOMPATIBILITY) :
<?php
endif;
?>
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Ji/WNgLpfd+sJgYjJv67Tjs4aYeMVrMeYIgCp5VU+EB8INzpfilh0ptjBHa3xem8tLCMROgJR10ilK3saci2YA==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Yey/0yoaVmSbqMEyyff3DIu8kCPwpHvHf7tY1AuZ1lrX9NPCMg87PwzngMi+VNbe4ilCApmePeuKT869RTcyCQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.7.js" integrity="sha512-/Bi1AJIP0TtxEB+Jh6Hk809H1G7vn4iJV80qagslf0+Hm0UjUi1s3qNrn1kZULjzUYuaf6ck0ndLGJ7MxWLmgQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.3.7.js" integrity="sha512-iztkobsvnjKfAtTNdHkGVjAYTrrtlC7mGp/54c40wowO7LhURYl3gVzzcEqGl/qKXQltJ2HwMrdLcNUdo+N/RQ==" crossorigin="anonymous"></script>

View File

@@ -33,7 +33,7 @@ if ($ZEROBINCOMPATIBILITY):
<?php
endif;
?>
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Ji/WNgLpfd+sJgYjJv67Tjs4aYeMVrMeYIgCp5VU+EB8INzpfilh0ptjBHa3xem8tLCMROgJR10ilK3saci2YA==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Yey/0yoaVmSbqMEyyff3DIu8kCPwpHvHf7tY1AuZ1lrX9NPCMg87PwzngMi+VNbe4ilCApmePeuKT869RTcyCQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.7.js" integrity="sha512-/Bi1AJIP0TtxEB+Jh6Hk809H1G7vn4iJV80qagslf0+Hm0UjUi1s3qNrn1kZULjzUYuaf6ck0ndLGJ7MxWLmgQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
<?php

View File

@@ -1,11 +1,5 @@
<?php
use Google\Cloud\Core\Exception\BadRequestException;
use Google\Cloud\Core\Exception\NotFoundException;
use Google\Cloud\Storage\Bucket;
use Google\Cloud\Storage\Connection\ConnectionInterface;
use Google\Cloud\Storage\StorageClient;
use Google\Cloud\Storage\StorageObject;
use PrivateBin\Persistence\ServerSalt;
error_reporting(E_ALL | E_STRICT);
@@ -27,586 +21,6 @@ if (!defined('CONF_SAMPLE')) {
require PATH . 'vendor/autoload.php';
Helper::updateSubresourceIntegrity();
/**
* Class StorageClientStub provides a limited stub for performing the unit test
*/
class StorageClientStub extends StorageClient
{
private $_config = null;
private $_connection = null;
private $_buckets = array();
public function __construct(array $config = array())
{
$this->_config = $config;
$this->_connection = new ConnectionInterfaceStub();
}
public function bucket($name, $userProject = false)
{
if (!key_exists($name, $this->_buckets)) {
$b = new BucketStub($this->_connection, $name, array(), $this);
$this->_buckets[$name] = $b;
}
return $this->_buckets[$name];
}
/**
* @throws \Google\Cloud\Core\Exception\NotFoundException
*/
public function deleteBucket($name)
{
if (key_exists($name, $this->_buckets)) {
unset($this->_buckets[$name]);
} else {
throw new NotFoundException();
}
}
public function buckets(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function registerStreamWrapper($protocol = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function unregisterStreamWrapper($protocol = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUrlUploader($uri, $data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function timestamp(\DateTimeInterface $timestamp, $nanoSeconds = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function getServiceAccount(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function hmacKeys(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function hmacKey($accessId, $projectId = null, array $metadata = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function createHmacKey($serviceAccountEmail, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function createBucket($name, array $options = array())
{
if (key_exists($name, $this->_buckets)) {
throw new BadRequestException('already exists');
}
$b = new BucketStub($this->_connection, $name, array(), $this);
$this->_buckets[$name] = $b;
return $b;
}
}
/**
* Class BucketStub stubs a GCS bucket.
*/
class BucketStub extends Bucket
{
public $_objects;
private $_name;
private $_info;
private $_connection;
private $_client;
public function __construct(ConnectionInterface $connection, $name, array $info = array(), $client = null)
{
$this->_name = $name;
$this->_info = $info;
$this->_connection = $connection;
$this->_objects = array();
$this->_client = $client;
}
public function acl()
{
throw new BadMethodCallException('not supported by this stub');
}
public function defaultAcl()
{
throw new BadMethodCallException('not supported by this stub');
}
public function exists()
{
return true;
}
public function upload($data, array $options = array())
{
if (!is_string($data) || !key_exists('name', $options)) {
throw new BadMethodCallException('not supported by this stub');
}
$name = $options['name'];
$generation = '1';
$o = new StorageObjectStub($this->_connection, $name, $this, $generation, $options);
$this->_objects[$options['name']] = $o;
$o->setData($data);
}
public function uploadAsync($data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getResumableUploader($data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getStreamableUploader($data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function object($name, array $options = array())
{
if (key_exists($name, $this->_objects)) {
return $this->_objects[$name];
} else {
return new StorageObjectStub($this->_connection, $name, $this, null, $options);
}
}
public function objects(array $options = array())
{
$prefix = key_exists('prefix', $options) ? $options['prefix'] : '';
return new CallbackFilterIterator(
new ArrayIterator($this->_objects),
function ($current, $key, $iterator) use ($prefix) {
return substr($key, 0, strlen($prefix)) == $prefix;
}
);
}
public function createNotification($topic, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function notification($id)
{
throw new BadMethodCallException('not supported by this stub');
}
public function notifications(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function delete(array $options = array())
{
$this->_client->deleteBucket($this->_name);
}
public function update(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function compose(array $sourceObjects, $name, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function info(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function reload(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function name()
{
return $this->_name;
}
public static function lifecycle(array $lifecycle = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function currentLifecycle(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function isWritable($file = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function iam()
{
throw new BadMethodCallException('not supported by this stub');
}
public function lockRetentionPolicy(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUrl($expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function generateSignedPostPolicyV4($objectName, $expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
}
/**
* Class StorageObjectStub stubs a GCS storage object.
*/
class StorageObjectStub extends StorageObject
{
private $_name;
private $_data;
private $_info;
private $_bucket;
private $_generation;
private $_exists = false;
private $_connection;
public function __construct(ConnectionInterface $connection, $name, $bucket, $generation = null, array $info = array(), $encryptionKey = null, $encryptionKeySHA256 = null)
{
$this->_name = $name;
$this->_bucket = $bucket;
$this->_generation = $generation;
$this->_info = $info;
$this->_connection = $connection;
$timeCreated = new Datetime();
$this->_info['metadata']['timeCreated'] = $timeCreated->format('Y-m-d\TH:i:s.u\Z');
}
public function acl()
{
throw new BadMethodCallException('not supported by this stub');
}
public function exists(array $options = array())
{
return key_exists($this->_name, $this->_bucket->_objects);
}
/**
* @throws NotFoundException
*/
public function delete(array $options = array())
{
if (key_exists($this->_name, $this->_bucket->_objects)) {
unset($this->_bucket->_objects[$this->_name]);
} else {
throw new NotFoundException('key ' . $this->_name . ' not found.');
}
}
/**
* @throws NotFoundException
*/
public function update(array $metadata, array $options = array())
{
if (!$this->_exists) {
throw new NotFoundException('key ' . $this->_name . ' not found.');
}
$this->_info = $metadata;
}
public function copy($destination, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function rewrite($destination, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function rename($name, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
/**
* @throws NotFoundException
*/
public function downloadAsString(array $options = array())
{
if (!$this->_exists) {
throw new NotFoundException('key ' . $this->_name . ' not found.');
}
return $this->_data;
}
public function downloadToFile($path, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function downloadAsStream(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function downloadAsStreamAsync(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUrl($expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUploadUrl($expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function beginSignedUploadSession(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function info(array $options = array())
{
return key_exists('metadata',$this->_info) ? $this->_info['metadata'] : array();
}
public function reload(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function name()
{
return $this->_name;
}
public function identity()
{
throw new BadMethodCallException('not supported by this stub');
}
public function gcsUri()
{
return sprintf(
'gs://%s/%s',
$this->_bucket->name(),
$this->_name
);
}
public function setData($data)
{
$this->_data = $data;
$this->_exists = true;
}
}
/**
* Class ConnectionInterfaceStub required for the stubs.
*/
class ConnectionInterfaceStub implements ConnectionInterface
{
public function deleteAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function patchAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listBuckets(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getBucketIamPolicy(array $args)
{
throw new BadMethodCallException('not supported by this stub');
}
public function setBucketIamPolicy(array $args)
{
throw new BadMethodCallException('not supported by this stub');
}
public function testBucketIamPermissions(array $args)
{
throw new BadMethodCallException('not supported by this stub');
}
public function patchBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function copyObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function rewriteObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function composeObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listObjects(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function patchObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function downloadObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getNotification(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteNotification(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertNotification(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listNotifications(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getServiceAccount(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function lockRetentionPolicy(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function createHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function updateHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listHmacKeys(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
}
/**
* Class Helper provides unit tests pastes and comments of various formats
*/
class Helper
{
/**
@@ -741,11 +155,7 @@ class Helper
public static function getPastePost($version = 2, array $meta = array())
{
$example = self::getPaste($version, $meta);
if ($version == 2) {
$example['meta'] = array('expire' => $example['meta']['expire']);
} else {
unset($example['meta']['postdate']);
}
return $example;
}

View File

@@ -17,6 +17,8 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
$this->_minimalConfig = '[main]' . PHP_EOL . '[model]' . PHP_EOL . '[model_options]';
$this->_options = Configuration::getDefaults();
$this->_options['model_options']['dir'] = PATH . $this->_options['model_options']['dir'];
$this->_options['traffic']['dir'] = PATH . $this->_options['traffic']['dir'];
$this->_options['purge']['dir'] = PATH . $this->_options['purge']['dir'];
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_cfg';
if (!is_dir($this->_path)) {
mkdir($this->_path);
@@ -145,6 +147,44 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
$this->assertEquals('Database', $conf->getKey('class', 'model'), 'old db class gets renamed');
}
public function testHandleConfigFileRename()
{
$options = $this->_options;
Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample', $options);
$options['main']['opendiscussion'] = true;
$options['main']['fileupload'] = true;
$options['main']['template'] = 'darkstrap';
Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', $options);
$conf = new Configuration;
$this->assertFileExists(CONF, 'old configuration file gets converted');
$this->assertFileNotExists(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', 'old configuration file gets removed');
$this->assertFileNotExists(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample', 'old configuration sample file gets removed');
$this->assertTrue(
$conf->getKey('opendiscussion') &&
$conf->getKey('fileupload') &&
$conf->getKey('template') === 'darkstrap',
'configuration values get converted'
);
}
public function testRenameIniSample()
{
$iniSample = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample';
Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', $this->_options);
if (is_file(CONF)) {
unlink(CONF);
}
rename(CONF_SAMPLE, $iniSample);
new Configuration;
$this->assertFileNotExists($iniSample, 'old sample file gets removed');
$this->assertFileExists(CONF_SAMPLE, 'new sample file gets created');
$this->assertFileExists(CONF, 'old configuration file gets converted');
$this->assertFileNotExists(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', 'old configuration file gets removed');
}
public function testConfigPath()
{
// setup
@@ -164,4 +204,29 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
}
putenv('CONFIG_PATH');
}
public function testConfigPathIni()
{
// setup
$configFile = $this->_path . DIRECTORY_SEPARATOR . 'conf.ini';
$configMigrated = $this->_path . DIRECTORY_SEPARATOR . 'conf.php';
$options = $this->_options;
$options['main']['name'] = 'OtherBin';
Helper::createIniFile($configFile, $options);
$this->assertFileNotExists(CONF, 'configuration in the default location is non existing');
// test
putenv('CONFIG_PATH=' . $this->_path);
$conf = new Configuration;
$this->assertEquals('OtherBin', $conf->getKey('name'), 'changing config path is supported for ini files as well');
$this->assertFileExists($configMigrated, 'old configuration file gets converted');
$this->assertFileNotExists($configFile, 'old configuration file gets removed');
$this->assertFileNotExists(CONF, 'configuration is not created in the default location');
// cleanup environment
if (is_file($configFile)) {
unlink($configFile);
}
putenv('CONFIG_PATH');
}
}

View File

@@ -428,6 +428,8 @@ class ConfigurationCombinationsTest extends PHPUnit_Framework_TestCase
Helper::confBackup();
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
$this->_model = Filesystem::getInstance(array('dir' => $this->_path));
ServerSalt::setPath($this->_path);
TrafficLimiter::setPath($this->_path);
$this->reset();
}
@@ -447,6 +449,8 @@ class ConfigurationCombinationsTest extends PHPUnit_Framework_TestCase
if ($this->_model->exists(Helper::getPasteId()))
$this->_model->delete(Helper::getPasteId());
$configuration['model_options']['dir'] = $this->_path;
$configuration['traffic']['dir'] = $this->_path;
$configuration['purge']['dir'] = $this->_path;
Helper::createIniFile(CONF, $configuration);
}

View File

@@ -17,8 +17,6 @@ class ControllerTest extends PHPUnit_Framework_TestCase
/* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
$this->_data = Filesystem::getInstance(array('dir' => $this->_path));
ServerSalt::setStore($this->_data);
TrafficLimiter::setStore($this->_data);
$this->reset();
}
@@ -39,8 +37,11 @@ class ControllerTest extends PHPUnit_Framework_TestCase
$this->_data->delete(Helper::getPasteId());
}
$options = parse_ini_file(CONF_SAMPLE, true);
$options['purge']['dir'] = $this->_path;
$options['traffic']['dir'] = $this->_path;
$options['model_options']['dir'] = $this->_path;
Helper::createIniFile(CONF, $options);
ServerSalt::setPath($this->_path);
}
/**
@@ -48,8 +49,6 @@ class ControllerTest extends PHPUnit_Framework_TestCase
*/
public function testView()
{
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
$_GET[Helper::getPasteId()] = '';
ob_start();
new Controller;
$content = ob_get_contents();
@@ -128,6 +127,28 @@ class ControllerTest extends PHPUnit_Framework_TestCase
);
}
/**
* @runInSeparateProcess
*/
public function testHtaccess()
{
$htaccess = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
@unlink($htaccess);
$paste = Helper::getPasteJson();
$file = tempnam(sys_get_temp_dir(), 'FOO');
file_put_contents($file, $paste);
Request::setInputStream($file);
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['REMOTE_ADDR'] = '::1';
ob_start();
new Controller;
ob_end_clean();
$this->assertFileExists($htaccess, 'htaccess recreated');
}
/**
* @expectedException Exception
* @expectedExceptionCode 2
@@ -472,29 +493,6 @@ class ControllerTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
}
/**
* @runInSeparateProcess
*/
public function testCreateInvalidFormat()
{
$options = parse_ini_file(CONF, true);
$options['traffic']['limit'] = 0;
Helper::createIniFile(CONF, $options);
$file = tempnam(sys_get_temp_dir(), 'FOO');
file_put_contents($file, Helper::getPasteJson(1));
Request::setInputStream($file);
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['REMOTE_ADDR'] = '::1';
ob_start();
new Controller;
$content = ob_get_contents();
ob_end_clean();
$response = json_decode($content, true);
$this->assertEquals(1, $response['status'], 'outputs error status');
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
}
/**
* @runInSeparateProcess
*/
@@ -543,7 +541,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
ob_end_clean();
$response = json_decode($content, true);
$this->assertEquals(1, $response['status'], 'outputs error status');
$this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after posting data');
$this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
}
/**

View File

@@ -1,8 +1,6 @@
<?php
use PrivateBin\Data\Database;
use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Persistence\TrafficLimiter;
require_once 'ControllerTest.php';
@@ -26,8 +24,6 @@ class ControllerWithDbTest extends ControllerTest
}
$this->_options['dsn'] = 'sqlite:' . $this->_path . DIRECTORY_SEPARATOR . 'tst.sq3';
$this->_data = Database::getInstance($this->_options);
ServerSalt::setStore($this->_data);
TrafficLimiter::setStore($this->_data);
$this->reset();
}

View File

@@ -1,59 +0,0 @@
<?php
use Google\Auth\HttpHandler\HttpHandlerFactory;
use GuzzleHttp\Client;
use PrivateBin\Data\GoogleCloudStorage;
use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Persistence\TrafficLimiter;
require_once 'ControllerTest.php';
class ControllerWithGcsTest extends ControllerTest
{
private static $_client;
private static $_bucket;
private $_options = array();
public static function setUpBeforeClass()
{
$httpClient = new Client(array('debug'=>false));
$handler = HttpHandlerFactory::build($httpClient);
$name = 'pb-';
$alphabet = 'abcdefghijklmnopqrstuvwxyz';
for ($i = 0; $i < 29; ++$i) {
$name .= $alphabet[rand(0, strlen($alphabet) - 1)];
}
self::$_client = new StorageClientStub(array());
self::$_bucket = self::$_client->createBucket($name);
}
public function setUp()
{
/* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
if (!is_dir($this->_path)) {
mkdir($this->_path);
}
$this->_options = array(
'bucket' => self::$_bucket->name(),
'prefix' => 'pastes',
);
$this->_data = GoogleCloudStorage::getInstance($this->_options);
ServerSalt::setStore($this->_data);
TrafficLimiter::setStore($this->_data);
$this->reset();
}
public function reset()
{
parent::reset();
// but then inject a db config
$options = parse_ini_file(CONF, true);
$options['model'] = array(
'class' => 'GoogleCloudStorage',
);
$options['model_options'] = $this->_options;
Helper::createIniFile(CONF, $options);
}
}

View File

@@ -2,8 +2,6 @@
use PrivateBin\Controller;
use PrivateBin\Data\Database;
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt;
class DatabaseTest extends PHPUnit_Framework_TestCase
{
@@ -33,19 +31,6 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
}
}
public function testSaltMigration()
{
ServerSalt::setStore(Filesystem::getInstance(array('dir' => 'data')));
$salt = ServerSalt::get();
$file = 'data' . DIRECTORY_SEPARATOR . 'salt.php';
$this->assertFileExists($file, 'ServerSalt got initialized and stored on disk');
$this->assertNotEquals($salt, '');
ServerSalt::setStore($this->_model);
ServerSalt::get();
$this->assertFileNotExists($file, 'legacy ServerSalt got removed');
$this->assertEquals($salt, ServerSalt::get(), 'ServerSalt got preserved & migrated');
}
public function testDatabaseBasedDataStoreWorks()
{
$this->_model->delete(Helper::getPasteId());
@@ -302,48 +287,6 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
Helper::rmDir($this->_path);
}
public function testCorruptMeta()
{
mkdir($this->_path);
$path = $this->_path . DIRECTORY_SEPARATOR . 'meta-test.sq3';
if (is_file($path)) {
unlink($path);
}
$this->_options['dsn'] = 'sqlite:' . $path;
$this->_options['tbl'] = 'baz_';
$model = Database::getInstance($this->_options);
$paste = Helper::getPaste(1, array('expire_date' => 1344803344));
unset($paste['meta']['formatter'], $paste['meta']['opendiscussion'], $paste['meta']['salt']);
$model->delete(Helper::getPasteId());
$db = new PDO(
$this->_options['dsn'],
$this->_options['usr'],
$this->_options['pwd'],
$this->_options['opt']
);
$statement = $db->prepare('INSERT INTO baz_paste VALUES(?,?,?,?,?,?,?,?,?)');
$statement->execute(
array(
Helper::getPasteId(),
$paste['data'],
$paste['meta']['postdate'],
$paste['meta']['expire_date'],
0,
0,
'{',
null,
null,
)
);
$statement->closeCursor();
$this->assertTrue($model->exists(Helper::getPasteId()), 'paste exists after storing it');
$this->assertEquals($paste, $model->read(Helper::getPasteId()));
Helper::rmDir($this->_path);
}
public function testTableUpgrade()
{
mkdir($this->_path);

View File

@@ -117,7 +117,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store broken paste');
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
$this->assertFalse($this->_model->setValue('foo', 'non existing namespace'), 'rejects setting value in non existing namespace');
}
public function testCommentErrorDetection()

View File

@@ -1,6 +1,12 @@
<?php
use Google\Auth\HttpHandler\HttpHandlerFactory;
use Google\Cloud\Core\Exception\BadRequestException;
use Google\Cloud\Core\Exception\NotFoundException;
use Google\Cloud\Storage\Bucket;
use Google\Cloud\Storage\Connection\ConnectionInterface;
use Google\Cloud\Storage\StorageClient;
use Google\Cloud\Storage\StorageObject;
use GuzzleHttp\Client;
use PrivateBin\Data\GoogleCloudStorage;
@@ -25,11 +31,13 @@ class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase
public function setUp()
{
ini_set('error_log', stream_get_meta_data(tmpfile())['uri']);
// do not report E_NOTICE as fsouza/fake-gcs-server does not return a `generation` value in the response
// which the Google Cloud Storage PHP library expects.
error_reporting(E_ERROR | E_WARNING | E_PARSE);
$this->_model = GoogleCloudStorage::getInstance(array(
'bucket' => self::$_bucket->name(),
'prefix' => 'pastes',
));
'client' => self::$_client, ));
}
public function tearDown()
@@ -37,6 +45,7 @@ class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase
foreach (self::$_bucket->objects() as $object) {
$object->delete();
}
error_reporting(E_ALL);
}
public static function tearDownAfterClass()
@@ -128,55 +137,579 @@ class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment');
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist');
}
}
/**
* @throws Exception
/**
* Class StorageClientStub provides a limited stub for performing the unit test
*/
public function testKeyValueStore()
class StorageClientStub extends StorageClient
{
private $_config = null;
private $_connection = null;
private $_buckets = array();
public function __construct(array $config = array())
{
$salt = bin2hex(random_bytes(256));
$this->_model->setValue($salt, 'salt', '');
$storedSalt = $this->_model->getValue('salt', '');
$this->assertEquals($salt, $storedSalt);
$this->_model->purgeValues('salt', time() + 60);
$this->assertEquals('', $this->_model->getValue('salt', 'master'));
$this->_config = $config;
$this->_connection = new ConnectionInterfaceStub();
}
$client = hash_hmac('sha512', '127.0.0.1', $salt);
$expire = time();
$this->_model->setValue(strval($expire), 'traffic_limiter', $client);
$storedExpired = $this->_model->getValue('traffic_limiter', $client);
$this->assertEquals(strval($expire), $storedExpired);
$this->_model->purgeValues('traffic_limiter', time() - 60);
$this->assertEquals($storedExpired, $this->_model->getValue('traffic_limiter', $client));
$this->_model->purgeValues('traffic_limiter', time() + 60);
$this->assertEquals('', $this->_model->getValue('traffic_limiter', $client));
$purgeAt = $expire + (15 * 60);
$this->_model->setValue(strval($purgeAt), 'purge_limiter', '');
$storedPurgedAt = $this->_model->getValue('purge_limiter', '');
$this->assertEquals(strval($purgeAt), $storedPurgedAt);
$this->_model->purgeValues('purge_limiter', $purgeAt + 60);
$this->assertEquals('', $this->_model->getValue('purge_limiter', ''));
$this->assertEquals('', $this->_model->getValue('purge_limiter', 'at'));
public function bucket($name, $userProject = false)
{
if (!key_exists($name, $this->_buckets)) {
$b = new BucketStub($this->_connection, $name, array(), $this);
$this->_buckets[$name] = $b;
}
return $this->_buckets[$name];
}
/**
* @throws Exception
* @throws \Google\Cloud\Core\Exception\NotFoundException
*/
public function testKeyValuePurgeTrafficLimiter()
public function deleteBucket($name)
{
$salt = bin2hex(random_bytes(256));
$client = hash_hmac('sha512', '127.0.0.1', $salt);
$expire = time();
$this->_model->setValue(strval($expire), 'traffic_limiter', $client);
$storedExpired = $this->_model->getValue('traffic_limiter', $client);
$this->assertEquals(strval($expire), $storedExpired);
if (key_exists($name, $this->_buckets)) {
unset($this->_buckets[$name]);
} else {
throw new NotFoundException();
}
}
$this->_model->purgeValues('traffic_limiter', time() - 60);
$this->assertEquals($storedExpired, $this->_model->getValue('traffic_limiter', $client));
public function buckets(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
$this->_model->purgeValues('traffic_limiter', time() + 60);
$this->assertEquals('', $this->_model->getValue('traffic_limiter', $client));
public function registerStreamWrapper($protocol = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function unregisterStreamWrapper($protocol = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUrlUploader($uri, $data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function timestamp(\DateTimeInterface $timestamp, $nanoSeconds = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function getServiceAccount(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function hmacKeys(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function hmacKey($accessId, $projectId = null, array $metadata = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function createHmacKey($serviceAccountEmail, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function createBucket($name, array $options = array())
{
if (key_exists($name, $this->_buckets)) {
throw new BadRequestException('already exists');
}
$b = new BucketStub($this->_connection, $name, array(), $this);
$this->_buckets[$name] = $b;
return $b;
}
}
/**
* Class BucketStub stubs a GCS bucket.
*/
class BucketStub extends Bucket
{
public $_objects;
private $_name;
private $_info;
private $_connection;
private $_client;
public function __construct(ConnectionInterface $connection, $name, array $info = array(), $client = null)
{
$this->_name = $name;
$this->_info = $info;
$this->_connection = $connection;
$this->_objects = array();
$this->_client = $client;
}
public function acl()
{
throw new BadMethodCallException('not supported by this stub');
}
public function defaultAcl()
{
throw new BadMethodCallException('not supported by this stub');
}
public function exists()
{
return true;
}
public function upload($data, array $options = array())
{
if (!is_string($data) || !key_exists('name', $options)) {
throw new BadMethodCallException('not supported by this stub');
}
$name = $options['name'];
$generation = '1';
$o = new StorageObjectStub($this->_connection, $name, $this, $generation, $options);
$this->_objects[$options['name']] = $o;
$o->setData($data);
}
public function uploadAsync($data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getResumableUploader($data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getStreamableUploader($data, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function object($name, array $options = array())
{
if (key_exists($name, $this->_objects)) {
return $this->_objects[$name];
} else {
return new StorageObjectStub($this->_connection, $name, $this, null, $options);
}
}
public function objects(array $options = array())
{
$prefix = key_exists('prefix', $options) ? $options['prefix'] : '';
return new CallbackFilterIterator(
new ArrayIterator($this->_objects),
function ($current, $key, $iterator) use ($prefix) {
return substr($key, 0, strlen($prefix)) == $prefix;
}
);
}
public function createNotification($topic, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function notification($id)
{
throw new BadMethodCallException('not supported by this stub');
}
public function notifications(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function delete(array $options = array())
{
$this->_client->deleteBucket($this->_name);
}
public function update(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function compose(array $sourceObjects, $name, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function info(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function reload(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function name()
{
return $this->_name;
}
public static function lifecycle(array $lifecycle = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function currentLifecycle(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function isWritable($file = null)
{
throw new BadMethodCallException('not supported by this stub');
}
public function iam()
{
throw new BadMethodCallException('not supported by this stub');
}
public function lockRetentionPolicy(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUrl($expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function generateSignedPostPolicyV4($objectName, $expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
}
/**
* Class StorageObjectStub stubs a GCS storage object.
*/
class StorageObjectStub extends StorageObject
{
private $_name;
private $_data;
private $_info;
private $_bucket;
private $_generation;
private $_exists = false;
private $_connection;
public function __construct(ConnectionInterface $connection, $name, $bucket, $generation = null, array $info = array(), $encryptionKey = null, $encryptionKeySHA256 = null)
{
$this->_name = $name;
$this->_bucket = $bucket;
$this->_generation = $generation;
$this->_info = $info;
$this->_connection = $connection;
}
public function acl()
{
throw new BadMethodCallException('not supported by this stub');
}
public function exists(array $options = array())
{
return key_exists($this->_name, $this->_bucket->_objects);
}
/**
* @throws NotFoundException
*/
public function delete(array $options = array())
{
if (key_exists($this->_name, $this->_bucket->_objects)) {
unset($this->_bucket->_objects[$this->_name]);
} else {
throw new NotFoundException('key ' . $this->_name . ' not found.');
}
}
/**
* @throws NotFoundException
*/
public function update(array $metadata, array $options = array())
{
if (!$this->_exists) {
throw new NotFoundException('key ' . $this->_name . ' not found.');
}
$this->_info = $metadata;
}
public function copy($destination, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function rewrite($destination, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function rename($name, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
/**
* @throws NotFoundException
*/
public function downloadAsString(array $options = array())
{
if (!$this->_exists) {
throw new NotFoundException('key ' . $this->_name . ' not found.');
}
return $this->_data;
}
public function downloadToFile($path, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function downloadAsStream(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function downloadAsStreamAsync(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUrl($expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function signedUploadUrl($expires, array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function beginSignedUploadSession(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function info(array $options = array())
{
return key_exists('metadata',$this->_info) ? $this->_info['metadata'] : array();
}
public function reload(array $options = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function name()
{
return $this->_name;
}
public function identity()
{
throw new BadMethodCallException('not supported by this stub');
}
public function gcsUri()
{
return sprintf(
'gs://%s/%s',
$this->_bucket->name(),
$this->_name
);
}
public function setData($data)
{
$this->_data = $data;
$this->_exists = true;
}
}
/**
* Class ConnectionInterfaceStub required for the stubs.
*/
class ConnectionInterfaceStub implements ConnectionInterface
{
public function deleteAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function patchAcl(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listBuckets(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getBucketIamPolicy(array $args)
{
throw new BadMethodCallException('not supported by this stub');
}
public function setBucketIamPolicy(array $args)
{
throw new BadMethodCallException('not supported by this stub');
}
public function testBucketIamPermissions(array $args)
{
throw new BadMethodCallException('not supported by this stub');
}
public function patchBucket(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function copyObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function rewriteObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function composeObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listObjects(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function patchObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function downloadObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getNotification(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteNotification(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function insertNotification(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listNotifications(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getServiceAccount(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function lockRetentionPolicy(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function createHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function deleteHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function getHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function updateHmacKey(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function listHmacKeys(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
}

View File

@@ -16,7 +16,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
/* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
$this->_model = Filesystem::getInstance(array('dir' => $this->_path));
ServerSalt::setStore($this->_model);
ServerSalt::setPath($this->_path);
$_POST = array();
$_GET = array();
@@ -25,6 +25,8 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
$this->_model->delete(Helper::getPasteId());
}
$options = parse_ini_file(CONF_SAMPLE, true);
$options['purge']['dir'] = $this->_path;
$options['traffic']['dir'] = $this->_path;
$options['model_options']['dir'] = $this->_path;
Helper::confBackup();
Helper::createIniFile(CONF, $options);

View File

@@ -25,6 +25,7 @@ class ModelTest extends PHPUnit_Framework_TestCase
if (!is_dir($this->_path)) {
mkdir($this->_path);
}
ServerSalt::setPath($this->_path);
$options = parse_ini_file(CONF_SAMPLE, true);
$options['purge']['limit'] = 0;
$options['model'] = array(
@@ -38,7 +39,6 @@ class ModelTest extends PHPUnit_Framework_TestCase
);
Helper::confBackup();
Helper::createIniFile(CONF, $options);
ServerSalt::setStore(Database::getInstance($options['model_options']));
$this->_conf = new Configuration;
$this->_model = new Model($this->_conf);
$_SERVER['REMOTE_ADDR'] = '::1';
@@ -102,58 +102,6 @@ class ModelTest extends PHPUnit_Framework_TestCase
$this->assertEquals(array(), $paste->getComments(), 'comment was deleted with paste');
}
public function testPasteV1()
{
$pasteData = Helper::getPaste(1);
unset($pasteData['meta']['formatter']);
$path = $this->_path . DIRECTORY_SEPARATOR . 'v1-test.sq3';
if (is_file($path)) {
unlink($path);
}
$options = parse_ini_file(CONF_SAMPLE, true);
$options['purge']['limit'] = 0;
$options['model'] = array(
'class' => 'Database',
);
$options['model_options'] = array(
'dsn' => 'sqlite:' . $path,
'usr' => null,
'pwd' => null,
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
);
Helper::createIniFile(CONF, $options);
$model = new Model(new Configuration);
$model->getPaste('0000000000000000')->exists(); // triggers database table creation
$model->getPaste(Helper::getPasteId())->delete(); // deletes the cache
$db = new PDO(
$options['model_options']['dsn'],
$options['model_options']['usr'],
$options['model_options']['pwd'],
$options['model_options']['opt']
);
$statement = $db->prepare('INSERT INTO paste VALUES(?,?,?,?,?,?,?,?,?)');
$statement->execute(
array(
Helper::getPasteId(),
$pasteData['data'],
$pasteData['meta']['postdate'],
0,
0,
0,
json_encode($pasteData['meta']),
null,
null,
)
);
$statement->closeCursor();
$paste = $model->getPaste(Helper::getPasteId());
$this->assertNotEmpty($paste->getDeleteToken(), 'excercise the condition to load the data from storage');
$this->assertEquals('plaintext', $paste->get()['meta']['formatter'], 'paste got created with default formatter');
}
public function testCommentDefaults()
{
$comment = new Comment(
@@ -185,97 +133,6 @@ class ModelTest extends PHPUnit_Framework_TestCase
$paste->store();
}
/**
* @expectedException Exception
* @expectedExceptionCode 76
*/
public function testStoreFail()
{
$path = $this->_path . DIRECTORY_SEPARATOR . 'model-store-test.sq3';
if (is_file($path)) {
unlink($path);
}
$options = parse_ini_file(CONF_SAMPLE, true);
$options['purge']['limit'] = 0;
$options['model'] = array(
'class' => 'Database',
);
$options['model_options'] = array(
'dsn' => 'sqlite:' . $path,
'usr' => null,
'pwd' => null,
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
);
Helper::createIniFile(CONF, $options);
$model = new Model(new Configuration);
$pasteData = Helper::getPastePost();
$model->getPaste(Helper::getPasteId())->delete();
$model->getPaste(Helper::getPasteId())->exists();
$db = new PDO(
$options['model_options']['dsn'],
$options['model_options']['usr'],
$options['model_options']['pwd'],
$options['model_options']['opt']
);
$statement = $db->prepare('DROP TABLE paste');
$statement->execute();
$statement->closeCursor();
$paste = $model->getPaste();
$paste->setData($pasteData);
$paste->store();
}
/**
* @expectedException Exception
* @expectedExceptionCode 70
*/
public function testCommentStoreFail()
{
$path = $this->_path . DIRECTORY_SEPARATOR . 'model-test.sq3';
if (is_file($path)) {
unlink($path);
}
$options = parse_ini_file(CONF_SAMPLE, true);
$options['purge']['limit'] = 0;
$options['model'] = array(
'class' => 'Database',
);
$options['model_options'] = array(
'dsn' => 'sqlite:' . $path,
'usr' => null,
'pwd' => null,
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
);
Helper::createIniFile(CONF, $options);
$model = new Model(new Configuration);
$pasteData = Helper::getPastePost();
$commentData = Helper::getCommentPost();
$model->getPaste(Helper::getPasteId())->delete();
$paste = $model->getPaste();
$paste->setData($pasteData);
$paste->store();
$paste->exists();
$db = new PDO(
$options['model_options']['dsn'],
$options['model_options']['usr'],
$options['model_options']['pwd'],
$options['model_options']['opt']
);
$statement = $db->prepare('DROP TABLE comment');
$statement->execute();
$statement->closeCursor();
$comment = $paste->getComment(Helper::getPasteId());
$comment->setData($commentData);
$comment->store();
}
/**
* @expectedException Exception
* @expectedExceptionCode 69
@@ -338,18 +195,6 @@ class ModelTest extends PHPUnit_Framework_TestCase
$paste->get();
}
/**
* @expectedException Exception
* @expectedExceptionCode 75
*/
public function testInvalidPasteFormat()
{
$pasteData = Helper::getPastePost();
$pasteData['adata'][1] = 'format does not exist';
$paste = $this->_model->getPaste();
$paste->setData($pasteData);
}
/**
* @expectedException Exception
* @expectedExceptionCode 60

View File

@@ -1,6 +1,5 @@
<?php
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\PurgeLimiter;
class PurgeLimiterTest extends PHPUnit_Framework_TestCase
@@ -14,9 +13,7 @@ class PurgeLimiterTest extends PHPUnit_Framework_TestCase
if (!is_dir($this->_path)) {
mkdir($this->_path);
}
PurgeLimiter::setStore(
Filesystem::getInstance(array('dir' => $this->_path))
);
PurgeLimiter::setPath($this->_path);
}
public function tearDown()

View File

@@ -1,6 +1,5 @@
<?php
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt;
class ServerSaltTest extends PHPUnit_Framework_TestCase
@@ -20,9 +19,7 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase
if (!is_dir($this->_path)) {
mkdir($this->_path);
}
ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_path))
);
ServerSalt::setPath($this->_path);
$this->_otherPath = $this->_path . DIRECTORY_SEPARATOR . 'foo';
@@ -43,46 +40,46 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase
public function testGeneration()
{
// generating new salt
ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_path))
);
ServerSalt::setPath($this->_path);
$salt = ServerSalt::get();
// try setting a different path and resetting it
ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_otherPath))
);
ServerSalt::setPath($this->_otherPath);
$this->assertNotEquals($salt, ServerSalt::get());
ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_path))
);
ServerSalt::setPath($this->_path);
$this->assertEquals($salt, ServerSalt::get());
}
/**
* @expectedException Exception
* @expectedExceptionCode 11
*/
public function testPathShenanigans()
{
// try setting an invalid path
chmod($this->_invalidPath, 0000);
$store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
ServerSalt::setPath($this->_invalidPath);
ServerSalt::get();
}
/**
* @expectedException Exception
* @expectedExceptionCode 20
*/
public function testFileRead()
{
// try setting an invalid file
chmod($this->_invalidPath, 0700);
file_put_contents($this->_invalidFile, '');
chmod($this->_invalidFile, 0000);
$store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
ServerSalt::setPath($this->_invalidPath);
ServerSalt::get();
}
/**
* @expectedException Exception
* @expectedExceptionCode 13
*/
public function testFileWrite()
{
// try setting an invalid file
@@ -93,24 +90,19 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase
}
file_put_contents($this->_invalidPath . DIRECTORY_SEPARATOR . '.htaccess', '');
chmod($this->_invalidPath, 0500);
$store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
ServerSalt::setPath($this->_invalidPath);
ServerSalt::get();
}
/**
* @expectedException Exception
* @expectedExceptionCode 10
*/
public function testPermissionShenanigans()
{
// try creating an invalid path
chmod($this->_invalidPath, 0000);
ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz'))
);
$store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
ServerSalt::setPath($this->_invalidPath . DIRECTORY_SEPARATOR . 'baz');
ServerSalt::get();
}
}

View File

@@ -1,7 +1,5 @@
<?php
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Persistence\TrafficLimiter;
class TrafficLimiterTest extends PHPUnit_Framework_TestCase
@@ -12,9 +10,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
{
/* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'trafficlimit';
$store = Filesystem::getInstance(array('dir' => $this->_path));
ServerSalt::setStore($store);
TrafficLimiter::setStore($store);
TrafficLimiter::setPath($this->_path);
}
public function tearDown()
@@ -23,17 +19,11 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
Helper::rmDir($this->_path . DIRECTORY_SEPARATOR);
}
public function testHtaccess()
{
$htaccess = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
@unlink($htaccess);
$_SERVER['REMOTE_ADDR'] = 'foobar';
TrafficLimiter::canPass();
$this->assertFileExists($htaccess, 'htaccess recreated');
}
public function testTrafficGetsLimited()
{
$this->assertEquals($this->_path, TrafficLimiter::getPath());
$file = 'baz';
$this->assertEquals($this->_path . DIRECTORY_SEPARATOR . $file, TrafficLimiter::getPath($file));
TrafficLimiter::setLimit(4);
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->assertTrue(TrafficLimiter::canPass(), 'first request may pass');

View File

@@ -1,6 +1,5 @@
<?php
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Vizhash16x16;
@@ -18,7 +17,7 @@ class Vizhash16x16Test extends PHPUnit_Framework_TestCase
mkdir($this->_path);
}
$this->_file = $this->_path . DIRECTORY_SEPARATOR . 'vizhash.png';
ServerSalt::setStore(Filesystem::getInstance(array('dir' => $this->_path)));
ServerSalt::setPath($this->_path);
}
public function tearDown()