Add CheckPrivate page to manage ATA IDENTIFY DEVICE responses with potential private data

This commit is contained in:
2025-09-12 23:57:30 +01:00
parent c5af3bb0d2
commit 2f675757f1
3 changed files with 318 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
@page "/admin/ata/check-private"
@using System.Text
@using Aaru.CommonTypes.Structs.Devices.ATA
@attribute [Authorize]
@layout AdminLayout
@inject Microsoft.EntityFrameworkCore.IDbContextFactory<DbContext> DbContextFactory
<PageTitle>ATA IDENTIFY DEVICE responses with possible private data</PageTitle>
@if(!_initialized)
{
<div class="stats-section">
<h1 style="color: red; align-content: center; padding: 2rem">Loading...</h1>
</div>
return;
}
@if(_items.Count == 0)
{
<section class="stats-section">
No private data found.
<a href="/admin/ata" class="btn btn-primary">Back to list</a>
</section>
return;
}
<section class="stats-section">
<h4>ATA IDENTIFY DEVICE responses with possible private data</h4>
<div>
<a @onclick="ClearPrivateAll" class="btn btn-primary">Clear private fields for all</a>
<a @onclick="ClearReservedAll" class="btn btn-danger">Clear reserved fields for all</a>
<a href="/admin/ata" class="btn btn-secondary">Back to list</a>
</div>
<table class="table table-dark table-striped table-bordered mt-4 mb-4">
<thead class="thead-dark">
<tr>
<th class="fw-bold bg-secondary text-light">
@DisplayNameHelper.GetDisplayName(typeof(Ata), nameof(Ata.Id))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.Model))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.SerialNumber))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.WWN))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.WWNExtension))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.MediaSerial))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.ReservedWords121))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.ReservedWords129))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.ReservedCFA))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.ReservedCEATA224))
</th>
<th>
@DisplayNameHelper.GetDisplayName(typeof(Identify.IdentifyDevice), nameof(Identify.IdentifyDevice.ReservedWords))
</th>
<th class="fw-bold bg-secondary text-light">
Actions
</th>
</tr>
</thead>
<tbody>
@foreach(Ata item in _items)
{
<tr>
<td>
@item.Id
</td>
<td>
@item.IdentifyDevice?.Model
</td>
<td>
@item.IdentifyDevice?.SerialNumber
</td>
<td>
@item.IdentifyDevice?.WWN
</td>
<td>
@item.IdentifyDevice?.WWNExtension
</td>
<td>
@item.IdentifyDevice?.MediaSerial
</td>
<td>
@Encoding.ASCII.GetString(item.Identify, 121 * 2, 10).Replace("\0", "")
</td>
<td>
@Encoding.ASCII.GetString(item.Identify, 129 * 2, 62).Replace("\0", "")
</td>
<td>
@Encoding.ASCII.GetString(item.Identify, 161 * 2, 14).Replace("\0", "")
</td>
<td>
@Encoding.ASCII.GetString(item.Identify, 224 * 2, 12).Replace("\0", "")
</td>
<td>
@Encoding.ASCII.GetString(item.Identify, 236 * 2, 38).Replace("\0", "")
</td>
<td>
<a @onclick="() => ClearPrivate(item.Id)" class="btn btn-primary">Clear private fields</a>
<a @onclick="() => ClearReserved(item.Id)" class="btn btn-danger">Clear reserved fields</a>
</td>
</tr>
}
</tbody>
</table>
</section>

View File

