Skip to content

Fix ReDoS in PowerShell prompt detection#279853

Merged
meganrogge merged 2 commits intomicrosoft:mainfrom
rducom:patch-1
Dec 11, 2025
Merged

Fix ReDoS in PowerShell prompt detection#279853
meganrogge merged 2 commits intomicrosoft:mainfrom
rducom:patch-1

Conversation

@rducom
Copy link
Contributor

@rducom rducom commented Nov 27, 2025

Fixes #279842

Description

This PR fixes a ReDoS (Regular Expression Denial of Service) vulnerability in the PowerShell prompt detection regex located in outputMonitor.ts.

The Issue:
The original regex \s*(?:\[[^\]]\]\s+[^\[]+\s*)+... suffers from catastrophic backtracking due to ambiguity between the separator \s+ and the content matcher [^\[]+.
Since [^\[]+ matches any character that isn't [ (including whitespace), a long sequence of indented lines (common in structured logs like .NET) causes the engine to explore exponential combinations of how to split the whitespace between the separator and the content.

The Fix:
I have optimized the regex to strictly enforce the boundary between the separator and the content:

  • Old: [^\[]+ (Allowed content to start with whitespace, creating overlap with the preceding \s+)
  • New: [^\[\s][^\[]*

This change enforces that the "content" part of the prompt pattern must start with a non-whitespace character. This eliminates the ambiguity: any whitespace following the [...] block is now greedily consumed by the explicit \s+ separator and cannot be "claimed" by the content matcher.

Performance Verification:
Tested against a 16KB log file containing indented .NET logs that previously caused a terminal freeze:

  • Old Regex: Timeout (>5s) / Crash
  • New Regex: ~4ms

The complexity is reduced from exponential to linear O(n).

Copilot AI review requested due to automatic review settings November 27, 2025 23:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a ReDoS (Regular Expression Denial of Service) vulnerability in the PowerShell prompt detection regex within the detectsInputRequiredPattern function. The vulnerability could allow an attacker to cause exponential backtracking by providing specially crafted input strings with many consecutive spaces.

Key Changes

  • Modified the regex pattern in outputMonitor.ts to eliminate ambiguity in whitespace matching
  • Changed [^\[]+ to (?:[^\[\s]|\s+(?!\[))+ to ensure deterministic consumption of whitespace
  • The fix prevents the regex engine from exploring exponential combinations when backtracking

@rducom
Copy link
Contributor Author

rducom commented Nov 29, 2025

Hello @Tyriar, please find the results of the fix :

node reproduce_issue_embedded.js
Total Log Content Length: 15909 characters
Chars   | Old (ms)      | Proposed (ms) | Match Equal
----------------------------------------------------------------
500     | 1.7053        | 0.0683                | YES
600     | 2.9498        | 0.0822                | YES
700     | 10.7116       | 0.1032                | YES
800     | 17.9709       | 0.0973                | YES
1100    | 28.3592       | 0.0710                | YES
1200    | 85.5718       | 0.0702                | YES
1300    | 153.2329      | 0.0690                | YES
1400    | 471.8805      | 0.0858                | YES
1500    | 938.9027      | 0.0793                | YES
1600    | 1341.8973     | 0.1117                | YES
1650    | 2855.1964     | 0.0872                | YES
1700    | 4281.7549     | 0.0855                | YES
1800    | TIMEOUT       | 0.0932                | NO
1900    | TIMEOUT       | 0.0963                | NO
2000    | TIMEOUT       | 0.0977                | NO
3000    | TIMEOUT       | 0.1550                | NO
4000    | TIMEOUT       | 0.2316                | NO
5000    | TIMEOUT       | 0.3792                | NO
10000   | TIMEOUT       | 1.4454                | NO
15000   | TIMEOUT       | 3.3913                | NO
15909   | TIMEOUT       | 3.7709                | NO

And the code to reproduce the issue bellow :

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

if (isMainThread) {
    // Console log output involved in the issue
    const logContent = `info: TransformationPipeline.Services.PipelineInitializer[0]
      Initializing all pipelines...
info: TransformationPipeline.Services.TransformationService[0]
      Loading pipeline configuration (default: /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config, override: /app/config-override)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loading default configuration from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loading pipeline configuration from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/pipelines.yaml
info: TransformationPipeline.Services.TracePipeline[0]
      Loading trace pipeline configuration (default: /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/traces, override: /app/config-override/traces)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loading pipeline configuration from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/traces/pipelines.yaml
info: TransformationPipeline.Services.MetricPipeline[0]
      Loading metric pipeline configuration (default: /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/metrics, override: /app/config-override/metrics)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loading pipeline configuration from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/metrics/pipelines.yaml
info: TransformationPipeline.Services.ProfilePipeline[0]
      Loading profile pipeline configuration (default: /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/profiles, override: /app/config-override/profiles)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loading pipeline configuration from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/profiles/pipelines.yaml
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loaded 1 pipeline(s) from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/metrics/pipelines.yaml
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Resolving imports for 1 pipeline(s)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loaded 11 pipeline(s) from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/profiles/pipelines.yaml
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Resolving imports for 11 pipeline(s)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loaded 14 pipeline(s) from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/pipelines.yaml
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Resolving imports for 14 pipeline(s)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Loaded 8 pipeline(s) from /Users/rducom/sources/poc-pipeline/transformation-pipeline/tools/PipelineTester/bin/Debug/net10.0/config/traces/pipelines.yaml
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      Resolving imports for 8 pipeline(s)
info: TransformationPipeline.Services.MetricPipeline[0]
      Successfully loaded 1 metric pipeline(s)
info: TransformationPipeline.Services.MetricPipeline[0]
      Metric pipeline 'metrics-passthrough' initialized with 0 processor(s), filter: '*'
info: TransformationPipeline.Services.MetricPipeline[0]
      MetricPipeline initialized with 1 active pipeline(s)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/http-server-spans.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'http-server-traces' resolved with 0 processor(s) (0 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/http-client-spans.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'http-client-traces' resolved with 0 processor(s) (0 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/database-spans.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'database-traces' resolved with 0 processor(s) (0 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/cpu_profiles.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'cpu-profiles' resolved with 0 processor(s) (0 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/kubernetes-spans.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'kubernetes-traces' resolved with 0 processor(s) (0 imported + 0 own)
info: TransformationPipeline.Services.TracePipeline[0]
      Successfully loaded 8 trace pipeline(s)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/memory_profiles.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'memory-profiles' resolved with 0 processor(s) (0 imported + 0 own)
info: TransformationPipeline.Services.TracePipeline[0]
      Trace pipeline 'global-trace-normalizer' initialized with 1 processor(s), filter: '*'
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/goroutine_profiles.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'goroutine-profiles' resolved with 0 processor(s) (0 imported + 0 own)
info: TransformationPipeline.Services.TracePipeline[0]
      Trace pipeline 'filter-health-checks' initialized with 1 processor(s), filter: 'span.name:/health|/ping|/ready|/live'
info: TransformationPipeline.Services.TracePipeline[0]
      Trace pipeline 'http-server-traces' initialized with 0 processor(s), filter: 'span.kind:Server'
info: TransformationPipeline.Services.TracePipeline[0]
      Trace pipeline 'http-client-traces' initialized with 0 processor(s), filter: 'span.kind:Client'
info: TransformationPipeline.Services.TracePipeline[0]
      Trace pipeline 'database-traces' initialized with 0 processor(s), filter: 'span_attributes.db.system:*'
info: TransformationPipeline.Services.TracePipeline[0]
      Trace pipeline 'kubernetes-traces' initialized with 0 processor(s), filter: 'resource_attributes.k8s.pod.name:*'
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'kubernetes-audit-base' resolved with 6 processor(s) (1 imported + 5 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'kubernetes-audit' resolved with 6 processor(s) (6 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/block_profiles.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'block-profiles' resolved with 0 processor(s) (0 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/mutex_profiles.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'mutex-profiles' resolved with 0 processor(s) (0 imported + 0 own)
info: TransformationPipeline.Services.TracePipeline[0]
      Trace pipeline 'error-traces' initialized with 2 processor(s), filter: 'span.status_code:Error'
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'kubernetes-events-base' resolved with 3 processor(s) (1 imported + 2 own)
info: TransformationPipeline.Services.TracePipeline[0]
      TracePipeline initialized with 7 pipeline(s)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'kubernetes-events' resolved with 3 processor(s) (3 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/kubernetes_profiles.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'kubernetes-profiles' resolved with 0 processor(s) (0 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'talos-os' resolved with 1 processor(s) (1 imported + 0 own)
warn: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Import file sources/application_profiles.yaml contains no pipelines
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'application-profiles' resolved with 0 processor(s) (0 imported + 0 own)
info: TransformationPipeline.Services.ProfilePipeline[0]
      Successfully loaded 11 profile pipeline(s)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'otel-collector-internal' resolved with 1 processor(s) (1 imported + 0 own)
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'filter-otel-internal-profiles' initialized with 1 processor(s), filter: 'profile.type == "cpu" and resource.service.name =~ "otel-collector|opentelemetry"'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'filter-profiler-overhead' initialized with 1 processor(s), filter: 'profile.type == "cpu" and (function_name =~ "pprof|profiler|agent" or file_name =~ "profiling|pprof")'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'cpu-profiles' initialized with 0 processor(s), filter: 'profile.type == "cpu"'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'memory-profiles' initialized with 0 processor(s), filter: 'profile.type == "memory" or sample.type =~ "alloc|inuse"'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'goroutine-profiles' initialized with 0 processor(s), filter: 'profile.type == "goroutine" or sample.type == "goroutines"'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'block-profiles' initialized with 0 processor(s), filter: 'profile.type == "block" or sample.type =~ "contentions|delay"'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'mutex-profiles' initialized with 0 processor(s), filter: 'profile.type == "mutex" or sample.type =~ "mutex|lock"'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'kubernetes-profiles' initialized with 0 processor(s), filter: 'resource.k8s.namespace.name != null'
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'argocd' resolved with 1 processor(s) (1 imported + 0 own)
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'high-sample-count' initialized with 2 processor(s), filter: 'sample_count > 10000'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'deep-stacktraces' initialized with 2 processor(s), filter: 'max_stack_depth > 100'
info: TransformationPipeline.Services.ProfilePipeline[0]
      Profile pipeline 'application-profiles' initialized with 0 processor(s), filter: 'signal.type == "profile"'
info: TransformationPipeline.Services.ProfilePipeline[0]
      ProfilePipeline initialized with 11 active pipeline(s)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'strimzi-operator' resolved with 3 processor(s) (3 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'kafka-broker' resolved with 2 processor(s) (2 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'cilium' resolved with 1 processor(s) (1 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'otel-http' resolved with 9 processor(s) (9 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'otel-messaging' resolved with 8 processor(s) (8 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'otel-exceptions' resolved with 5 processor(s) (5 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'otel-generic' resolved with 3 processor(s) (3 imported + 0 own)
info: TransformationPipeline.Configuration.PipelineImportResolver[0]
      Pipeline 'unknown-fallback' resolved with 1 processor(s) (1 imported + 0 own)
info: TransformationPipeline.Configuration.ConfigurationLoader[0]
      No override configuration found. Using default configuration only.
info: TransformationPipeline.Services.TransformationService[0]
      Successfully loaded 14 pipeline(s)
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'global-casing-normalizer' initialized with 1 processor(s), filter: '*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'kubernetes-audit' initialized with 6 processor(s), filter: 'apiVersion:audit.k8s.io*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'kubernetes-events' initialized with 3 processor(s), filter: 'event.domain:*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'talos-os' initialized with 1 processor(s), filter: 'talos.service:*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'otel-collector-internal' initialized with 1 processor(s), filter: 'otelcol.component.id:*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'argocd' initialized with 1 processor(s), filter: 'resource.container.image.name:*argoproj/argocd*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'strimzi-operator' initialized with 3 processor(s), filter: 'resource.container.image.name:*strimzi/operator*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'kafka-broker' initialized with 2 processor(s), filter: 'resource.container.image.name:*strimzi/kafka*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'cilium' initialized with 1 processor(s), filter: 'ciliumEndpointName:*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'otel-http' initialized with 9 processor(s), filter: 'Attributes.http.method:*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'otel-messaging' initialized with 8 processor(s), filter: 'Attributes.messaging.system:*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'otel-exceptions' initialized with 5 processor(s), filter: 'Attributes.exception.type:*'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'otel-generic' initialized with 3 processor(s), filter: 'none'
info: TransformationPipeline.Services.TransformationService[0]
      Pipeline 'unknown-fallback' initialized with 1 processor(s), filter: 'none'
info: TransformationPipeline.Services.TransformationService[0]
      TransformationService initialized successfully with 14 pipelines
info: TransformationPipeline.Services.PipelineInitializer[0]
      All pipelines initialized successfully.`;

    const chunkSizes = [500, 600, 700, 800, 1100, 1200, 1300, 1400, 1500, 1600, 1650, 1700, 1800, 1900, 2000, 5000, 10000, 15000, logContent.length];

    console.log(`Total Log Content Length: ${logContent.length} characters`);
    console.log('Chars\t| Old (ms)\t| Proposed (ms)\t| Match Equal');
    console.log('----------------------------------------------------------------');

    (async () => {
        for (const size of chunkSizes) {
            if (size > logContent.length && size !== logContent.length) continue;
            
            const chunk = logContent.substring(0, size) + "X";
            
            const resultOld = await runRegexInWorker('old', chunk);
            const resultProp = await runRegexInWorker('proposed', chunk);
            
            const timeOld = resultOld.time;
            const timeProp = resultProp.time;
            
            // Compare matches (boolean result of .test())
            // Note: Since we append 'X' to force failure/backtracking, both should return false.
            // But let's verify they behave identically.
            const equal = (resultOld.match === resultProp.match) ? "YES" : "NO";
            
            console.log(`${size}\t| ${timeOld}\t| ${timeProp}\t\t| ${equal}`);
        }
    })();

    function runRegexInWorker(regexName, input) {
        return new Promise((resolve) => {
            const worker = new Worker(__filename, {
                workerData: { regexName, input }
            });

            const timeout = setTimeout(() => {
                worker.terminate();
                resolve({ time: 'TIMEOUT', match: 'N/A' });
            }, 5000); // 5 seconds timeout

            worker.on('message', (msg) => {
                clearTimeout(timeout);
                resolve(msg);
            });

            worker.on('error', (err) => {
                clearTimeout(timeout);
                resolve({ time: 'ERROR', match: 'N/A' });
            });
            
            worker.on('exit', (code) => {
                if (code !== 0) {
                     clearTimeout(timeout);
                }
            });
        });
    }

} else {
    const { performance } = require('perf_hooks');
    const { regexName, input } = workerData;

    const regexes = {
        old: /\s*(?:\[[^\]]\]\s+[^\[\]]+\s*)+(?:\(default is\s+"[^"]+"\):)?\s+$/,
        proposed: /\s*(?:\[[^\]]\]\s+[^\[\s][^\[]*\s*)+(?:\(default is\s+"[^"]+"\):)?\s+$/
    };

    const regex = regexes[regexName];
    
    try {
        const start = performance.now();
        const match = regex.test(input);
        const end = performance.now();
        parentPort.postMessage({ 
            time: (end - start).toFixed(4),
            match: match
        });
    } catch (e) {
        // Should not happen for regex test
    }
}

Copy link
Contributor

@meganrogge meganrogge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@meganrogge meganrogge enabled auto-merge (squash) December 11, 2025 18:09
Terminal: Fix ReDoS in PowerShell prompt detection

Refactors the PowerShell confirmation regex to prevent catastrophic backtracking (ReDoS).

The previous pattern `[^\[]+` implicitly matched whitespace, creating an overlap with the outer loop's `\s+`. This caused exponential complexity when processing strings with many spaces not followed by a valid bracket.

The new pattern `(?:[^\[\s]|\s+(?!\[))+` enforces mutual exclusion between content and separators, ensuring linear performance.

Fixes microsoft#279842
initial proposed fix is false, here's the fixed fix
auto-merge was automatically disabled December 11, 2025 18:36

Head branch was pushed to by a user without write access

@rducom
Copy link
Contributor Author

rducom commented Dec 11, 2025

i've rebased the branch since the CI was failing (https://github.com/microsoft/vscode/actions/runs/19782951892/job/57814695787)

@meganrogge meganrogge enabled auto-merge (squash) December 11, 2025 18:38
@meganrogge meganrogge merged commit 87e50c6 into microsoft:main Dec 11, 2025
17 checks passed
@vs-code-engineering vs-code-engineering bot locked and limited conversation to collaborators Jan 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Terminal tool freezes workbench when evaluating detectsInputRequiredPattern

3 participants