implement chrono privacy for pastes, addresses #1290

This commit is contained in:
El RIDO 2024-05-01 20:16:03 +02:00
parent ec02afca04
commit a9f1926b96
No known key found for this signature in database
GPG key ID: 0F5C940A6BD81F92
9 changed files with 24 additions and 29 deletions

View file

@ -5,6 +5,7 @@
* ADDED: Input sanitation to some not yet filtered query and server parameters * ADDED: Input sanitation to some not yet filtered query and server parameters
* CHANGED: "Send" button now labeled "Create" (#946) * CHANGED: "Send" button now labeled "Create" (#946)
* CHANGED: drop some PHP < 5.6 fallbacks, minimum version is PHP 7.3 as of release 1.6.0 * CHANGED: drop some PHP < 5.6 fallbacks, minimum version is PHP 7.3 as of release 1.6.0
* CHANGED: `create` attribute is no longer returned in API for pastes (#1290)
* FIXED: Add cache control headers also to API calls (#1263) * FIXED: Add cache control headers also to API calls (#1263)
* FIXED: Shortened paste URL does not appear in email (#606) * FIXED: Shortened paste URL does not appear in email (#606)

View file

@ -169,14 +169,13 @@ user these additional privileges:
For reference or if you want to create the table schema for yourself to avoid For reference or if you want to create the table schema for yourself to avoid
having to give PrivateBin too many permissions (replace `prefix_` with your own having to give PrivateBin too many permissions (replace `prefix_` with your own
table prefix and create the table schema with your favourite MariaDB/MySQL table prefix and create the table schema with your favorite MariaDB/MySQL
client): client):
```sql ```sql
CREATE TABLE prefix_paste ( CREATE TABLE prefix_paste (
dataid CHAR(16) NOT NULL, dataid CHAR(16) NOT NULL,
data MEDIUMBLOB, data MEDIUMBLOB,
postdate INT,
expiredate INT, expiredate INT,
opendiscussion INT, opendiscussion INT,
burnafterreading INT, burnafterreading INT,

View file

@ -146,9 +146,6 @@ class Database extends AbstractData
$attachment = $attachmentname = null; $attachment = $attachmentname = null;
$meta = $paste['meta']; $meta = $paste['meta'];
$isVersion1 = array_key_exists('data', $paste); $isVersion1 = array_key_exists('data', $paste);
list($createdKey) = $this->_getVersionedKeys($isVersion1 ? 1 : 2);
$created = (int) $meta[$createdKey];
unset($meta[$createdKey], $paste['meta']);
if (array_key_exists('expire_date', $meta)) { if (array_key_exists('expire_date', $meta)) {
$expire_date = (int) $meta['expire_date']; $expire_date = (int) $meta['expire_date'];
unset($meta['expire_date']); unset($meta['expire_date']);
@ -177,11 +174,10 @@ class Database extends AbstractData
try { try {
return $this->_exec( return $this->_exec(
'INSERT INTO "' . $this->_sanitizeIdentifier('paste') . 'INSERT INTO "' . $this->_sanitizeIdentifier('paste') .
'" VALUES(?,?,?,?,?,?,?,?,?)', '" VALUES(?,?,?,?,?,?,?,?)',
array( array(
$pasteid, $pasteid,
$isVersion1 ? $paste['data'] : Json::encode($paste), $isVersion1 ? $paste['data'] : Json::encode($paste),
$created,
$expire_date, $expire_date,
(int) $opendiscussion, (int) $opendiscussion,
(int) $burnafterreading, (int) $burnafterreading,
@ -218,13 +214,7 @@ class Database extends AbstractData
// create array // create array
$data = Json::decode($row['data']); $data = Json::decode($row['data']);
$isVersion2 = array_key_exists('v', $data) && $data['v'] >= 2; $isVersion2 = array_key_exists('v', $data) && $data['v'] >= 2;
if ($isVersion2) { $paste = $isVersion2 ? $data : array('data' => $row['data']);
$paste = $data;
list($createdKey) = $this->_getVersionedKeys(2);
} else {
$paste = array('data' => $row['data']);
list($createdKey) = $this->_getVersionedKeys(1);
}
try { try {
$row['meta'] = Json::decode($row['meta']); $row['meta'] = Json::decode($row['meta']);
@ -233,7 +223,6 @@ class Database extends AbstractData
} }
$row = self::upgradePreV1Format($row); $row = self::upgradePreV1Format($row);
$paste['meta'] = $row['meta']; $paste['meta'] = $row['meta'];
$paste['meta'][$createdKey] = (int) $row['postdate'];
$expire_date = (int) $row['expiredate']; $expire_date = (int) $row['expiredate'];
if ($expire_date > 0) { if ($expire_date > 0) {
$paste['meta']['expire_date'] = $expire_date; $paste['meta']['expire_date'] = $expire_date;
@ -751,7 +740,6 @@ class Database extends AbstractData
'CREATE TABLE "' . $this->_sanitizeIdentifier('paste') . '" ( ' . 'CREATE TABLE "' . $this->_sanitizeIdentifier('paste') . '" ( ' .
"\"dataid\" CHAR(16) NOT NULL$main_key, " . "\"dataid\" CHAR(16) NOT NULL$main_key, " .
"\"data\" $attachmentType, " . "\"data\" $attachmentType, " .
'"postdate" INT, ' .
'"expiredate" INT, ' . '"expiredate" INT, ' .
'"opendiscussion" INT, ' . '"opendiscussion" INT, ' .
'"burnafterreading" INT, ' . '"burnafterreading" INT, ' .
@ -926,6 +914,12 @@ class Database extends AbstractData
); );
} }
// no break, continue with updates for all newer versions // no break, continue with updates for all newer versions
case '1.7.2':
$this->_db->exec(
'ALTER TABLE "' . $this->_sanitizeIdentifier('paste') .
"\" DROP COLUMN \"postdate\""
);
// no break, continue with updates for all newer versions
default: default:
$this->_exec( $this->_exec(
'UPDATE "' . $this->_sanitizeIdentifier('config') . 'UPDATE "' . $this->_sanitizeIdentifier('config') .

View file

@ -37,7 +37,7 @@ class Paste extends AbstractModel
throw new Exception(Controller::GENERIC_ERROR, 64); throw new Exception(Controller::GENERIC_ERROR, 64);
} }
// check if paste has expired and delete it if neccessary. // check if paste has expired and delete it if necessary.
if (array_key_exists('expire_date', $data['meta'])) { if (array_key_exists('expire_date', $data['meta'])) {
if ($data['meta']['expire_date'] < time()) { if ($data['meta']['expire_date'] < time()) {
$this->delete(); $this->delete();
@ -92,7 +92,6 @@ class Paste extends AbstractModel
throw new Exception('You are unlucky. Try again.', 75); throw new Exception('You are unlucky. Try again.', 75);
} }
$this->_data['meta']['created'] = time();
$this->_data['meta']['salt'] = ServerSalt::generate(); $this->_data['meta']['salt'] = ServerSalt::generate();
// store paste // store paste

View file

@ -508,6 +508,11 @@ class ConnectionInterfaceStub implements ConnectionInterface
throw new BadMethodCallException('not supported by this stub'); throw new BadMethodCallException('not supported by this stub');
} }
public function restoreObject(array $args = array())
{
throw new BadMethodCallException('not supported by this stub');
}
public function copyObject(array $args = array()) public function copyObject(array $args = array())
{ {
throw new BadMethodCallException('not supported by this stub'); throw new BadMethodCallException('not supported by this stub');
@ -655,7 +660,6 @@ class Helper
), ),
'meta' => array( 'meta' => array(
'expire' => '5min', 'expire' => '5min',
'created' => 1344803344,
), ),
'v' => 2, 'v' => 2,
'ct' => 'ME5JF/YBEijp2uYMzLZozbKtWc5wfy6R59NBb7SmRig=', 'ct' => 'ME5JF/YBEijp2uYMzLZozbKtWc5wfy6R59NBb7SmRig=',

View file

@ -170,6 +170,7 @@ class ControllerTest extends TestCase
$this->assertEquals(0, $response['status'], 'outputs status'); $this->assertEquals(0, $response['status'], 'outputs status');
$this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data'); $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
$paste = $this->_data->read($response['id']); $paste = $this->_data->read($response['id']);
$this->assertFalse(array_key_exists('created', $paste['meta']), 'does not output created');
$this->assertEquals( $this->assertEquals(
hash_hmac('sha256', $response['id'], $paste['meta']['salt']), hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
$response['deletetoken'], $response['deletetoken'],
@ -712,7 +713,7 @@ class ControllerTest extends TestCase
$this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly'); $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
$this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly'); $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
$this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly'); $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
$this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly'); $this->assertFalse(array_key_exists('created', $paste['meta']), 'does not output created');
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
// by default it will be deleted instantly after it is read // by default it will be deleted instantly after it is read
@ -741,7 +742,7 @@ class ControllerTest extends TestCase
$this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly'); $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
$this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly'); $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
$this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly'); $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
$this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly'); $this->assertFalse(array_key_exists('created', $paste['meta']), 'does not output created');
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
} }

View file

@ -259,12 +259,11 @@ class DatabaseTest extends TestCase
$this->_options['pwd'], $this->_options['pwd'],
$this->_options['opt'] $this->_options['opt']
); );
$statement = $db->prepare('INSERT INTO bar_paste VALUES(?,?,?,?,?,?,?,?,?)'); $statement = $db->prepare('INSERT INTO bar_paste VALUES(?,?,?,?,?,?,?,?)');
$statement->execute( $statement->execute(
array( array(
Helper::getPasteId(), Helper::getPasteId(),
$paste['data'], $paste['data'],
$paste['meta']['postdate'],
$paste['meta']['expire_date'], $paste['meta']['expire_date'],
0, 0,
0, 0,
@ -292,7 +291,7 @@ class DatabaseTest extends TestCase
$this->_options['tbl'] = 'baz_'; $this->_options['tbl'] = 'baz_';
$model = new Database($this->_options); $model = new Database($this->_options);
$paste = Helper::getPaste(1, array('expire_date' => 1344803344)); $paste = Helper::getPaste(1, array('expire_date' => 1344803344));
unset($paste['meta']['formatter'], $paste['meta']['opendiscussion'], $paste['meta']['salt']); unset($paste['meta']['formatter'], $paste['meta']['opendiscussion'], $paste['meta']['postdate'], $paste['meta']['salt']);
$model->delete(Helper::getPasteId()); $model->delete(Helper::getPasteId());
$db = new PDO( $db = new PDO(
@ -301,12 +300,11 @@ class DatabaseTest extends TestCase
$this->_options['pwd'], $this->_options['pwd'],
$this->_options['opt'] $this->_options['opt']
); );
$statement = $db->prepare('INSERT INTO baz_paste VALUES(?,?,?,?,?,?,?,?,?)'); $statement = $db->prepare('INSERT INTO baz_paste VALUES(?,?,?,?,?,?,?,?)');
$statement->execute( $statement->execute(
array( array(
Helper::getPasteId(), Helper::getPasteId(),
$paste['data'], $paste['data'],
$paste['meta']['postdate'],
$paste['meta']['expire_date'], $paste['meta']['expire_date'],
0, 0,
0, 0,

View file

@ -180,7 +180,7 @@ class JsonApiTest extends TestCase
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly'); $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste'); $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
$this->assertEquals($paste['ct'], $response['ct'], 'outputs data correctly'); $this->assertEquals($paste['ct'], $response['ct'], 'outputs data correctly');
$this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs postdate correctly'); $this->assertFalse(array_key_exists('created', $paste['meta']), 'does not output created');
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
} }

View file

@ -134,12 +134,11 @@ class ModelTest extends TestCase
$options['model_options']['pwd'], $options['model_options']['pwd'],
$options['model_options']['opt'] $options['model_options']['opt']
); );
$statement = $db->prepare('INSERT INTO paste VALUES(?,?,?,?,?,?,?,?,?)'); $statement = $db->prepare('INSERT INTO paste VALUES(?,?,?,?,?,?,?,?)');
$statement->execute( $statement->execute(
array( array(
Helper::getPasteId(), Helper::getPasteId(),
$pasteData['data'], $pasteData['data'],
$pasteData['meta']['postdate'],
0, 0,
0, 0,
0, 0,