@@ -0,0 +1,195 @@
using Aaru.CommonTypes.Structs.Devices.ATA;
using Aaru.Server.Database;
namespace Aaru.Server.Components.Admin.Pages.Ata;
public partial class CheckPrivate
{
bool _initialized;
readonly List<CommonTypes.Metadata.Ata> _items = [];
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
StateHasChanged();
await RefreshItemsAsync();
_initialized = true;
StateHasChanged();
}
async Task RefreshItemsAsync()
{
_items.Clear();
byte[] tmp;
await using DbContext _context = await DbContextFactory.CreateDbContextAsync();
foreach(CommonTypes.Metadata.Ata ata in _context.Ata)
{
Identify.IdentifyDevice? id = ata.IdentifyDevice;
if(id is null) continue;
if(!string.IsNullOrWhiteSpace(((Identify.IdentifyDevice)id).SerialNumber) ||
((Identify.IdentifyDevice)id).WWN != 0 ||
((Identify.IdentifyDevice)id).WWNExtension != 0 ||
!string.IsNullOrWhiteSpace(((Identify.IdentifyDevice)id).MediaSerial))
{
_items.Add(ata);
continue;
}
tmp = new byte[10];
Array.Copy(ata.Identify, 121 * 2, tmp, 0, 10);
if(tmp.All(static b => b > 0x20) && tmp.All(static b => b <= 0x5F))
{
_items.Add(ata);
continue;
}
tmp = new byte[62];
Array.Copy(ata.Identify, 129 * 2, tmp, 0, 62);
if(tmp.All(static b => b > 0x20) && tmp.All(static b => b <= 0x5F))
{
_items.Add(ata);
continue;
}
tmp = new byte[14];
Array.Copy(ata.Identify, 161 * 2, tmp, 0, 14);
if(tmp.All(static b => b > 0x20) && tmp.All(static b => b <= 0x5F))
{
_items.Add(ata);
continue;
}
tmp = new byte[12];
Array.Copy(ata.Identify, 224 * 2, tmp, 0, 12);
if(tmp.All(static b => b > 0x20) && tmp.All(static b => b <= 0x5F))
{
_items.Add(ata);
continue;
}
tmp = new byte[38];
Array.Copy(ata.Identify, 236 * 2, tmp, 0, 38);
if(tmp.All(static b => b > 0x20) && tmp.All(static b => b <= 0x5F)) _items.Add(ata);
}
}
async Task ClearPrivateAll()
{
await using DbContext ctx = await DbContextFactory.CreateDbContextAsync();
foreach(CommonTypes.Metadata.Ata ata in ctx.Ata)
{
// Serial number
for(int i = 0; i < 20; i++) ata.Identify[10 * 2 + i] = 0x20;
// Media serial number
for(int i = 0; i < 40; i++) ata.Identify[176 * 2 + i] = 0x20;
// WWN and WWN Extension
for(int i = 0; i < 16; i++) ata.Identify[108 * 2 + i] = 0;
// We need to tell EFCore the entity has changed
ctx.Update(ata);
}
await ctx.SaveChangesAsync();
}
async Task ClearReservedAll()
{
await using DbContext ctx = await DbContextFactory.CreateDbContextAsync();
foreach(CommonTypes.Metadata.Ata ata in ctx.Ata)
{
// ReservedWords121
for(int i = 0; i < 10; i++) ata.Identify[121 * 2 + i] = 0;
// ReservedWords129
for(int i = 0; i < 40; i++) ata.Identify[129 * 2 + i] = 0;
// ReservedCFA
for(int i = 0; i < 14; i++) ata.Identify[161 * 2 + i] = 0;
// ReservedCEATA224
for(int i = 0; i < 12; i++) ata.Identify[224 * 2 + i] = 0;
// ReservedWords
for(int i = 0; i < 14; i++) ata.Identify[161 * 2 + i] = 0;
// We need to tell EFCore the entity has changed
ctx.Update(ata);
}
await ctx.SaveChangesAsync();
}
async Task ClearPrivate(int id)
{
await using DbContext ctx = await DbContextFactory.CreateDbContextAsync();
CommonTypes.Metadata.Ata? ata = ctx.Ata.FirstOrDefault(a => a.Id == id);
if(ata is null) return;
// Serial number
for(int i = 0; i < 20; i++) ata.Identify[10 * 2 + i] = 0x20;
// Media serial number
for(int i = 0; i < 40; i++) ata.Identify[176 * 2 + i] = 0x20;
// WWN and WWN Extension
for(int i = 0; i < 16; i++) ata.Identify[108 * 2 + i] = 0;
// We need to tell EFCore the entity has changed
ctx.Update(ata);
await ctx.SaveChangesAsync();
await RefreshItemsAsync();
}
async Task ClearReserved(int id)
{
await using DbContext ctx = await DbContextFactory.CreateDbContextAsync();
CommonTypes.Metadata.Ata? ata = ctx.Ata.FirstOrDefault(a => a.Id == id);
if(ata is null) return;
// ReservedWords121
for(int i = 0; i < 10; i++) ata.Identify[121 * 2 + i] = 0;
// ReservedWords129
for(int i = 0; i < 40; i++) ata.Identify[129 * 2 + i] = 0;
// ReservedCFA
for(int i = 0; i < 14; i++) ata.Identify[161 * 2 + i] = 0;
// ReservedCEATA224
for(int i = 0; i < 12; i++) ata.Identify[224 * 2 + i] = 0;
// ReservedWords
for(int i = 0; i < 14; i++) ata.Identify[161 * 2 + i] = 0;
// We need to tell EFCore the entity has changed
ctx.Update(ata);
await ctx.SaveChangesAsync();
await RefreshItemsAsync();
}
}

View File

@@ -17,6 +17,7 @@
<section class="stats-section">
<h4>ATA IDENTIFY DEVICE responses</h4>
<a href="/admin/ata/check-private" class="btn btn-primary mb-3">Check for private data</a>
<table class="table table-dark table-striped table-bordered mt-4 mb-4">
<thead class="thead-dark">
<tr>