Compare commits

...

12 Commits
11.2 ... master

Author SHA1 Message Date
Mario
deb4c54ebf version 11.2.1 2026-05-20 07:53:27 +02:00
Mario
281f518705 update changelog
(cherry picked from commit 496dade675)

Co-authored-by: Mario <mario@mariovavti.com>
2026-05-20 05:51:49 +00:00
Mario
a8d9747b12 test variable before using it
(cherry picked from commit 82b8ed2652)

Co-authored-by: Mario <mario@mariovavti.com>
2026-05-20 05:49:33 +00:00
Mario
4b15b07b8b update changelog
(cherry picked from commit 2625fe9527)

Co-authored-by: Mario <mario@mariovavti.com>
2026-05-20 05:43:42 +00:00
Mario
607a5488d6 Improve detecting suspicious ActivityStreams keys
Using string comparison on the whole key does not work, as some keys
will be given prefixes during expansion. We need to check if the payload
has keys that _contain_ the suspicious keywords we're looking for.


(cherry picked from commit 0c7731bb76)

Co-authored-by: Harald Eilertsen <haraldei@anduin.net>
2026-05-18 19:06:06 +00:00
Mario
519b52ccdc check for currently unsafe json-ld constructs
(cherry picked from commit 67d73f74ac)

45460e99 check for currently unsafe constructs
5ebb8546 move the jsonld unsafe keys check up a little so that it will actually terminate if positive.
363e2ab5 fix invalid json
b2362c8c only expand and check jsonld if verifying
d21ac6ef jsonld: refactor and hard fail on normalisation or expansion error
2e64496e Merge branch 'dev' into json-ld
c0918861 revert always hard fail

Co-authored-by: Mario <mario@mariovavti.com>
2026-05-18 19:05:16 +00:00
Mario
99fd2e1cb9 tests: Use minimal channel for LDSignature tests
Move the channel keypair and other fields needed by the
LDSignature::verify function to the test case itself. This makes the
test case independent on any potential future changes to the fixtures.


(cherry picked from commit 8d283e0be5)

Co-authored-by: Harald Eilertsen <haraldei@anduin.net>
2026-05-18 19:03:59 +00:00
Mario
a69c460ee6 tests: Add basic test for LDSignature::verify
(cherry picked from commit 0cd682d85e)

Co-authored-by: Harald Eilertsen <haraldei@anduin.net>
2026-05-18 19:03:11 +00:00
Mario
71ba6406e3 tests: Fix invalid keypair for channel fixture
(cherry picked from commit 9aff1d4024)

Co-authored-by: Harald Eilertsen <haraldei@anduin.net>
2026-05-18 19:02:48 +00:00
Mario
4922acb18a actually there is no need to set App::$profile here because it will be set in profile_load() if applicable
(cherry picked from commit 0376b5d442)

Co-authored-by: Mario <mario@mariovavti.com>
2026-05-18 19:01:04 +00:00
Mario
a0ea19e51c do not set App::$profile for removed channels and minor cleanup
(cherry picked from commit b20ed4f455)

Co-authored-by: Mario <mario@mariovavti.com>
2026-05-18 19:00:15 +00:00
Mario
8c4457db8b messageFilter: make sure we do not choke if we expect a string but an array is given 2026-03-27 21:53:04 +00:00
11 changed files with 197 additions and 57 deletions

View File

@@ -1,3 +1,16 @@
Hubzilla 11.2.1 (2026-05-20)
- Fix channel creation failing in some situations
- Drop payloads with unsafe json-ld keys in LDSignatures::verify() and add tests
- Fix App::$profile set for removed channels
- Fix MessageFilter breaking when expecting string but array is given
- Superblock: fix blocking failed in some setups (sponsored by NLnet NGI0 Commons Fund/Superblock)
- Superblock: fix missing import of attribute (sponsored by NLnet NGI0 Commons Fund/Superblock)
- Superblock: improved detection of reshares (sponsored by NLnet NGI0 Commons Fund/Superblock)
- Cards: fix PHP warning
- Gallery: fix PHP warnings and only implement observer related javascript if there actually is an observer
- Diaspora: fix missing local namespace for thr_parent
Hubzilla 11.2 (2026-03-26)
Features
- Introduce parse_webbie() for preparing webbies and URLs for webfinger usage

