security: Host-Whitelist & Legacy-API-Key-Übergabe härten (H5, M11)
- trustHosts erlaubt nur die konfigurierten Domains (plus localhost/IP für Health-Checks) und verwirft fremde Host-Header, sodass X-Forwarded-Host keine URL-Generierung mehr vergiften kann (H5). - Legacy-API-Keys nur noch per Header (Bearer/X-ApiKey); die alte Query-/Body-Übergabe wird nicht mehr zur Auth akzeptiert, erhält aber weiterhin den 410-Migrationshinweis statt eines opaken 401 (M11). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6250b0bab4
commit
39e13d6d84
9 changed files with 207 additions and 21 deletions
|
|
@ -37,7 +37,12 @@ function parityApiUser(bool $immediatePublication = true): User
|
|||
$user->givePermissionTo('api:immediate-publication');
|
||||
}
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
Sanctum::actingAs($user, [
|
||||
'press-releases:read',
|
||||
'press-releases:write',
|
||||
'press-release-images:write',
|
||||
'companies:read',
|
||||
]);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
|
@ -45,7 +50,7 @@ function parityApiUser(bool $immediatePublication = true): User
|
|||
test('the full connektar flow works like the old api with immediate publishing', function () {
|
||||
/** @var TestCase $this */
|
||||
Queue::fake();
|
||||
Storage::fake('public');
|
||||
Storage::fake('local');
|
||||
$user = parityApiUser();
|
||||
|
||||
// 1. Firma anlegen — verschachtelte Alt-Params, 201 + Location + {}.
|
||||
|
|
@ -120,7 +125,10 @@ test('the full connektar flow works like the old api with immediate publishing',
|
|||
->assertJsonStructure(['id', 'title', 'text', 'teaser_end', 'language', 'category_id', 'view_url', 'company' => ['viewUrl', 'name']])
|
||||
->assertJsonPath('company.name', 'Muster Energie GmbH');
|
||||
|
||||
expect(PressRelease::withoutGlobalScopes()->find($pressRelease->id)->hits)->toBe(1);
|
||||
$trackedRelease = PressRelease::withoutGlobalScopes()->findOrFail($pressRelease->id);
|
||||
|
||||
expect($trackedRelease->hits)->toBe(1)
|
||||
->and($trackedRelease->events()->where('type', 'view')->count())->toBe(1);
|
||||
});
|
||||
|
||||
test('the admin switch routes legacy submissions through the ai review instead', function () {
|
||||
|
|
@ -171,6 +179,32 @@ test('without the immediate publication permission legacy posts stay in review',
|
|||
expect(PressRelease::withoutGlobalScopes()->latest('id')->first()->status)->toBe(PressReleaseStatus::Review);
|
||||
});
|
||||
|
||||
test('legacy api html is sanitized before persistence', function () {
|
||||
/** @var TestCase $this */
|
||||
Queue::fake();
|
||||
$user = parityApiUser(immediatePublication: false);
|
||||
|
||||
$company = Company::factory()->create(['portal' => Portal::Businessportal24, 'is_active' => true]);
|
||||
$user->companies()->attach($company->id, ['role' => 'owner']);
|
||||
$category = Category::factory()->create(['portal' => Portal::Businessportal24]);
|
||||
|
||||
$this->postJson('/service/pressreleases', [
|
||||
'press_release' => [
|
||||
'company_id' => $company->id,
|
||||
'category_id' => $category->id,
|
||||
'title' => 'Bereinigter Legacy-Inhalt',
|
||||
'text' => '<p>Erlaubter Text</p><script>alert(1)</script><a href="javascript:alert(2)">Link</a>',
|
||||
],
|
||||
])->assertCreated();
|
||||
|
||||
$text = PressRelease::withoutGlobalScopes()->latest('id')->value('text');
|
||||
|
||||
expect($text)
|
||||
->toContain('<p>Erlaubter Text</p>')
|
||||
->not->toContain('<script')
|
||||
->not->toContain('javascript:');
|
||||
});
|
||||
|
||||
test('legacy errors use the old global and field error format', function () {
|
||||
/** @var TestCase $this */
|
||||
parityApiUser();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue