Implements detect-redact-sanitize pipeline with regex, domain rules, and ONNX NER before the LLM boundary, plus NUnit tests and Xenovex push documentation. Co-authored-by: Cursor <cursoragent@cursor.com>
59 lines
2.2 KiB
C#
59 lines
2.2 KiB
C#
using PiiRedaction.Core.Abstractions;
|
|
using PiiRedaction.Core.Models;
|
|
|
|
namespace PiiRedaction.Core.Redaction;
|
|
|
|
public sealed class PlaceholderPiiRedactor : IPiiRedactor
|
|
{
|
|
public RedactionResult Redact(string text, IReadOnlyList<PiiEntity> entities)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(text);
|
|
ArgumentNullException.ThrowIfNull(entities);
|
|
|
|
var placeholderByValue = new Dictionary<string, string>(StringComparer.Ordinal);
|
|
var counters = new Dictionary<PiiEntityType, int>();
|
|
var orderedEntities = entities.OrderByDescending(entity => entity.StartIndex).ToList();
|
|
var sanitized = text;
|
|
|
|
foreach (var entity in orderedEntities)
|
|
{
|
|
var mapKey = CreateValueKey(entity);
|
|
if (!placeholderByValue.TryGetValue(mapKey, out var placeholder))
|
|
{
|
|
counters.TryGetValue(entity.Type, out var count);
|
|
count++;
|
|
counters[entity.Type] = count;
|
|
placeholder = $"<{ToPlaceholderPrefix(entity.Type)}_{count}>";
|
|
placeholderByValue[mapKey] = placeholder;
|
|
}
|
|
|
|
sanitized = string.Concat(
|
|
sanitized.AsSpan(0, entity.StartIndex),
|
|
placeholder,
|
|
sanitized.AsSpan(entity.EndIndex));
|
|
}
|
|
|
|
var placeholderMap = placeholderByValue
|
|
.ToDictionary(pair => pair.Value, pair => pair.Key.Split('|', 2)[1], StringComparer.Ordinal);
|
|
|
|
return new RedactionResult(sanitized, placeholderMap);
|
|
}
|
|
|
|
private static string CreateValueKey(PiiEntity entity) =>
|
|
$"{entity.Type}|{entity.Value}";
|
|
|
|
private static string ToPlaceholderPrefix(PiiEntityType type) => type switch
|
|
{
|
|
PiiEntityType.Person => "PERSON",
|
|
PiiEntityType.Email => "EMAIL",
|
|
PiiEntityType.Phone => "PHONE",
|
|
PiiEntityType.Pan => "PAN",
|
|
PiiEntityType.Aadhaar => "AADHAAR",
|
|
PiiEntityType.CreditCard => "CREDIT_CARD",
|
|
PiiEntityType.LoanNumber => "LOAN_NUMBER",
|
|
PiiEntityType.CustomerId => "CUSTOMER_ID",
|
|
PiiEntityType.AccountNumber => "ACCOUNT_NUMBER",
|
|
_ => type.ToString().ToUpperInvariant()
|
|
};
|
|
}
|