DEV Community

Cover image for An AI-generated login endpoint "works" - I still found SQL concatenation before launch
yuan ming
yuan ming

Posted on

An AI-generated login endpoint "works" - I still found SQL concatenation before launch

Functional tests passing is not the same as code being safe to ship. Here is a reproducible case: an AI-generated login endpoint that behaves correctly, the scan finding I checked before launch, and the fix that made the pattern disappear.

The code runs, but I would not ship it like this

This is a local demo equivalent of a login endpoint, not live production code and not a client project:

username = request.form.get("username", "")
password = request.form.get("password", "")

sql = f"SELECT id FROM users WHERE username = '{username}' AND password = '{password}'"
cursor.execute(sql)
row = cursor.fetchone()

if row:
    return {"ok": True}
return {"ok": False}, 401
Enter fullscreen mode Exit fullscreen mode

It works: correct credentials return success. My concern is not whether it works today. It is that username and password come from an HTTP request and are placed directly inside an SQL string.

A first-pass scan finds the pattern to check

I did not read every line first. I ran a local quick scan:

code-audit app --format html --output report.html
Enter fullscreen mode Exit fullscreen mode

One result was:

Severity Rule Risk Location
High sql-concat SQL assembled from strings, user input can reach the query app/login.py:12

A high finding is not an automatic conclusion. I confirm it in four steps.

1. Where does the input come from?

username and password come from request.form. That means a client can submit arbitrary values. Input from HTTP requests is untrusted by default.

2. Where does it go?

The values skip length checks, type checks, escaping and parameterization, then enter the SQL template:

sql = f"SELECT id FROM users WHERE username = '{username}' AND password = '{password}'"
cursor.execute(sql)
Enter fullscreen mode Exit fullscreen mode

The source is a request parameter. The sink is SQL execution. There is no boundary between them.

3. Can it be exploited?

I do not attack my own project. I reason through SQL syntax:

If username contains:

' OR '1'='1
Enter fullscreen mode Exit fullscreen mode

the resulting SQL can become:

SELECT id FROM users WHERE username = '' OR '1'='1' AND password = '{password}'
Enter fullscreen mode Exit fullscreen mode

I cannot prove that every login is bypassable. I can confirm there is a suspicious path that does not require advanced exploitation. For a login endpoint, fixing it before launch is cheaper than investigating it after launch.

4. Use a parameterized query

The fix is not filtering single quotes. It is keeping user input out of the SQL string:

cursor.execute(
    "SELECT id, password_hash FROM users WHERE username = %s",
    (username,),
)
row = cursor.fetchone()

if row and verify_password(password, row["password_hash"]):
    return {"ok": True}
return {"ok": False}, 401
Enter fullscreen mode Exit fullscreen mode

This also changes the login flow to fetch the password hash by username first and verify the password separately, instead of putting the plaintext password into SQL.

After the fix, the sql-concat High finding no longer appears.

A clean scan is not "absolutely safe"

A clean scan only means this round did not find that rule pattern. It does not mean:

  • There are no other vulnerabilities.
  • The login flow meets every production security requirement.
  • You can skip rate limiting, lockout policy, audit logging and access checks.

Pre-launch checking is a process, not a verdict: scan for suspicious locations, confirm with a human, fix what can be fixed, and list what still needs review.

Evidence you can inspect

The rules, sample output and boundaries are public on GitHub: https://github.com/yuan1521913/code-audit-cli

The full source package is a separate licensed delivery. If you want a local scanner that produces Markdown, JSON, HTML or SARIF reports and can act as a CI gate, the English source package is here: https://5552463341538.gumroad.com/l/code-audit-cli-source

The scanner runs locally and does not upload your code. Automatic results still need human confirmation.

Top comments (1)

Collapse
 
raknaos profile image
Baptiste Le Bouquin

The SQL concatenation caught by the scanner but not by the green tests is exactly the gap I keep hitting: functional tests assert behaviour, not safety. I have had AI-generated endpoints pass every happy-path test while the actual SQL that gets built is interpolated straight into a cursor.execute. For me the fix that stuck was checking the generated SQL string itself in the tests — asserting the query is parameterised before it ever reaches the executor — rather than trusting that a passing request means the code is safe to ship. Nice write-up, the reproducible case makes it easy to copy the test approach.