You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Dev Monitor's AI context provider (AI_Context_Provider interface) is built around one rule: only metadata leaves the developer's machine. No SQL, no error strings, no user input, no post content. Even a single accidental leak from one collector would break the privacy guarantee.
AI_Data_Sanitizer is a stateless defence-in-depth pass that runs over every payload before it is handed to any AI client. Each collector is also expected to filter at source, but the sanitizer is the safety net.
Expected Outcome
A single stateless helper class plus a focused unit test that proves it strips the forbidden patterns.
namespaceRtCamp\WPToolkit\Dev\AI;
finalclass AI_Data_Sanitizer {
privatefunction__construct() {} // stateless, do not instantiate/** * Sanitize a structured payload before it leaves the dev machine. * Strips SQL, error messages, user-identifiable data, and free-form strings * over a length threshold. Recursive on nested arrays. * * @param array $payload Raw collector context. * @return array Sanitized copy. Forbidden keys are removed. Suspicious string * values are replaced with the token "[redacted:<reason>]". */publicstaticfunctionsanitize( array$payload ): array;
/** * Returns true if the given string looks like SQL, an exception message, * a user email, an IP, a session token, or any other forbidden pattern. */publicstaticfunctionlooks_sensitive( string$value ): bool;
}
Forbidden patterns (stripped or redacted):
Pattern
Detection rule
Replacement
SQL
matches `/\b(SELECT
INSERT
Exception trace
contains #0 followed by file path
[redacted:trace]
Email
matches /[\w.+-]+@[\w-]+\.[\w.-]+/
[redacted:email]
IP address
matches /\b\d{1,3}(\.\d{1,3}){3}\b/
[redacted:ip]
Long free text
string length > 500 characters
[redacted:long_text]
Forbidden keys
any key in [ 'password', 'secret', 'token', 'api_key', 'cookie', 'auth' ] (case-insensitive)
Self-contained brief. This is a pure-function helper. No state, no Singleton, no instances.
Step-by-step
Create src/Dev/AI/AI_Data_Sanitizer.php. Mark the class final, give it private function __construct() {} so it cannot be instantiated.
Implement looks_sensitive() first with the six pattern checks. Use preg_match() for SQL, exception trace, email, IP. Use strlen() for the long-text check.
Implement sanitize() recursively. Walk the array; for each value:
If the key matches a forbidden key (case-insensitive), drop the key entirely.
If the value is an array, recurse.
If the value is a string, run looks_sensitive() and replace with the redaction token.
Otherwise pass through unchanged (int / bool / float / null are safe).
Use a private helper for the redaction token so the format is consistent: redact( string $reason ): string returns "[redacted:{$reason}]".
Write the 12 tests: one per forbidden pattern (SQL, trace, email, IP, long text, each forbidden key) + nested-array test + clean-pass-through test.
Order of pattern checks matters. Check exception traces first (they often contain everything else).
is_array() does not catch ArrayObject. Document in the @param array $payload that only plain arrays are supported. Throw InvalidArgumentException if an ArrayObject is passed at the top level (test this).
Do not log the sanitised vs original. Logging defeats the purpose.
Do not call any WordPress functions. This class must be testable without WP loaded.
Pre-PR self-check
Class is final with private __construct()
No instance state — all methods static
Recursion works on nested arrays (verified by test)
All 6 forbidden patterns enforced (verified by 6 tests)
Forbidden keys are dropped (case-insensitive, verified by test)
No WordPress dependency (verified by running test in isolation)
PHPCS, PHPStan, PHPUnit all green
CHANGELOG entry under ## Unreleased
Acceptance Criteria
AI_Data_Sanitizer is final with private __construct() (stateless, cannot be instantiated)
Both methods are public static with full type declarations
sanitize() recurses into nested arrays
All 6 forbidden-pattern rules from the table are enforced
At least 12 unit tests: one per forbidden pattern, plus nested-array, plus pass-through-of-clean-metadata
This is a stateless helper, not a Singleton or multi-instance class — pure functions only.
The sanitizer is defence in depth, not the primary filter. Each collector's ai_context() is expected to return only metadata in the first place.
A ai_context() REST endpoint in the skeleton is gated separately (manage_options + nonce + DEV_MODE + rate limit). Sanitizer applies regardless of caller.
Console-log collector data is memory-only and never reaches the server, so it never reaches this sanitizer either.
Context
The Dev Monitor's AI context provider (
AI_Context_Providerinterface) is built around one rule: only metadata leaves the developer's machine. No SQL, no error strings, no user input, no post content. Even a single accidental leak from one collector would break the privacy guarantee.AI_Data_Sanitizeris a stateless defence-in-depth pass that runs over every payload before it is handed to any AI client. Each collector is also expected to filter at source, but the sanitizer is the safety net.Expected Outcome
A single stateless helper class plus a focused unit test that proves it strips the forbidden patterns.
Files added:
Class shape:
Forbidden patterns (stripped or redacted):
#0followed by file path[redacted:trace]/[\w.+-]+@[\w-]+\.[\w.-]+/[redacted:email]/\b\d{1,3}(\.\d{1,3}){3}\b/[redacted:ip][redacted:long_text][ 'password', 'secret', 'token', 'api_key', 'cookie', 'auth' ](case-insensitive)Verification commands (all must exit 0):
composer install composer phpcs src/Dev/AI/ tests/Dev/AI/ composer phpstan composer test -- tests/Dev/AI/AI_Data_SanitizerTest.phpImplementation guidance
Step-by-step
src/Dev/AI/AI_Data_Sanitizer.php. Mark the classfinal, give itprivate function __construct() {}so it cannot be instantiated.looks_sensitive()first with the six pattern checks. Usepreg_match()for SQL, exception trace, email, IP. Usestrlen()for the long-text check.sanitize()recursively. Walk the array; for each value:looks_sensitive()and replace with the redaction token.redact( string $reason ): stringreturns"[redacted:{$reason}]".Reference patterns
Complete sample: full sanitize_value() with all six patterns
Sample input → sanitized output
Edge cases
is_array()does not catchArrayObject. Document in the@param array $payloadthat only plain arrays are supported. ThrowInvalidArgumentExceptionif anArrayObjectis passed at the top level (test this).Pre-PR self-check
finalwithprivate __construct()## UnreleasedAcceptance Criteria
AI_Data_Sanitizerisfinalwithprivate __construct()(stateless, cannot be instantiated)public staticwith full type declarationssanitize()recurses into nested arraysCHANGELOG.mdentry added under## UnreleasedNotes
phpcs.xml.dist) #15) and PHPStan baseline (Add shared PHPStan baseline (phpstan.neon.dist) #16) merged.ai_context()is expected to return only metadata in the first place.ai_context()REST endpoint in the skeleton is gated separately (manage_options + nonce + DEV_MODE + rate limit). Sanitizer applies regardless of caller.release/v1.0.0. Branch:v1.0.0/task/ai-data-sanitizer. Commit subject:feat(dev): add AI_Data_Sanitizer helper.