DEV Community

Cover image for I Found a Silent Bug in Formbricks That Crashes Live Surveys at Runtime
Laurina Ayarah
Laurina Ayarah Subscriber

Posted on

I Found a Silent Bug in Formbricks That Crashes Live Surveys at Runtime

Summer Bug Smash: Clear the Lineup ๐Ÿ›๐Ÿ›น

This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry.

Project Overview

Formbricks is an open-source survey and experience management platform built with Next.js, TypeScript, React, and Tailwind CSS. It lets teams create and deploy surveys across websites, apps, and email. Developers can self-host it or use the cloud version. With over 12,000 GitHub stars and hundreds of contributors, it is one of the most actively maintained open source alternatives to Qualtrics.

Bug Fix or Performance Improvement

Formbricks supports custom regex validation rules on survey questions. A survey creator can set a pattern that user responses must match before they are accepted. The problem was in how that pattern was stored.

The validation schema for regex pattern rules only checked that the input was a non-empty string:

// Before the fix
export const ZValidationRuleParamsPattern = z.object({
  pattern: z.string().min(1),
  flags: z.string().optional(),
});
Enter fullscreen mode Exit fullscreen mode

z.string().min(1) means "give me any string with at least one character." It does not verify that the string is actually a valid regular expression. So a survey creator could type [invalid as their pattern, the schema would accept it, it would be saved to the database, and then when a real user submitted a response, the system would try to run new RegExp("[invalid"), JavaScript would throw a SyntaxError, and the survey would crash silently at runtime.

The bug never surfaced during setup. It only appeared when a real user was trying to submit a real response.

Code

Pull Request: github.com/Tobore005/formbricks/pull/1

Pull request showing the fix on Tobore005 formbricks fork

Here is the fix:

const isValidRegexPattern = (pattern: string): boolean => {
  try {
    new RegExp(pattern);
    return true;
  } catch {
    return false;
  }
};

const isValidRegexFlags = (flags: string | undefined): boolean => {
  if (flags === undefined) return true;
  try {
    new RegExp("", flags);
    return true;
  } catch {
    return false;
  }
};

export const ZValidationRuleParamsPattern = z.object({
  pattern: z
    .string()
    .min(1)
    .refine(isValidRegexPattern, {
      message: "Invalid regular expression pattern",
    }),
  flags: z
    .string()
    .optional()
    .refine(isValidRegexFlags, {
      message: "Invalid regular expression flags",
    }),
});
Enter fullscreen mode Exit fullscreen mode

Two helper functions wrap RegExp construction in a try/catch. If JavaScript throws on an invalid pattern, the function returns false and the schema rejects the input with a clear error message before anything reaches the database.

My Improvements

The key decision was where to catch this error. I could have added the validation at the UI layer, showing an error in the survey editor when someone types a bad pattern. But UI validation can be bypassed. The right place to catch it is at the schema level, so even if someone sends a malformed pattern directly through the API, it gets rejected before touching the database.

I also validated both the pattern and the flags separately. An invalid flag like z is just as capable of throwing a SyntaxError as an invalid pattern, and the original code left flags unvalidated too. Both fields now go through runtime validation before the schema accepts them.

The .refine() approach keeps the fix minimal and readable. The named helper functions mean anyone reading the schema immediately understands what the validation is doing without needing to read a try/catch block inline.

Best Use of Sentry

To prove the bug was real and show exactly what would happen in production, I set up Sentry error monitoring and reproduced the crash in a Node.js environment.

I wrote a script that simulated what Formbricks was doing before the fix: accepting an invalid regex pattern string, then trying to compile it into a real RegExp object at runtime. Sentry captured the full crash with a complete stack trace pointing directly to the line where new RegExp(pattern) failed.

Sentry showed:

  • Error: SyntaxError: Invalid regular expression: /[invalid/: Unterminated character class
  • Exact file and line: index.js:13:17 in validateWithPattern
  • Full breadcrumb trail showing the sequence of events leading to the crash
  • Environment details including Node version, OS, and production context

The stack trace confirmed that without the fix, this error would reach real users silently. With the fix in place, the invalid pattern is rejected at the schema level and never gets the chance to crash at runtime.

Sentry dashboard showing SyntaxError from invalid regex pattern

Sentry stack trace showing crash at line 13 in validateWithPattern

Best Use of Google AI

I used Gemini to help me understand the full scope of the bug and verify that my fix was sound before submitting the PR.

I described the bug to Gemini and asked it to explain why unvalidated regex strings are dangerous and how a try/catch around RegExp construction fixes it. Gemini confirmed the danger, walked through the exact failure scenario, and explained the three stages of the fix: the attempt, the safety net, and graceful failure.

Gemini also asked whether I was planning to open a PR, which I confirmed I had already done. That conversation gave me confidence that the fix was complete and covered both the pattern and flags fields.

Gemini explaining why unvalidated regex strings are dangerous

Gemini showing the three stages of the try/catch fix with code

Top comments (1)

Collapse
 
biomathcode profile image
Pratik sharma

nice work