Respect the view encrypted custom fields permission

This commit is contained in:
snipe
2026-05-18 14:18:58 +01:00
parent 87d6328fb8
commit 63454f8c63
3 changed files with 45 additions and 39 deletions
@@ -116,8 +116,8 @@ class ActionlogsTransformer
$clean_meta[$fieldname]['old'] = '************';
$clean_meta[$fieldname]['new'] = '************';
// Display the changes if the user is an admin or superadmin
if (Gate::allows('admin')) {
// Display the changes if the user has permission to view encrypted custom fields
if (Gate::allows('assets.view.encrypted_custom_fields')) {
$clean_meta[$fieldname]['old'] = ($enc_old) ? e(unserialize($enc_old, ['allowed_classes' => false])) : '';
$clean_meta[$fieldname]['new'] = ($enc_new) ? e(unserialize($enc_new, ['allowed_classes' => false])) : '';
}
+5
View File
@@ -180,6 +180,11 @@ class UserFactory extends Factory
return $this->appendPermission(['assets.view.requestable' => '1']);
}
public function viewEncryptedCustomFields()
{
return $this->appendPermission(['assets.view.encrypted_custom_fields' => '1']);
}
public function deleteAssetModels()
{
return $this->appendPermission(['models.delete' => '1']);
+38 -37
View File
@@ -10,7 +10,36 @@ use Tests\TestCase;
class AssetHistoryTest extends TestCase
{
public function test_encrypted_custom_field_values_are_html_encoded_in_history_for_admins()
private function findFieldEntry(array $rows, string $dbColumn): ?array
{
foreach ($rows as $row) {
$entry = ($row['log_meta'] ?? [])[$dbColumn] ?? null;
if ($entry !== null) {
return $entry;
}
}
return null;
}
private function updateAssetEncryptedField(Asset $asset, CustomField $field, string $value, User $actor): void
{
$this->actingAsForApi($actor)
->patchJson(route('api.assets.update', $asset->id), [
$field->db_column_name() => $value,
])
->assertOk();
}
private function getUpdateRows(Asset $asset, User $viewer): array
{
return $this->actingAsForApi($viewer)
->getJson(route('api.assets.history', ['asset' => $asset->id, 'action_type' => 'update']))
->assertOk()
->json('rows');
}
public function test_encrypted_custom_field_values_are_visible_to_users_with_encrypted_field_permission()
{
$this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL');
@@ -20,32 +49,19 @@ class AssetHistoryTest extends TestCase
]);
$superuser = User::factory()->superuser()->create();
$this->actingAsForApi($superuser)
->patchJson(route('api.assets.update', $asset->id), [
$field->db_column_name() => '<img src=x onerror=alert(1)>',
])
->assertOk();
$this->updateAssetEncryptedField($asset, $field, '<img src=x onerror=alert(1)>', $superuser);
$rows = $this->actingAsForApi($superuser)
->getJson(route('api.assets.history', ['asset' => $asset->id, 'action_type' => 'update']))
->assertOk()
->json('rows');
$viewer = User::factory()->viewAssets()->viewAssetHistory()->viewEncryptedCustomFields()->create();
$fieldEntry = null;
foreach ($rows as $row) {
$fieldEntry = ($row['log_meta'] ?? [])[$field->db_column] ?? null;
if ($fieldEntry !== null) {
break;
}
}
$this->assertNotNull($fieldEntry, 'Encrypted field change should appear in an update log_meta entry');
$fieldEntry = $this->findFieldEntry($this->getUpdateRows($asset, $viewer), $field->db_column);
$this->assertNotNull($fieldEntry, 'Encrypted field change should be visible to users with the encrypted fields permission');
$newValue = $fieldEntry['new'];
$this->assertStringNotContainsString('<img', $newValue, 'Raw HTML tag must not appear in history log_meta');
$this->assertStringContainsString('&lt;', $newValue, 'Value should be HTML-encoded in history log_meta');
}
public function test_encrypted_custom_field_values_are_masked_for_non_admins()
public function test_encrypted_custom_field_values_are_masked_for_users_without_encrypted_field_permission()
{
$this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL');
@@ -55,29 +71,14 @@ class AssetHistoryTest extends TestCase
]);
$superuser = User::factory()->superuser()->create();
$this->actingAsForApi($superuser)
->patchJson(route('api.assets.update', $asset->id), [
$field->db_column_name() => '<img src=x onerror=alert(1)>',
])
->assertOk();
$this->updateAssetEncryptedField($asset, $field, '<img src=x onerror=alert(1)>', $superuser);
$viewer = User::factory()->viewAssets()->viewAssetHistory()->create();
$rows = $this->actingAsForApi($viewer)
->getJson(route('api.assets.history', ['asset' => $asset->id, 'action_type' => 'update']))
->assertOk()
->json('rows');
$fieldEntry = null;
foreach ($rows as $row) {
$fieldEntry = ($row['log_meta'] ?? [])[$field->db_column] ?? null;
if ($fieldEntry !== null) {
break;
}
}
$fieldEntry = $this->findFieldEntry($this->getUpdateRows($asset, $viewer), $field->db_column);
if ($fieldEntry !== null) {
$this->assertEquals('************', $fieldEntry['new'], 'Non-admin should see masked value for encrypted field changes');
$this->assertEquals('************', $fieldEntry['new'], 'Users without encrypted field permission should see masked value');
$this->assertStringNotContainsString('<img', $fieldEntry['new']);
}
}