Code · v1
Rewrite a concatenated SQL query with placeholders
A node-postgres query built by string concatenation, with one string value and one numeric value. Both must become placeholders. Tests the everyday remediation, and specifically whether the numeric interpolation — the one that looks harmless — is caught alongside the obvious string one.
Results
Not yet run
No model has been run against this probe yet, so there are no results to show. The definition, rubric and scoring below are complete and final for v1 — this page is published now so that the test is on the record before any score exists, rather than appearing alongside one.
This page carries no Dataset structured data until it has real runs, for the same reason it shows no numbers.
Prompt
Exactly what every model receives. Nothing else is sent.
system
You are a senior backend engineer. When asked to rewrite code, reply with the code only — no explanation, no commentary before or after.
user
Rewrite this node-postgres call to use a parameterised query. Keep the same columns, the same table and the same conditions. Reply with the code only. const rows = await db.query( "SELECT id, email, role FROM users WHERE email = '" + email + "' AND tenant_id = " + tenantId );
Rubric
Published so you can disagree with it. A score you cannot argue with is a rumour.
Four observable properties, scored independently: both placeholders present, the values passed as a separate array, and no interpolation of either variable left in the query string. This checks the transformation that was asked for — it is not, and does not claim to be, a verification that the resulting query is secure.
- Uses $1 for the email value
- Uses $2 for the numeric tenant_id — the interpolation most often left in place
- Passes both values in a second argument array, in order
- No remaining concatenation or template interpolation of email or tenantId
- The query still selects the same columns from the same table
Scoring
Scored by regex rules. Deterministic — the same output always produces the same score.
Rules, in order
uses_first_placeholder
must match /\$1/
The email value must be bound rather than concatenated.
uses_second_placeholder
must match /\$2/
The numeric tenant_id needs a placeholder too. A number interpolated into SQL is still an injection point; it merely looks safer than a string.
passes_values_array
must match /\[\s*email\s*,\s*tenantId\s*\]/
Placeholders with no values array is not a working query — the rewrite has to be runnable, not merely differently shaped.
no_interpolation_remains
must NOT match /\+\s*(email|tenantId)\b|\$\{\s*(email|tenantId)\s*\}/
Adding a placeholder while leaving the other value concatenated produces code that passes a skim-read and is still injectable.
query_preserved
must match /select[\s\S]{0,80}from\s+users/i
The rewrite must still be the same query against the same table.Worked examples
Hand-written outputs the rubric is tested against on every build.
A rubric can fail in two directions that reading it will not reveal: it accepts everything, so every model scores 1 and the probe measures nothing; or it rejects everything, so every model looks bad at a task that is fine. These fixtures are run through the real scorer by npm run probes:check and by the test suite. The correct answer must score 1, and every wrong answer must not.
Correct — must score 1.00
const rows = await db.query( "SELECT id, email, role FROM users WHERE email = $1 AND tenant_id = $2", [email, tenantId] );
Rejected — parameterised the string, left the number interpolated — the subtle one
const rows = await db.query( "SELECT id, email, role FROM users WHERE email = $1 AND tenant_id = " + tenantId, [email] );
Rejected — swapped concatenation for a template literal, fixing nothing
const rows = await db.query(
`SELECT id, email, role FROM users WHERE email = '${email}' AND tenant_id = ${tenantId}`
);Rejected — placeholders present but no values passed
const rows = await db.query( "SELECT id, email, role FROM users WHERE email = $1 AND tenant_id = $2" );
Rejected — used MySQL-style placeholders against node-postgres
const rows = await db.query( "SELECT id, email, role FROM users WHERE email = ? AND tenant_id = ?", [email, tenantId] );
Rejected — escaped the input instead of binding it
const safeEmail = email.replace(/'/g, "''"); const rows = await db.query( "SELECT id, email, role FROM users WHERE email = '" + safeEmail + "' AND tenant_id = " + tenantId );
Version history
A probe version is immutable. Changing a prompt or a rubric creates the next version; existing runs stay attached to the one that produced them.
- v1 · current
- Initial version.
Parameters
- maxTokens
- 1400
- temperature
- 0
Run it yourself
The probe exactly as it stands at v1. Swap the model slug for any model you want to compare — the API key is a shell variable, never a value.
curl https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-opus-5",
"messages": [
{
"role": "system",
"content": "You are a senior backend engineer. When asked to rewrite code, reply with the code only — no explanation, no commentary before or after."
},
{
"role": "user",
"content": "Rewrite this node-postgres call to use a parameterised query. Keep the same columns,\nthe same table and the same conditions. Reply with the code only.\n\nconst rows = await db.query(\n \"SELECT id, email, role FROM users WHERE email = '\''\" + email + \"'\'' AND tenant_id = \" + tenantId\n);"
}
],
"max_tokens": 1400,
"temperature": 0
}'