Preventing sneaky whitespace-only comments that AI let pass in Supabase

This week on stream, I paired with Cursor + Claude to add commenting functionality to Feedback Fort: an Outseta + React + Supabase Starter to create a feedback system or as a starting point for your own app/SaaS.

I teased the stream with "Will it #AI?" a nod to "Will it blend?" of yesteryear.

It felt like the answer was unequivocally yes - Claude seamlessly pumped out the Supabase migration SQL and working React code.

See me react live on stream here.

Or so I thought...

Testing after the stream revealed that sneaky whitespace-only comments could make it to the database. Turns out, AI is just as prone to the "client-side validation is enough" fallacy.

🤦‍♀️🤦‍♀️🤦‍♀️

The Claude-generated setup

Here's what our AI pair programmer came up with:

-- Create comments table
CREATE TABLE IF NOT EXISTS comments (
    uid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    content TEXT NOT NULL,
    feedback_uid UUID NOT NULL REFERENCES feedback(uid),
    outseta_person_uid VARCHAR NOT NULL DEFAULT auth.jwt() ->> 'sub',
    deleted_at TIMESTAMP WITH TIME ZONE
);

-- Create policy to allow users to create comments
CREATE POLICY "Users can create comments"
    ON comments FOR INSERT
    WITH CHECK (auth.jwt() ->> 'sub' = outseta_person_uid);
const handleSubmit = async (event) => {
  event.preventDefault();

  // Client-side validation courtesy of Claude
  if (!content.trim()) {
    setError("Comments cannot be empty. Please add some text.");
    return;
  }

  // Submit to Supabase
  const { error } = await supabase.from("comments").insert({
    feedback_uid: feedbackUid,
    content,
  });

  if (error) {
    setError(error.message);
    return;
  }
};

As you can see the AI made sure the content is NOT NULL and even added the .trim() check before submitting to Supabase.

The issue

After stream I took a closer look and realized whitespace-only comments could make it to the database if someone wanted to be sneaky doing something like:

  • Direct API calls to Supabase using something like Postman
  • Browser console manipulation to skip the validation

or in the future (if this was not merely a demo project) we could have:

  • Other front-ends that don't implement the same validation
  • A dev removing the validation for some reason
  • AI removing the validation while working on another feature

Therfore the general rule of the World Wide Web is to never-ever trust data coming from a client to be correct or valid!

The Solution

When using Supabase adding a similar "trim" check at the database level is a good place to put your server-side validation.

So I added this constraint to the table:

ALTER TABLE comments
ADD CONSTRAINT non_empty_content
CHECK (TRIM(BOTH FROM content) <> ''::text);

Trying to insert a whitespace-only comment after adding this constraint I got:

ERROR: new row for relation "comments" violates check constraint "non_empty_content"

Perfect! The database now rejects these problematic comments regardless of where they come from.

If you are working on a new project and want to add this constraint from the start, you may add it as part of your table creation:

-- Create comments table
CREATE TABLE IF NOT EXISTS comments (
    uid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    content TEXT NOT NULL CHECK (TRIM(BOTH FROM content) <> ''::text),
    feedback_uid UUID NOT NULL REFERENCES feedback(uid),
    outseta_person_uid VARCHAR NOT NULL DEFAULT auth.jwt() ->> 'sub',
    deleted_at TIMESTAMP WITH TIME ZONE
);

For other tech stacks it might make more sense to do this in an API layer.

Key Takeaways

  • AI generated code is trained on human code, so the same typical mistakes are made!
  • Implement validation at every layer, but always on the server side!!!
  • TRIM(BOTH FROM content) <> ''::text is your friend for text content validation

Let's see if we can get Claude to add the constraint from the start next time, Monday 26th May at 15:30 CEST.

Interested in more daily treasures like this one?
Sent directly to your inbox?