Add PII redaction POC for secure LLM prompting.
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>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
using FluentAssertions;
|
||||
using PiiRedaction.Core.Detection;
|
||||
using PiiRedaction.Core.Models;
|
||||
|
||||
namespace PiiRedaction.Core.Tests.Detection;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class RegexPiiDetectorTests
|
||||
{
|
||||
private readonly RegexPiiDetector _detector = new();
|
||||
|
||||
[Test]
|
||||
public void Detect_PositiveEmail_ReturnsEntity()
|
||||
{
|
||||
const string text = "Contact ravi.kumar@gmail.com now.";
|
||||
var entities = _detector.Detect(text);
|
||||
|
||||
entities.Should().ContainSingle(entity =>
|
||||
entity.Type == PiiEntityType.Email &&
|
||||
entity.Value == "ravi.kumar@gmail.com" &&
|
||||
entity.Source == PiiDetectionSource.Regex);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Detect_PositivePhone_ReturnsTenDigitEntity()
|
||||
{
|
||||
var entities = _detector.Detect("Call 9876543210 today.");
|
||||
|
||||
entities.Should().ContainSingle(entity =>
|
||||
entity.Type == PiiEntityType.Phone &&
|
||||
entity.Value == "9876543210" &&
|
||||
entity.Source == PiiDetectionSource.Regex);
|
||||
}
|
||||
|
||||
[TestCase("Aadhaar 1234 5678 9012 linked.", "1234 5678 9012")]
|
||||
[TestCase("Aadhaar 123456789012 linked.", "123456789012")]
|
||||
public void Detect_PositiveAadhaar_ReturnsEntity(string text, string expectedValue)
|
||||
{
|
||||
var entities = _detector.Detect(text);
|
||||
|
||||
entities.Should().ContainSingle(entity =>
|
||||
entity.Type == PiiEntityType.Aadhaar &&
|
||||
entity.Value == expectedValue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Detect_PositivePan_ReturnsUppercaseEntity()
|
||||
{
|
||||
var entities = _detector.Detect("PAN ABCDE1234F verified.");
|
||||
|
||||
entities.Should().ContainSingle(entity => entity.Type == PiiEntityType.Pan);
|
||||
entities[0].Value.Should().MatchRegex("^[A-Z]{5}\\d{4}[A-Z]$");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Detect_PositiveCreditCard_ReturnsEntity()
|
||||
{
|
||||
var entities = _detector.Detect("Card 4111-1111-1111-1111 used.");
|
||||
|
||||
entities.Should().ContainSingle(entity => entity.Type == PiiEntityType.CreditCard);
|
||||
}
|
||||
|
||||
[TestCase("not-an-email")]
|
||||
[TestCase("@missing.com")]
|
||||
[TestCase("pan abcde1234f")]
|
||||
[TestCase("Number 987654321")]
|
||||
public void Detect_NegativePatterns_ReturnsNoMatch(string text)
|
||||
{
|
||||
_detector.Detect(text).Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Detect_EmailSpan_HasCorrectIndices()
|
||||
{
|
||||
const string text = "Email ravi@test.com end.";
|
||||
var entities = _detector.Detect(text);
|
||||
|
||||
var email = entities.Single(entity => entity.Type == PiiEntityType.Email);
|
||||
email.StartIndex.Should().Be(6);
|
||||
email.Length.Should().Be("ravi@test.com".Length);
|
||||
text[email.StartIndex..email.EndIndex].Should().Be("ravi@test.com");
|
||||
}
|
||||
|
||||
[TestCase(null)]
|
||||
[TestCase("")]
|
||||
[TestCase(" ")]
|
||||
public void Detect_InvalidInput_ThrowsArgumentException(string? text)
|
||||
{
|
||||
var action = () => _detector.Detect(text!);
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user