1
0
mirror of https://github.com/flarum/core.git synced 2025-08-23 08:33:45 +02:00

Compare commits

...

18 Commits

Author SHA1 Message Date
luceos
aea72957fd Apply fixes from StyleCI
[ci skip] [skip ci]
2021-06-28 09:37:49 +00:00
Daniel Klabbers
838d9c5106 Allow easier extensibility of page document
This allows extensions to:

- mutate the json api document sent with php documents
- override/interact with the Document

Right now extensions need to replace the complete Frontend class
in order to interact with the document. Let's abstract into ioc
so that advanced devs can interact with it.
2021-06-28 11:36:03 +02:00
Daniel Klabbers
e92c267cde update version constant for the next release 2021-06-22 23:38:47 +02:00
Daniel Klabbers
f959a69530 changelog entry for laravel filesystem issue 2021-06-22 23:15:25 +02:00
Daniel Klabbers
4e246779f4 changelog so far for 1.0.3 2021-06-22 23:15:25 +02:00
Daniel Klabbers
5b0f5aeaa0 updated foundation version 2021-06-22 23:15:25 +02:00
Daniel Klabbers
6e92af8b00 Fixes issue with Laravel 8.48 filesystem changes
The FilesystemManager has changed to also allow to override
the config while resolving a filesystem.

