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