View File

@@ -8,9 +8,10 @@ class LDSignatures {
static function verify($data,$pubkey) {
$expand_and_check_unsafe = true;
$ohash = self::hash(self::signable_options($data['signature']));
$dhash = self::hash(self::signable_data($data));
$ohash = self::hash(self::signable_options($data['signature']), $expand_and_check_unsafe);
$dhash = self::hash(self::signable_data($data), $expand_and_check_unsafe);
$x = Crypto::verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey);
logger('LD-verify: ' . intval($x));
@@ -74,11 +75,11 @@ class LDSignatures {
return json_encode($newopts,JSON_UNESCAPED_SLASHES);
}
static function hash($obj) {
return hash('sha256', self::normalise($obj));
static function hash($obj, $expand_and_check_unsafe = false) {
return hash('sha256', self::normalise($obj, $expand_and_check_unsafe));
}
static function normalise($data) {
static function normalise($data, $expand_and_check_unsafe) {
$ret = '';
if(is_string($data)) {
@@ -90,6 +91,15 @@ class LDSignatures {
jsonld_set_document_loader('jsonld_document_loader');
if ($expand_and_check_unsafe) {
$expanded = jsonld_expand($data);
if (self::contains_unsafe_keys($expanded)) {
logger('contains_unsafe_keys: ' . print_r($data,true));
throw new \Exception('json-ld graph modification operation detected');
}
}
try {
$ret = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
}
@@ -132,6 +142,41 @@ class LDSignatures {
}
static function contains_unsafe_keys(array|object $data, int $depth = 0): bool
{
if ($depth > 64) {
return true;
}
$unsafe_keys = ['@graph', '@included', '@reverse'];
if (is_object($data)) {
$data = (array) $data;
}
if (is_array($data)) {
foreach ($data as $key => $value) {
//
// We can't use `in_array` since the keys may contain more than
// just the keyword after expansion, typically "_:@included"
// for an unnamed node with the "@included" key.
//
// So we use `array_filter` with a callback instead:
$matches = array_filter($unsafe_keys, fn ($k) => strpos($key, $k) !== false);
if (!empty($matches)) {
return true;
}
if (is_array($value) || is_object($value)) {
if (self::contains_unsafe_keys($value, $depth + 1)) {
return true;
}
}
}
}
return false;
}
}

View File

@@ -316,7 +316,7 @@ class MessageFilter
if (preg_match('/(.*?)\s~=\s(.*?)$/', $s, $matches)) {
$x = ((array_key_exists(trim($matches[1]),$item)) ? $item[trim($matches[1])] : EMPTY_STR);
if (stripos($x, trim($matches[2])) !== false) {
if (is_string($x) && stripos($x, trim($matches[2])) !== false) {
return true;
}
return false;

View File

@@ -33,7 +33,7 @@ class New_channel extends \Zotlabs\Web\Controller {
// first name
if(strpos($x,' '))
$test[] = legal_webbie(substr($x,0,strpos($x,' ')));
if($test[0]) {
if (!empty($test[0])) {
// first name plus first initial of last
$test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : '');
// first name plus random number
@@ -69,7 +69,7 @@ class New_channel extends \Zotlabs\Web\Controller {
// first name
if(strpos($x,' '))
$test[] = legal_webbie(substr($x,0,strpos($x,' ')));
if($test[0]) {
if (!empty($test[0])) {
// first name plus first initial of last
$test[] = ((strpos($x,' ')) ? $test[0] . legal_webbie(trim(substr($x,strpos($x,' '),2))) : '');
// first name plus random number

View File

@@ -60,26 +60,16 @@ class Profile extends Controller {
'rel' => 'alternate',
'type' => 'application/atom+xml',
'title' => t('Posts and comments'),
'href' => z_root() . '/feed/' . $which
'href' => z_root() . '/feed/' . $which . '?top=0'
]);
head_add_link([
'rel' => 'alternate',
'type' => 'application/atom+xml',
'title' => t('Only posts'),
'href' => z_root() . '/feed/' . $which . '?f=&top=1'
'href' => z_root() . '/feed/' . $which . '?top=1'
]);
if (!$profile) {
$x = q("select channel_id as profile_uid from channel where channel_address = '%s' limit 1",
dbesc(argv(1))
);
if ($x) {
App::$profile = $x[0];
}
}
profile_load($which, $profile);
}

View File

@@ -8,15 +8,18 @@
namespace Zotlabs\Widget;
use App;
class Fullprofile {
function widget($arr) {
if(!(isset(\App::$profile['profile_uid']) && \App::$profile['profile_uid']))
if (empty(App::$profile['profile_uid'])) {
return;
}
$block = observer_prohibited();
return profile_sidebar(\App::$profile, $block, true, true);
return profile_sidebar(App::$profile, $block, true, true);
}
}

View File

@@ -13,7 +13,7 @@ use App;
class Profile {
function widget($args) {
if(!isset(App::$profile['profile_uid'])) {
if (empty(App::$profile['profile_uid'])) {
return;
}

View File

@@ -70,7 +70,7 @@ require_once('include/security.php');
define('PLATFORM_NAME', 'hubzilla');
define('STD_VERSION', '11.2');
define('STD_VERSION', '11.2.1');
define('ZOT_REVISION', '6.0');
define('DB_UPDATE_VERSION', 1265);

View File

@@ -4,7 +4,7 @@
"type": "@type",
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@type": "@id"
},
"DataIntegrityProof": {
"@id": "https://w3id.org/security#DataIntegrityProof"
@@ -35,19 +35,19 @@
},
"assertionMethod": {
"@id": "https://w3id.org/security#assertionMethod",
"@type": "@id",
"@type": "@id"
},
"authentication": {
"@id": "https://w3id.org/security#authenticationMethod",
"@type": "@id",
"@type": "@id"
},
"capabilityInvocation": {
"@id": "https://w3id.org/security#capabilityInvocationMethod",
"@type": "@id",
"@type": "@id"
},
"capabilityDelegation": {
"@id": "https://w3id.org/security#capabilityDelegationMethod",
"@type": "@id",
"@type": "@id"
},
"keyAgreement": {
"@id": "https://w3id.org/security#keyAgreementMethod",

View File

@@ -0,0 +1,106 @@
<?php
/*
* SPDX-FileCopyrightText: 2026 The Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Tests\Unit\Lib;
use Zotlabs\Lib\Crypto;
use Zotlabs\Lib\LDSignatures;
use Zotlabs\Tests\Unit\UnitTestCase;
class LDSignaturesTest extends UnitTestCase {
public function testVerifyLDSignature(): void {
$channel = [
'channel_address' => 'test',
'channel_prvkey' => <<<'EOD'
-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvUNHsxNNL8egKxBl
rQdhdKz7N1DfJ8yMAYGPizta9uZ9uoe2qbgYpFPP41gbWvCJqDptmRCWXVZnmH6E
Pe6pLwIDAQABAkBtvgJoKsv55YXREqvyPbJbxiXQuFr9J9US1n5WXG9tc8+S1SB3
Azh7GtORAVnFkba5Ruj/Qij+CLe1ggCkwu0pAiEA41HXbZzQbb4hOxB9mkVvlMYj
r8UqOEtbvKEpnCUAeLUCIQDVJD8BnuCLKRxdtJRbWjwSe++/czwCTVTFv+XIyXX8
0wIgMx6qhZnoPWWub2vr8w9+YkSUreh28CXyQV80zkp76qkCIQCA1X34Ps6/j0QE
KCkc5vg0vBF5CfCV+6RoO8xrh8r33QIhANFLEX+THSmi6s+/d3rUHRqj7cTzJdHh
31v2ixfbFhB6
-----END PRIVATE KEY-----
EOD,
'channel_pubkey' => <<<'EOD'
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL1DR7MTTS/HoCsQZa0HYXSs+zdQ3yfM
jAGBj4s7WvbmfbqHtqm4GKRTz+NYG1rwiag6bZkQll1WZ5h+hD3uqS8CAwEAAQ==
-----END PUBLIC KEY-----
EOD,
];
$activity = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'type' => 'Like',
'actor' => 'https://hubzilla.test/channel/test',
'to' => [
'https://hatchat.example/sarah/',
'https://example.com/peeps/john/',
],
'object' => [
'@context' => ['@language' => 'en'],
'id' => 'https://example.org/~alice/note/23',
'type' => 'Note',
'attributedTo' => 'https://example.org/~alice',
'content' => "I'm a goat",
],
'signature' => [
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'type' => 'RsaSignature2017',
'nonce' => 'e3158dce627da99138d238805d2f129db05bd65efce0f712de494565c852f7fc',
'created' => '2026-05-01T09:22:05Z',
'creator' => 'https://hubzilla.test/channel/test',
'signatureValue' =>
'iFo/j+sG4h7zDiVis8FMOaJvw7ZanbsPdUrS5WTUkcMzL/dg3xPPpt9pKREhVx73ne6y6tIjzq6TWzWEpYvUFg=='
],
];
$this->assertTrue(LDSignatures::verify($activity, $channel['channel_pubkey']));
// Verify that the signature is independent of the order
// of the fields
$activity_2 = [
'type' => 'Like',
'@context' => 'https://www.w3.org/ns/activitystreams',
'actor' => 'https://hubzilla.test/channel/test',
'to' => [
'https://example.com/peeps/john/',
'https://hatchat.example/sarah/',
],
'signature' => [
'nonce' => 'e3158dce627da99138d238805d2f129db05bd65efce0f712de494565c852f7fc',
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'created' => '2026-05-01T09:22:05Z',
'type' => 'RsaSignature2017',
'creator' => 'https://hubzilla.test/channel/test',
'signatureValue' =>
'iFo/j+sG4h7zDiVis8FMOaJvw7ZanbsPdUrS5WTUkcMzL/dg3xPPpt9pKREhVx73ne6y6tIjzq6TWzWEpYvUFg=='
],
'object' => [
'@context' => ['@language' => 'en'],
'type' => 'Note',
'attributedTo' => 'https://example.org/~alice',
'id' => 'https://example.org/~alice/note/23',
'content' => "I'm a goat",
],
];
$this->assertFalse($activity == $activity_2);
$this->assertTrue(LDSignatures::verify($activity_2, $channel['channel_pubkey']));
}
}

View File

@@ -118,37 +118,20 @@ channel:
channel_startpage: ""
channel_pubkey: |
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAn2ads2RKygqTbN0kxuGI
3Vuh/1SSvHls2cYMXZJpwqix5+y2/zqqzAVUayMCPt1ZHLfc+uG0Z97+XmWNuuIo
RCBB5Os29NKo4+K66prYlfBFeuvji9tV1zGwBMYMC2RabITIjH9yazE+Tox54iV+
kw/IYPsnv4vPIcuC8dnvZ0v7/Kkhm6Hp0ZrGRNKccHuinJeiuhvfXXG3xrrBrMY7
iw7I3i2hvw/BSFdJ1RnuMGlr2W3qcZQvlqCWIGAzEpmXom9bwBKo/ZZ2MSJCgEVY
KKPqjLs/7IKRzlzUFIuk8Uibrhoxmc0NaqIeQOUtyelFdc/P3p2fEjwJd7sr8dzo
NFVgUMHGiqu0C5fZ90KnfLNrXJcMb4VjsvGFyIDt0AZY5w4rjoKmjlB/p4/CRfyz
qFnp2ZK4eRPqurmGwnSArsjPLMhWjlc7NRXtQ1nyMNAEi4RRhlJyjmvJIRrdeVu2
Nq6ZknBq0SeDlH/3uRfNPwHxqgDFNTCgsg4/Lgj2au1XbaEBlgRq3PDAh4a3H+fO
iGKinJy2/p9TGvrKTcO3mremHbuVg41G6FjxvzuZKx3P7ssN4KBlCJ9L/tJX5RDn
4m5u4ZdaPZBXxjgXj24SDLETaz+K0DxwtjGQ7kTpC0zv6hLcEGxS2220cX7Msgih
pWX+ItWfet/uLkmfyCpaxt0CAwEAAQ==
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL1DR7MTTS/HoCsQZa0HYXSs+zdQ3yfM
jAGBj4s7WvbmfbqHtqm4GKRTz+NYG1rwiag6bZkQll1WZ5h+hD3uqS8CAwEAAQ==
-----END PUBLIC KEY-----
channel_prvkey: |
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCfZp2zZErKCpNs
3STG4YjdW6H/VJK8eWzZxgxdkmnCqLHn7Lb/OqrMBVRrIwI+3Vkct9z64bRn3v5e
ZY264ihEIEHk6zb00qjj4rrqmtiV8EV66+OL21XXMbAExgwLZFpshMiMf3JrMT5O
jHniJX6TD8hg+ye/i88hy4Lx2e9nS/v8qSGboenRmsZE0pxwe6Kcl6K6G99dcbfG
usGsxjuLDsjeLaG/D8FIV0nVGe4waWvZbepxlC+WoJYgYDMSmZeib1vAEqj9lnYx
IkKARVgoo+qMuz/sgpHOXNQUi6TxSJuuGjGZzQ1qoh5A5S3J6UV1z8/enZ8SPAl3
uyvx3Og0VWBQwcaKq7QLl9n3Qqd8s2tclwxvhWOy8YXIgO3QBljnDiuOgqaOUH+n
j8JF/LOoWenZkrh5E+q6uYbCdICuyM8syFaOVzs1Fe1DWfIw0ASLhFGGUnKOa8kh
Gt15W7Y2rpmScGrRJ4OUf/e5F80/AfGqAMU1MKCyDj8uCPZq7VdtoQGWBGrc8MCH
hrcf586IYqKcnLb+n1Ma+spNw7eat6Ydu5WDjUboWPG/O5krHc/uyw3goGUIn0v+
0lflEOfibm7hl1o9kFfGOBePbhIMsRNrP4rQPHC2MZDuROkLTO/qEtwQbFLbbbRx
fsyyCKGlZf4i1Z963+4uSZ/IKlrG3QIDAQABAoICAAGXQ9gpllXVYgV1CzwBPhKI
KqynyAN3CnHS+EZ1IraVZJnTYuaBtqOCIxv4Sw+xua+1GxDARhgU9utzboUbYuT+
gGyGjMjH6HUbyxtHz9pmkz7ykesQVZc9UPZ/O9lfl0fG0dHrJ2BwAEvxkCQvFR1m
doIXR50xtJpvqPR9JRaeFhtTsc5B667n8Ut4YaTSwGZOh3UoODllxucwxSQ9DJTq
9nDB5P/JP7fXMoQHYKPy4
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvUNHsxNNL8egKxBl
rQdhdKz7N1DfJ8yMAYGPizta9uZ9uoe2qbgYpFPP41gbWvCJqDptmRCWXVZnmH6E
Pe6pLwIDAQABAkBtvgJoKsv55YXREqvyPbJbxiXQuFr9J9US1n5WXG9tc8+S1SB3
Azh7GtORAVnFkba5Ruj/Qij+CLe1ggCkwu0pAiEA41HXbZzQbb4hOxB9mkVvlMYj
r8UqOEtbvKEpnCUAeLUCIQDVJD8BnuCLKRxdtJRbWjwSe++/czwCTVTFv+XIyXX8
0wIgMx6qhZnoPWWub2vr8w9+YkSUreh28CXyQV80zkp76qkCIQCA1X34Ps6/j0QE
KCkc5vg0vBF5CfCV+6RoO8xrh8r33QIhANFLEX+THSmi6s+/d3rUHRqj7cTzJdHh
31v2ixfbFhB6
-----END PRIVATE KEY-----
channel_epubkey: "cBYhT+C+ZzzXDLlZh1hEsWzz5HI06TwW6o8HgfyLlFo"
channel_eprvkey: "/ClTxXKn7Lts+O6CgqJ2cCe0FlKpNSZfrBLPaB1Bq2twFiFP4L5nPNcMuVmHWESxbPPkcjTpPBbqjweB/IuUWg"
channel_notifyflags: 65535