This PR adds the argument and applies it if provided.
2021-06-22 23:07:41 +02:00
flarum-bot
1cf9491fe6 Bundled output for commit 3fcc7bd3b9
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2021-06-21 22:16:28 +00:00
ctml91
3fcc7bd3b9 use display name for avatar color gen 2021-06-22 00:14:37 +02:00
Daniel Klabbers
4acff91f80 allows replacing maintenance mode handler using ioc 2021-06-22 00:10:41 +02:00
Daniël Klabbers
a0152ffb18 Dw/huntr fix path traversal (#2931)
* Fix Huntr vuln with possible directory traversal
* Use `active_url` in Laravel validator
2021-06-21 10:14:15 +02:00
David Wheatley
d1e38558c5 Fix image avatar alignment in notifications (#2906) 2021-06-11 12:13:57 +01:00
Daniël Klabbers
0cca808275 minor improvements to the security policy 2021-06-10 21:56:30 +02:00
Daniël Klabbers
5ee5f82e3d huntr.dev as first point for security vuln (#2918)
* huntr.dev as first point for security vuln

* add badge for huntr.dev
2021-06-10 16:26:40 +02:00
Daniël Klabbers
9077fef5b2 clean up of composer.json, added funding and more support links 2021-06-08 01:58:37 +02:00
Daniël Klabbers
93cebec0be remove tidelift, we stopped doing that 2021-06-08 01:54:11 +02:00
Daniël Klabbers
a4a81c0ec2 Remove [forum] prefix in some mails
fixes #2515
2021-06-08 01:28:04 +02:00
David Wheatley
50dcfdb2a6 Mark typings as generated code (#2886) 2021-06-07 13:12:43 +01:00
22 changed files with 322 additions and 29 deletions

1
.gitattributes vendored
View File

@@ -12,5 +12,6 @@ tests export-ignore
js/dist/* -diff
js/dist/* linguist-generated
js/dist-typings/* linguist-generated
* text=auto eol=lf

1
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,2 @@
github: flarum
open_collective: flarum
tidelift: packagist/flarum/core

14
.github/SECURITY.md vendored
View File

@@ -1,13 +1,13 @@
# Security Policy
## Supported Versions
## Versions
We will only patch security vulnerabilities in the stable 1.x release.
Due to the nature of our project - being open source - we have decided to patch only the latest major release (currently v1.x) for security vulnerabilities.
## Reporting a Vulnerability
## How to disclose
If you discover a security vulnerability within Flarum, please send an email to security@flarum.org so we can address it promptly.
Please use [huntr.dev](https://huntr.dev/) for security issues that affect our project. If you believe you have found a vulnerability, please disclose it via [this form](https://huntr.dev/bounties/disclose/?target=https://github.com/flarum/core).
We will get back to you as time allows.
Discussions may commence internally, so you may not hear back immediately.
When reporting a vulnerability, please provide your GitHub username (if available), so that we can invite you to collaborate on a [security advisory on GitHub](https://help.github.com/en/articles/about-maintainer-security-advisories).
This will enable us to **review** the vulnerability, **fix** it promptly, and **reward** you for your efforts.
If you have any questions about the process, feel free to reach out to security@huntr.dev or security@flarum.org.

View File

@@ -1,5 +1,19 @@
# Changelog
## [1.0.3](https://github.com/flarum/core/compare/v1.0.2...v1.0.3)
### Changed
- Removed [forum] prefix from Request Password and Email Confirmation emails ([a4a81c0](https://github.com/flarum/core/commit/a4a81c0ec237476cd6e7ca00c1ed9465493af476))
- Adopt huntr.dev for handling our security vulnerability reports (https://github.com/flarum/core/pull/2918)
- Maintenance handler can now be replaced through the service container (ioc) ([4acff91](https://github.com/flarum/core/commit/4acff91f8063fcced9bf8c9a76fbb510d06823c0))
- The colors on the auto generated avatars are now based on the Display Name of the user (https://github.com/flarum/core/pull/2873)
### Fixed
- Avatar in notifications list are incorrectly aligned (https://github.com/flarum/core/pull/2906)
- FilesystemManager is not compatible with upstream Laravel implementation (https://github.com/flarum/core/pull/2936)
## [1.0.2](https://github.com/flarum/core/compare/v1.0.1...v1.0.2)
### Fixed

View File

@@ -5,6 +5,7 @@
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/packagist/dt/flarum/core" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/github/v/release/flarum/core?sort=semver" alt="Latest Version"></a>
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/packagist/l/flarum/core" alt="License"></a>
<a href="https://huntr.dev/bounties/disclose/?target=https://github.com/flarum/core"><img src="https://cdn.huntr.dev/huntr_security_badge_mono.svg" alt="huntr"></a>
<a href="https://github.styleci.io/repos/28257573"><img src="https://github.styleci.io/repos/28257573/shield?style=flat" alt="StyleCI"></a>
</p>

View File

@@ -14,10 +14,26 @@
"homepage": "https://flarum.org/team"
}
],
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/flarum"
},
{
"type": "github",
"url": "https://github.com/sponsors/flarum"
},
{
"type": "other",
"url": "https://flarum.org/donate"
}
],
"support": {
"issues": "https://github.com/flarum/core/issues",
"source": "https://github.com/flarum/core",
"docs": "https://flarum.org/docs/"
"docs": "https://docs.flarum.org",
"forum": "https://discuss.flarum.org",
"chat": "https://flarum.org/chat"
},
"require": {
"php": ">=7.3",

2
js/dist/admin.js generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/admin.js.map generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/forum.js generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/forum.js.map generated vendored

File diff suppressed because one or more lines are too long

View File

@@ -35,11 +35,11 @@ Object.assign(User.prototype, {
canDelete: Model.attribute('canDelete'),
avatarColor: null,
color: computed('username', 'avatarUrl', 'avatarColor', function (username, avatarUrl, avatarColor) {
color: computed('displayName', 'avatarUrl', 'avatarColor', function (displayName, avatarUrl, avatarColor) {
// If we've already calculated and cached the dominant color of the user's
// avatar, then we can return that in RGB format. If we haven't, we'll want
// to calculate it. Unless the user doesn't have an avatar, in which case
// we generate a color from their username.
// we generate a color from their display name.
if (avatarColor) {
return 'rgb(' + avatarColor.join(', ') + ')';
} else if (avatarUrl) {
@@ -47,7 +47,7 @@ Object.assign(User.prototype, {
return '';
}
return '#' + stringToColor(username);
return '#' + stringToColor(displayName);
}),
/**

View File

@@ -136,6 +136,14 @@
.Avatar--size(24px);
grid-area: avatar;
}
// Since images don't have baselines, aligning against the baseline won't work.
// Instead we need to do some manual hackery to fix then, otherwise they won't
// be correctly vertically aligned.
img.Avatar {
align-self: flex-start;
margin-top: -2px;
}
&-icon {
font-size: 14px;

View File

@@ -35,16 +35,16 @@ class FilesystemManager extends LaravelFilesystemManager
/**
* @inheritDoc
*/
protected function resolve($name): Filesystem
protected function resolve($name, $config = null): Filesystem
{
$driver = $this->getDriver($name);
$localConfig = $this->getLocalConfig($name);
$localConfig = $config ?? $this->getLocalConfig($name);
if (empty($localConfig)) {
throw new InvalidArgumentException("Disk [{$name}] has not been declared. Use the Filesystem extender to do this.");
}
$driver = $config['driver'] ?? $this->getDriver($name);
if ($driver === 'local') {
return $this->createLocalDriver($localConfig);
}

View File

@@ -21,7 +21,7 @@ class Application
*
* @var string
*/
const VERSION = '1.0.2';
const VERSION = '1.0.4-dev';
/**
* The IoC container for the Flarum application.

View File

@@ -48,7 +48,7 @@ class InstalledApp implements AppInterface
public function getRequestHandler()
{
if ($this->config->inMaintenanceMode()) {
return new MaintenanceModeHandler();
return $this->container->make('flarum.maintenance.handler');
} elseif ($this->needsUpdate()) {
return $this->getUpdaterHandler();
}

View File

@@ -105,6 +105,7 @@ class InstalledSite implements SiteInterface
$container->alias('flarum.config', Config::class);
$container->instance('flarum.debug', $this->config->inDebugMode());
$container->instance('config', $config = $this->getIlluminateConfig($laravel));
$container->instance('flarum.maintenance.handler', new MaintenanceModeHandler);
$this->registerLogger($container);
$this->registerCache($container);

View File

@@ -49,7 +49,9 @@ class Frontend
{
$forumDocument = $this->getForumDocument($request);
$document = new Document($this->view, $forumDocument, $request);
$responseDocument = resolve('flarum.frontend.document');
$document = $responseDocument($forumDocument, $request);
$this->populate($document, $request);

View File

@@ -16,6 +16,8 @@ use Flarum\Http\UrlGenerator;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\View\Factory;
use Psr\Http\Message\ServerRequestInterface as Request;
class FrontendServiceProvider extends AbstractServiceProvider
{
@@ -57,6 +59,16 @@ class FrontendServiceProvider extends AbstractServiceProvider
return $frontend;
};
});
$this->container->singleton('flarum.frontend.document', function (Container $container) {
return function (array $apiDocument, Request $request) use ($container) {
return new Document(
$container->make(Factory::class),
$apiDocument,
$request
);
};
});
}
/**

View File

@@ -21,8 +21,10 @@ use Flarum\User\UserValidator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Validation\Factory;
use Illuminate\Validation\ValidationException;
use Intervention\Image\ImageManager;
use InvalidArgumentException;
class RegisterUserHandler
{
@@ -36,12 +38,16 @@ class RegisterUserHandler
/**
* @var UserValidator
*/
protected $validator;
protected $userValidator;
/**
* @var AvatarUploader
*/
protected $avatarUploader;
/**
* @var Factory
*/
private $validator;
/**
* @param Dispatcher $events
@@ -49,12 +55,13 @@ class RegisterUserHandler
* @param UserValidator $validator
* @param AvatarUploader $avatarUploader
*/
public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, AvatarUploader $avatarUploader)
public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $userValidator, AvatarUploader $avatarUploader, Factory $validator)
{
$this->events = $events;
$this->settings = $settings;
$this->validator = $validator;
$this->userValidator = $userValidator;
$this->avatarUploader = $avatarUploader;
$this->validator = $validator;
}
/**
@@ -101,7 +108,7 @@ class RegisterUserHandler
new Saving($user, $actor, $data)
);
$this->validator->assertValid(array_merge($user->getAttributes(), compact('password')));
$this->userValidator->assertValid(array_merge($user->getAttributes(), compact('password')));
$user->save();
@@ -134,8 +141,25 @@ class RegisterUserHandler
);
}
/**
* @throws InvalidArgumentException
*/
private function uploadAvatarFromUrl(User $user, string $url)
{
$urlValidator = $this->validator->make(compact('url'), [
'url' => 'required|active_url',
]);
if ($urlValidator->fails()) {
throw new InvalidArgumentException('Provided avatar URL must be a valid URI.', 503);
}
$scheme = parse_url($url, PHP_URL_SCHEME);
if (! in_array($scheme, ['http', 'https'])) {
throw new InvalidArgumentException("Provided avatar URL must have scheme http or https. Scheme provided was $scheme.", 503);
}
$image = (new ImageManager)->make($url);
$this->avatarUploader->upload($user, $image);

View File

@@ -110,7 +110,7 @@ class RequestPasswordResetHandler
];
$body = $this->translator->trans('core.email.reset_password.body', $data);
$subject = '['.$data['forum'].'] '.$this->translator->trans('core.email.reset_password.subject');
$subject = $this->translator->trans('core.email.reset_password.subject');
$this->queue->push(new SendRawEmailJob($user->email, $subject, $body));

View File

@@ -52,7 +52,7 @@ class EmailConfirmationMailer
$data = $this->getEmailData($event->user, $email);
$body = $this->translator->trans('core.email.confirm_email.body', $data);
$subject = '['.$data['forum'].'] '.$this->translator->trans('core.email.confirm_email.subject');
$subject = $this->translator->trans('core.email.confirm_email.subject');
$this->queue->push(new SendRawEmailJob($email, $subject, $body));
}

View File

@@ -12,6 +12,7 @@ namespace Flarum\Tests\integration\api\users;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\RegistrationToken;
use Flarum\User\User;
class CreateTest extends TestCase
@@ -168,4 +169,218 @@ class CreateTest extends TestCase
$settings->set('allow_sign_up', true);
}
/**
* @test
*/
public function cannot_create_user_with_invalid_avatar_uri_scheme()
{
// Boot app
$this->app();
$regTokens = [];
// Add registration tokens that should cause a failure
$regTokens[] = [
'token' => RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'file://localhost/etc/passwd'
], []),
'scheme' => 'file'
];
$regTokens[] = [
'token' => RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'ftp://localhost/image.png'
], []),
'scheme' => 'ftp'
];
// Test each reg token
foreach ($regTokens as $regToken) {
$regToken['token']->saveOrFail();
// Call the registration endpoint
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'json' => [
'data' => [
'attributes' => [
'token' => $regToken['token']->token,
],
]
],
]
)->withAttribute('bypassCsrfToken', true)
);
// The response body should contain details about the invalid URI
$body = (string) $response->getBody();
$this->assertJson($body);
$decodedBody = json_decode($body, true);
$this->assertEquals(500, $response->getStatusCode());
$firstError = $decodedBody['errors'][0];
// Check that the error is an invalid URI
$this->assertStringStartsWith('InvalidArgumentException: Provided avatar URL must have scheme http or https. Scheme provided was '.$regToken['scheme'].'.', $firstError['detail']);
}
}
/**
* @test
*/
public function cannot_create_user_with_invalid_avatar_uri()
{
// Boot app
$this->app();
$regTokens = [];
// Add registration tokens that should cause a failure
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://127.0.0.1/image.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://192.168.0.1/image.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => '../image.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'image.png'
], []);
// Test each reg token
foreach ($regTokens as $regToken) {
$regToken->saveOrFail();
// Call the registration endpoint
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'json' => [
'data' => [
'attributes' => [
'token' => $regToken->token,
],
]
],
]
)->withAttribute('bypassCsrfToken', true)
);
// The response body should contain details about the invalid URI
$body = (string) $response->getBody();
$this->assertJson($body);
$decodedBody = json_decode($body, true);
$this->assertEquals(500, $response->getStatusCode());
$firstError = $decodedBody['errors'][0];
// Check that the error is an invalid URI
$this->assertStringStartsWith('InvalidArgumentException: Provided avatar URL must be a valid URI.', $firstError['detail']);
}
}
/**
* @test
*/
public function can_create_user_with_valid_avatar_uri()
{
// Boot app
$this->app();
$regTokens = [];
// Add registration tokens that should work fine
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test1',
'email' => 'test1@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://via.placeholder.com/150.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '2', [
'username' => 'test2',
'email' => 'test2@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://via.placeholder.com/150.jpg'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '3', [
'username' => 'test3',
'email' => 'test3@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://via.placeholder.com/150.gif'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '4', [
'username' => 'test4',
'email' => 'test4@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'http://via.placeholder.com/150.png'
], []);
/**
* Test each reg token.
*
* @var RegistrationToken $regToken
*/
foreach ($regTokens as $regToken) {
$regToken->saveOrFail();
// Call the registration endpoint
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'json' => [
'data' => [
'attributes' => [
'token' => $regToken->token,
],
]
],
]
)->withAttribute('bypassCsrfToken', true)
);
$this->assertEquals(201, $response->getStatusCode());
$user = User::where('username', $regToken->user_attributes['username'])->firstOrFail();
$this->assertEquals($regToken->user_attributes['is_email_confirmed'], $user->is_email_confirmed);
$this->assertEquals($regToken->user_attributes['username'], $user->username);
$this->assertEquals($regToken->user_attributes['email'], $user->email);
}
}
}