Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7a92b42

Browse files
wendrulhcourdent
andauthored
Add docs for backend schema validation and sql interpolated args (windmill-labs#866)
* Add docs for backend schema validation and sql interpolated args * Typos and changelog --------- Co-authored-by: Henri Courdent <[email protected]> Co-authored-by: hcourdent <[email protected]>
1 parent 0adf444 commit 7a92b42

File tree

4 files changed

+87
-4
lines changed

4 files changed

+87
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
slug: backend-schema-validation
3+
version: v1.478.0
4+
title: Backend schema validation
5+
tags: ['Scripts', 'SQL', 'Security']
6+
description: Backend schema validation and safe interpolated arguments for SQL queries.
7+
features:
8+
[
9+
'Backend schema validation for scripts using the schema_validation annotation.',
10+
'Safe interpolated arguments for SQL queries using %%parameter%% syntax.',
11+
'Protection against SQL injections with strict validation rules for interpolated parameters.',
12+
]
13+
image: ./safe_interpolated_args.png
14+
docs: /docs/core_concepts/json_schema_and_parsing#backend-schema-validation
15+
---

docs/core_concepts/13_json_schema_and_parsing/index.mdx

+35-1
Original file line numberDiff line numberDiff line change
@@ -298,4 +298,38 @@ You can also do your own filtering and sorting of the options. In the example ab
298298
className="border-2 rounded-xl object-cover w-full h-full dark:border-gray-800"
299299
controls
300300
src="/videos/dynamic_select.mp4"
301-
/>
301+
/>
302+
303+
## Backend schema validation
304+
305+
By default, the schema is not explicitly checked by Windmill. For example, when triggering a script via [webhook](../4_webhooks/index.mdx), it is possible to pass an arbitrary JSON payload for the arguments, and Windmill [workers](../9_worker_groups/index.mdx) will just try to execute the script with it.
306+
307+
In some cases, you might want the job to fail if the payload does not follow the defined schema. For this, just add the `schema_validation` annotation as a comment to the top of your script. The logs should tell you if schema validation is taking place.
308+
309+
For example in [TypeScript](../../getting_started/0_scripts_quickstart/1_typescript_quickstart/index.mdx):
310+
311+
```ts
312+
// schema_validation
313+
314+
export async function main(
315+
a: number,
316+
b: "my" | "enum",
317+
e = "inferred type string from default arg",
318+
f = { nested: "object" },
319+
g: {
320+
label: "Variant 1",
321+
foo: string
322+
} | {
323+
label: "Variant 2",
324+
bar: number
325+
}
326+
) {
327+
return { foo: a };
328+
}
329+
```
330+
331+
Here, if we were to pass a string to `a` instead of a number, or pass `"something else"` to `b` instead of `"my"` or `"enum"`, or even if the shape of `g` does not correspond to one of the OneOf variants, the job will fail.
332+
333+
This was an example in TypeScript but backend schema validation is available on all [languages](../../getting_started/0_scripts_quickstart/index.mdx), in particular for [SQL safe interpolated arguments](../../getting_started/0_scripts_quickstart/5_sql_quickstart/index.mdx#safe-interpolated-arguments).
334+
335+
Note that this validation is not a fully JSON schema compliant. The checks you can expect are type and shape, required fields, strict enums. One thing that is not supported yet for instance is matching regex patterns on strings. When in doubt, it's best to test it out or provide your own checks.

docs/getting_started/0_scripts_quickstart/5_sql_quickstart/index.mdx

+37-3
Original file line numberDiff line numberDiff line change
@@ -523,9 +523,43 @@ You can then write your prepared statement.
523523

524524
## Raw queries
525525

526-
A more convenient but less secure option is to execute raw queries with a TypeScript, Deno or Python client. This enables you more flexibility than SQL prepared statement. You can for instance do string interpolation to make the name of the table a parameter of your script: `SELECT * FROM ${table}`. However this is dangerous since the string is directly interpolated and this open the door for [SQL injections](https://en.wikipedia.org/wiki/SQL_injection). Use with care and only in trusted environment.
526+
### Safe interpolated arguments
527527

528-
### PostgreSQL
528+
To allow more flexibility than with prepared statements, Windmill offers the possibility to do safe string interpolation in your queries thanks to [backend schema validation](../../../core_concepts/13_json_schema_and_parsing/index.mdx#backend-schema-validation). This allows you to use script parameters for elements you would usually not be able to, such as table or column names. In order to avoid SQL injections however, these parameters are checked at runtime and the job will fail if any of these rules is not followed:
529+
530+
- The parameter is a non-empty string.
531+
- The characters are all either alphabetical (ASCII only), numeric, or an underscore (`_`). Meaning no whitespace or symbol is allowed.
532+
- The string does not start with a number.
533+
- If the parameter is an enum, it must be one of the defined variants.
534+
535+
These rules are strict enough to protect from any kind of unexpected injection, but lenient enough to have some powerful use cases. Let's look at an example:
536+
537+
```sql
538+
-- :daily_minimum_calories (int)
539+
-- %%table_name%% fruits/vegetables/cereals
540+
541+
SELECT name, calories FROM %%table_name%% WHERE calories > daily_minimum_calories
542+
```
543+
544+
In this example the argument `table_name` is defined as a string that can be either `"fruits"`, `"vegetables"` or `"cereals"`, and the user of the script can then choose which table to query by setting this argument. If the user of the script tries to query a different table, the job will fail before making a connection to the DB, and thus protecting potentially sensitive data.
545+
546+
It the enum variants are ommited, the field is considered to be a regular string and only the other rules apply:
547+
548+
```sql
549+
-- :daily_minimum_calories (int)
550+
-- %%table_name%%
551+
552+
SELECT name, calories FROM %%table_name%% WHERE calories > daily_minimum_calories
553+
```
554+
555+
Keep in mind that this means users of this script can try this query against all existant and non-existant tables of the database.
556+
557+
558+
### Unsafe interpolation on a REST script
559+
560+
A more convenient but less secure option is to execute raw queries with a TypeScript, Deno or Python client. You can for instance do string interpolation to make the name of the table a parameter of your script: `SELECT * FROM ${table}`. However this is dangerous since the string is directly interpolated and this open the door for [SQL injections](https://en.wikipedia.org/wiki/SQL_injection). Use with care and only in trusted environment.
561+
562+
#### PostgreSQL
529563

530564
<Tabs className="unique-tabs">
531565

@@ -684,7 +718,7 @@ You can find more Script examples related to PostgreSQL on
684718

685719
:::
686720

687-
### MySQL
721+
#### MySQL
688722

689723
The same logic goes for MySQL.
690724

0 commit comments

Comments
 (0)