RegexpGuardrail::processOutput() unconditionally returns a PassResult without ever running the configured regex pattern against the AI output text
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3580690. -->
Reported by: [ajv009](https://www.drupal.org/user/3653917)
Related to !1338
>>>
<p>[Tracker]<br>
<strong>Update Summary: </strong>RegexpGuardrail::processOutput() unconditionally returns PassResult, post-generate guardrails never scan AI output<br>
<strong>Short Description: </strong>RegexpGuardrail does not apply regex checks to AI output, making post-generate guardrails no-ops<br>
<strong>Check-in Date: </strong>03/22/2026<br>
<em>Metadata is used by the <a href="https://www.drupalstarforge.ai/" title="AI Tracker">AI Tracker.</a> Docs and additional fields <a href="https://www.drupalstarforge.ai/ai-dashboard/docs" title="AI Issue Tracker Documentation">here</a>.</em><br>
[/Tracker]</p>
<h3 id="summary-problem-motivation">Problem/Motivation</h3>
<p><code>RegexpGuardrail::processOutput()</code> unconditionally returns a <code>PassResult</code> without ever running the configured regex pattern against the AI output text:</p>
<pre>
public function processOutput(
OutputInterface $output,
): GuardrailResultInterface {
// This guardrail only processes input, not output.
return new PassResult('Output processing is not applicable for this guardrail.', $this);
}
</pre><p>This means any guardrail set that assigns a <code>regexp_guardrail</code> plugin to the <strong>post-generate</strong> phase will silently pass all AI output without scanning it.</p>
<p>The practical impact is significant: the <a href="https://www.drupal.org/project/ai_recipe_guardrails_pii">AI Guardrails PII recipe</a> (<a href="https://www.drupal.org/project/ai_initiative/issues/3577498">#3577498</a>) assigns all four PII guardrails (email, phone, credit card, IBAN) to both pre-generate and post-generate phases and claims bidirectional protection. The pre-generate side works correctly, but the post-generate side is completely non-functional because of this bug.</p>
<p>An LLM could return a response containing email addresses, credit card numbers, or other PII from RAG sources or training data, and the guardrail would not catch it.</p>
<p><code>RestrictToTopic</code> has the same no-op <code>processOutput()</code> pattern, but fixing that is more complex since it requires an LLM provider call. This issue focuses on <code>RegexpGuardrail</code> where the fix is straightforward.</p>
<h4 id="summary-steps-reproduce">Steps to reproduce</h4>
<ol>
<li>Install the AI module (1.3.x) and enable it</li>
<li>Create a guardrail of type "Regexp Guardrail" with pattern <code>/[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}/i</code> (email detection)</li>
<li>Create a guardrail set and assign this guardrail to <strong>both</strong> pre-generate and post-generate phases</li>
<li>Assign the guardrail set to a chat interaction (e.g. the chat generation explorer at <code>/admin/config/ai/explorers/chat_generator</code>)</li>
<li>Send a prompt that would cause the LLM to include an email address in its response (e.g. "What is the contact email for the Drupal security team?")</li>
<li><strong>Expected:</strong> The response should be blocked by the post-generate guardrail because it contains an email address</li>
<li><strong>Actual:</strong> The response passes through unfiltered. The post-generate guardrail never runs the regex check.</li>
</ol>
<p>AI modules enabled: ai (1.3.x), any AI provider module<br><br>
AI provider: Any (the bug is in the guardrail plugin, not the provider)</p>
<h3 id="summary-proposed-resolution">Proposed resolution</h3>
<p>Implement actual regex checking in <code>RegexpGuardrail::processOutput()</code>. The <code>ChatOutput::getNormalized()</code> method returns a <code>ChatMessage</code> (or <code>StreamedChatMessageIteratorInterface</code>), which provides <code>getText()</code> to extract the response text.</p>
<p>The implementation should mirror <code>processInput()</code> but operate on the output:</p>
<ol>
<li>Check if the output is a <code>ChatOutput</code> instance</li>
<li>Get the normalized output (<code>ChatMessage</code>)</li>
<li>Handle the case where the normalized output is a <code>StreamedChatMessageIteratorInterface</code> (streamed responses may need special handling or can pass through with a note)</li>
<li>Extract the text via <code>getText()</code></li>
<li>Run the configured regex pattern against it</li>
<li>Return <code>StopResult</code> on match, <code>PassResult</code> otherwise</li>
</ol>
<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<ul>
<li>Implement <code>processOutput()</code> in <code>RegexpGuardrail</code></li>
<li>Decide how to handle streamed responses (<code>StreamedChatMessageIteratorInterface</code>). Options: skip scanning with a log message, or buffer and scan.</li>
<li>Add tests covering post-generate guardrail scanning</li>
<li>Consider whether <code>RestrictToTopic::processOutput()</code> should also be addressed (separate issue)</li>
</ul>
<h3>Related issues</h3>
<ul>
<li><a href="https://www.drupal.org/project/ai_initiative/issues/3577498">#3577498</a>: [Meta] AI PII Guardrails Recipe - the recipe that exposed this bug</li>
<li><a href="https://www.drupal.org/project/ai/issues/3579088">#3579088</a>: Guardrail plugins only evaluate the last message - related guardrail processing issue</li>
<li><a href="https://www.drupal.org/project/ai/issues/3577790">#3577790</a>: Add validation to regex guardrail configuration - related regexp guardrail improvement</li>
<li><a href="https://www.drupal.org/project/ai/issues/3578846">#3578846</a>: AiGuardrailSet does not declare config dependencies - related guardrail infrastructure fix</li>
</ul>
<h3 id="summary-ai-usage">AI usage (if applicable)</h3>
<p>[x] AI Assisted Issue<br>
This issue was generated with AI assistance, but was reviewed and refined by the creator.</p>
<p>[ ] AI Assisted Code<br>
This code was mainly generated by a human, with AI autocompleting or parts AI generated, but under full human supervision.</p>
<p>[ ] AI Generated Code<br>
This code was mainly generated by an AI with human guidance, and reviewed, tested, and refined by a human.</p>
<p>[x] Vibe Coded<br>
This code was generated by an AI and has only been functionally tested.</p>
> Related issue: [Issue #3579088](https://www.drupal.org/node/3579088)
> Related issue: [Issue #3577498](https://www.drupal.org/node/3577498)
issue