DataGuard Engine is an anonymization and data loss prevention (DLP) engine featuring Streaming I/O, Zero-Garbage architecture, Virtual Threads (Project Loom), and strict Clean Architecture / SOLID compliance. It operates in two modes: PII file sanitization and DLP auditing for Git pre-commit hooks and CI/CD pipelines.
v1.2.0 Highlights: Pure Core engine (zero
System.errside effects),RuleProviderSPI enforced via OCP,LicenseValidatorSPI wired into CLI gate,DlpAuditResultdomain records, DRY-compliant single source of truth for all regex patterns, and a 24-test Red Team bypass suite.
Learning Project: This project was built to learn, practice and deepen skills in DevSecOps, Java 21 (Virtual Threads / Project Loom), Clean Architecture, SOLID principles, high-performance file processing, secure coding practices, JUnit 5 testing, Git-based security automation, and AI-assisted software engineering. It also serves as a hands-on exploration of how modern AI coding agents can be leveraged to design, generate, review, and strengthen automated test suites, including adversarial (Red Team) security testing.
src/
βββ main/java/com/devdiego/
β βββ Main.java # CLI Boundary β owns all presentation (System.out/err)
β βββ dataguard/
β βββ config/
β β βββ RuleProvider.java # SPI interface for rule catalogs (OCP)
β β βββ StandardPiiRuleProvider.java # 4 PII rules: EMAIL, CREDIT_CARD, IPV4, CELLPHONE
β β βββ DlpAuditRuleProvider.java # 4 DLP rules: AWS, GitHub, PrivateKey, JWT
β βββ core/
β β βββ DataGuardEngine.java # Pure engine: sanitizeStream / auditDLP / auditConcurrent
β β βββ license/
β β βββ LicenseValidator.java # SPI interface for license validation (OCP)
β β βββ CommunityLicenseValidator.java # Community Edition implementation (MIT)
β βββ model/
β βββ Rule.java # Immutable record (name, pattern, replacementTag)
β βββ DlpViolation.java # Immutable record (ruleName, file, lineNumber)
β βββ DlpAuditResult.java # Immutable record (leakDetected, violations)
βββ test/java/com/devdiego/dataguard/core/
βββ DataGuardEngineTest.java # 26 functional tests (JUnit 5)
βββ DlpBypassTest.java # 24 Red Team adversarial bypass tests
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Presentation Layer β
β Main.java (CLI) β owns System.out, System.err, System.exit β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Core / Domain Layer β
β DataGuardEngine β pure functions, returns domain records β
β RuleProvider (SPI) LicenseValidator (SPI) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Model / Domain Layer β
β Rule Β· DlpViolation Β· DlpAuditResult (immutable records) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Infrastructure / Config Layer β
β StandardPiiRuleProvider Β· DlpAuditRuleProvider β
β CommunityLicenseValidator β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Dependency Rule: Dependencies point only inward. The Core layer has zero references to System.err, System.out, or any presentation concern.
| Component | Responsibility |
|---|---|
Rule (record) |
Defines a compiled regex pattern, its name, and replacement tag. Immutable, thread-safe, validated on construction. |
RuleProvider (SPI) |
Interface for pluggable rule catalogs. Enforces Open/Closed Principle β new providers can be added without modifying the engine. |
StandardPiiRuleProvider |
Provider with 4 PII rules: EMAIL, CREDIT_CARD, IPV4, CELLPHONE. Used by the sanitization pipeline. |
DlpAuditRuleProvider |
Provider with 4 DLP rules: AWS_ACCESS_KEY, GITHUB_TOKEN, PRIVATE_KEY, JWT_TOKEN. Used by the DLP audit pipeline. |
DlpViolation (record) |
Immutable record capturing a single violation (ruleName, file, lineNumber). |
DlpAuditResult (record) |
Immutable aggregate result with leakDetected flag and violations list. Returned by the engine β no System.err side effects. |
LicenseValidator (SPI) |
Interface for pluggable license validation. Enforces OCP β swap implementations without modifying the CLI. |
CommunityLicenseValidator |
Community Edition (MIT) implementation. Always validates true. |
DataGuardEngine |
Pure core with three modes: streaming sanitization, DLP auditing with short-circuit, and concurrent auditing with Virtual Threads. Accepts RuleProvider SPI. Zero presentation side effects. |
Main |
CLI with subcommands: --dlp-audit <file> (Git hook) and --sanitize <src> <dst> (file cleanup). Gates execution behind LicenseValidator. Owns all presentation (System.out/System.err). |
Original line β [EMAIL] β [CREDIT_CARD] β [IPV4] β [CELLPHONE] β Clean line
Chained pipeline that recycles StringBuilder and Matcher. Each rule receives the output of the previous one. Inline counting without a second pass. Rules sourced from the injected RuleProvider SPI.
Line 1 β [AWS_ACCESS_KEY] β [GITHUB_TOKEN] β [PRIVATE_KEY] β [JWT_TOKEN] β clean
Line 2 β [AWS_ACCESS_KEY] β [GITHUB_TOKEN] β [PRIVATE_KEY] β [JWT_TOKEN] β clean
Line 3 β [AWS_ACCESS_KEY] β MATCH! β return DlpAuditResult (closes FD, reads no further)
On first match, immediately aborts without reading the rest of the file. Returns a DlpAuditResult with the first DlpViolation. Ideal for CI/CD where time is critical.
File 1 βββ [Virtual Thread 1] βββ auditDLP() βββ DlpAuditResult
File 2 βββ [Virtual Thread 2] βββ auditDLP() βββ DlpAuditResult
File 3 βββ [Virtual Thread 3] βββ auditDLP() βββ DlpAuditResult
...
File N βββ [Virtual Thread N] βββ auditDLP() βββ DlpAuditResult
β
Aggregated DlpAuditResult (all violations)
Uses Executors.newVirtualThreadPerTaskExecutor() from Project Loom. Each Virtual Thread weighs ~KB, enabling auditing of thousands of files simultaneously without saturating the OS. All DlpViolation objects from virtual threads are aggregated into a thread-safe Collections.synchronizedList and returned as a unified DlpAuditResult.
| Technique | NaΓ―ve approach | DataGuard Engine v1.2.0 |
|---|---|---|
| Read | Files.readString (all in RAM) |
BufferedReader (streaming O(1)) |
| Regex | String.replaceAll (millions of Strings) |
Matcher.appendReplacement (recycled buffers) |
| Memory | OutOfMemoryError with >1 GB |
~10β15 MB constant |
| Count | Second pass over the file | Inline counting in a single pass |
| DLP | Always reads entire file | Short-circuit: returns on first match |
| Concurrency | Platform Threads (~1 MB each) | Virtual Threads (~KB each, Project Loom) |
| Architecture | Core writes to System.err |
Pure Core β returns DlpAuditResult domain records |
| Extensibility | Static factory (RuleConfigurator) |
RuleProvider SPI (OCP-compliant) |
- Java 21 or higher
- Apache Maven 3.8 or higher
git clone https://github.com/your-user/java-anonimo-engine.git
cd java-anonimo-engine
mvn clean compilemvn clean test
# Tests run: 50, Failures: 0, Errors: 0, Skipped: 0
# BUILD SUCCESSjava -jar dataguard-engine.jar --sanitize logs.txt clean_logs.txtOutput:
License: Community Edition (MIT License - Free for all uses)
[DataGuard Engine] Sanitizing file stream...
Sanitization complete. Audit metrics:
-> EMAIL: 2 redactions
-> IPV4: 2 redactions
-> CREDIT_CARD: 1 redactions
-> CELLPHONE: 0 redactions
# Audit a file for secrets
java -jar dataguard-engine.jar --dlp-audit config.envIf a secret is found (exit code 1):
License: Community Edition (MIT License - Free for all uses)
π‘οΈ [DataGuard Engine v1.2.0] Starting DLP Audit on: config.env
π¨ [DLP VIOLATION] Rule: AWS_ACCESS_KEY | File: config.env | Line: 3
X / COMMIT REJECTED: Secrets or sensitive data detected in code.
If clean (exit code 0):
License: Community Edition (MIT License - Free for all uses)
π‘οΈ [DataGuard Engine v1.2.0] Starting DLP Audit on: clean_service.java
OK / DLP Audit Passed. Zero leaks detected.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Operation successful (audit passed / sanitize complete) |
| 1 | DLP violation detected or I/O error |
| 2 | License validation failed |
# .git/hooks/pre-commit
#!/bin/bash
for file in $(git diff --cached --name-only); do
java -jar dataguard.jar --dlp-audit "$file" || exit 1
done
β οΈ Security Note: Pre-commit hooks are advisory and can be bypassed withgit commit --no-verify. For production enforcement, add a server-sidepre-receivehook as a backstop. See the Red Team Analysis section for known bypass vectors.
| Rule | Detects | Tag |
|---|---|---|
EMAIL |
user@domain.com |
[EMAIL_REDACTED] |
CREDIT_CARD |
1234-5678-9012-3456 |
[CARD_REDACTED] |
IPV4 |
192.168.1.50 |
[IP_REDACTED] |
CELLPHONE |
+57 3001234567 |
[CELLPHONE_REDACTED] |
| Rule | Detects | Example |
|---|---|---|
AWS_ACCESS_KEY |
AKIA... (20 chars) |
AKIA_FAKE_FAKE_FAKE_ |
GITHUB_TOKEN |
ghp_... / github_pat_... |
ghp_abc123... |
PRIVATE_KEY |
-----BEGIN RSA PRIVATE KEY----- |
Private key headers |
JWT_TOKEN |
eyJ... (base64) |
eyJhbGciOiJIUzI1NiJ9.eyJzdWIi... |
Create your own RuleProvider implementation to add custom rule catalogs without modifying the engine (Open/Closed Principle):
public class MyCustomRuleProvider implements RuleProvider {
private static final List<Rule> RULES = List.of(
new Rule("DNI", Pattern.compile("\\b\\d{8}[A-Z]\\b"), "[DNI_REDACTED]"),
new Rule("API_KEY", Pattern.compile("sk-[a-zA-Z0-9]{32}"), "[API_KEY_BLOCKED]")
);
@Override public List<Rule> getRules() { return RULES; }
@Override public String getProviderName() { return "Custom Rule Provider"; }
}
// Sanitization
Map<String, Long> metrics = DataGuardEngine.sanitizeStream(source, target, new MyCustomRuleProvider());
// DLP Audit β returns DlpAuditResult (pure data, no System.err)
DlpAuditResult result = DataGuardEngine.auditDLP(file, new MyCustomRuleProvider());
if (result.leakDetected()) {
result.violations().forEach(v -> System.err.printf("π¨ %s | %s | line %d%n",
v.ruleName(), v.file().getFileName(), v.lineNumber()));
}
// Concurrent DLP (thousands of files)
DlpAuditResult result = DataGuardEngine.auditMultipleFilesConcurrently(fileList, new MyCustomRuleProvider());public class EnterpriseLicenseValidator implements LicenseValidator {
@Override
public boolean validate() throws SecurityException {
// Check license server, verify signature, etc.
return verifyEnterpriseLicense();
}
@Override
public String getLicenseType() {
return "Enterprise Edition (Commercial License)";
}
}
// In Main.java β swap one line:
LicenseValidator license = new EnterpriseLicenseValidator();Test suite using JUnit 5 and @TempDir:
| Test Group | Tests | What it validates |
|---|---|---|
| DLP Audit β Single File Detection | 6 | AWS key, GitHub token, private key, JWT, clean files, empty files |
| DLP Audit β Short-Circuit Optimization | 3 | Stop at line 1, stop at line 50, no false positives on near-miss patterns |
| DLP Audit β Concurrent (Virtual Threads) | 3 | Leak detection across files, all-clean, 100 files without crash |
| Sanitization β PII Redaction Pipeline | 5 | All PII types, accurate counts, clean line preservation, empty file, 10K-line file |
| Custom Rules & Edge Cases | 5 | Custom Stripe key, custom DNI, null/blank name rejection, null pattern rejection, default tag |
| Enterprise Scenario β CI/CD Simulation | 2 | Pre-commit hook (20 staged files), CI/CD log sanitization (500 lines) |
| License Validator SPI | 2 | Community license validates, non-blank license type |
Adversarial tests proving regex bypass vectors and pre-commit hook weaknesses:
| Test Group | Tests | What it validates |
|---|---|---|
| AWS Access Key β Bypass Vectors | 6 | Lowercase prefix, split lines, runtime assembly, base64, 15-char edge, zero-width space |
| GitHub Token β Bypass Vectors | 4 | 35-char body, hyphen in body, hex encoding, runtime assembly |
| JWT Token β Bypass Vectors | 7 | Space instead of dot, split lines, URL-encoded dots, zero-width space, hex, URL-encoded ey, control test |
| Pre-Commit Hook β Filesystem Bypass | 6 | --no-verify, symlink, word-splitting, binary null bytes, UTF-16, OOM long line |
| ReDoS β Catastrophic Backtracking | 1 | Alternating A. pattern Γ 10,000 |
mvn clean test
# Tests run: 50, Failures: 0, Errors: 0, Skipped: 0
# BUILD SUCCESSThe DlpBypassTest.java suite empirically demonstrates 24 bypass vectors against the current DLP regex patterns and pre-commit hook. Key findings:
| Pattern | Bypass Examples | Severity |
|---|---|---|
AKIA[0-9A-Z]{16} |
Lowercase akia, split across lines, base64-encoded, zero-width space |
π΄ Critical |
(ghp|github_pat)_[a-zA-Z0-9]{36,82} |
35-char body, hyphen in body, hex-encoded, runtime assembly | π΄ Critical |
ey[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+... |
URL-encoded dots, zero-width space in ey, hex-encoded, space instead of dot |
π΄ Critical |
| Vector | Mechanism | Severity |
|---|---|---|
git commit --no-verify |
Hooks are advisory, not enforced | π΄ Critical |
| Symlink to clean file | Engine follows symlink; real secret never staged | π΄ Critical |
| UTF-16 encoded file | BufferedReader (UTF-8) reads garbled chars | π΄ Critical |
| Binary file with null bytes | Null bytes break ASCII sequence | π΄ High |
- Server-side
pre-receivehook β backstop for--no-verifybypass - Multi-line sliding window β detect secrets split across lines
- Encoding detection β detect UTF-16/UTF-32 BOMs
- Case-insensitive regex β add
Pattern.CASE_INSENSITIVEfor AWS keys - Flexible quantifiers β
{16}β{15,20},{36,82}β{30,90} - Max line length cap β prevent OOM on minified files
- Symlink detection β flag staged symlinks
# STAGE 1: Build & Compile
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build
COPY pom.xml .
RUN apk add --no-cache maven && mvn dependency:go-offline -B
COPY src ./src
RUN mvn clean package -DskipTests
# STAGE 2: Production Runtime
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
RUN addgroup -S dataguard && adduser -S dataguard -G dataguard
USER dataguard
COPY --from=builder /build/target/java-anonimo-engine-1.0-SNAPSHOT.jar ./dataguard-engine.jar
VOLUME ["/data", "/etc/dataguard"]
ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar dataguard-engine.jar $0 $@"]
CMD ["--help"]# Build and run
docker build -t dataguard-engine .
docker run --rm -v $(pwd):/data dataguard-engine --dlp-audit /data/config.envMIT Β© 2026 β DataGuard Engine v1.2.0 Β· Clean Architecture Edition