mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 08:05:44 +00:00
## Summary
A job whose result contains a real NUL (U+0000) serializes to a `\u0000` JSON escape that the `jsonb`-typed `v2_job_completed.result` column rejects with Postgres `22P05` ("unsupported Unicode escape sequence"). This aborts the `INSERT` in `commit_completed_job`, which then retries 10 times and leaves the job unable to complete (surfaced as `Could not add completed job <id>: ... unsupported Unicode escape sequence`).
The fix sanitizes the serialized result immediately before the insert, with effectively zero overhead on the common NUL-free path.
## Changes
- **Promote `strip_json_nul` into `windmill-common`** (`utils.rs`): `fn strip_json_nul(&str) -> Cow<str>` — a `contains("\\u0000")` fast guard returns the input borrowed when clean; only a genuine odd-parity NUL escape triggers the O(n) rebuild. `Cow::Owned` is returned **only** when a NUL was actually stripped, so a legitimate `\\u0000` (escaped backslash + literal text) borrows through untouched. Replaces the two duplicated copies previously in `windmill-api/src/drafts.rs` (`strip_json_nul`) and `windmill-api/src/apps.rs` (`strip_null_chars`); both call sites now use the shared helper.
- **Add `serialized_json()` to the `ValidableJson` trait** (`windmill-queue/src/jobs.rs`): `Box<RawValue>` returns `Cow::Borrowed(self.get())` (zero-cost, already serialized); other impls serialize on demand via `to_raw_value`.
- **`commit_completed_job`** binds `strip_json_nul(result.serialized_json())` as `$3::text::jsonb` in both the `INSERT ... SELECT` and the `ON CONFLICT ... result = $3` (was `result as Json<&T>`). Stored data is unchanged (Postgres parses JSON text into `jsonb` identically); `wm_labels`/`result_metadata` still operate on the typed `T`.
- **Regenerated the sqlx offline cache** (one query file swapped; EE caches preserved).
- **Doc:** updated the stale `strip_null_chars` reference in `windmill-api-workspaces/src/workspaces.rs` to point at the shared `strip_json_nul`.
## Test plan
- [x] `cargo check -p windmill-queue -p windmill-api -p windmill-common -p windmill-api-workspaces` — clean, no warnings
- [x] `strip_json_nul` unit tests in `windmill-common` (clean-borrow, real-NUL, legit-escape borrow no-op, collision, nested keys/values, odd-run): 6 passed
- [x] End-to-end regression in `backend/tests/nativets_jobs.rs` (`--features deno_core`): a JS job returning a genuine NUL and a literal `\\u0000` completes, storing `"ab"` (stripped) and `"a\\u0000b"` (preserved). Without the fix the insert aborts and the job never completes.
- [x] `backend/tests/drafts_nul.rs` integration test still passes (helper refactor intact)
138 lines
4.2 KiB
TOML
138 lines
4.2 KiB
TOML
[package]
|
|
name = "windmill-common"
|
|
version.workspace = true
|
|
authors.workspace = true
|
|
edition.workspace = true
|
|
|
|
[features]
|
|
default = []
|
|
enterprise = ["dep:aws-config"]
|
|
instance_config_schema = ["dep:schemars"]
|
|
local_reports = ["dep:rsa", "dep:aes-gcm"]
|
|
private = ["dep:aws-sdk-rds", "dep:aws-sdk-secretsmanager", "dep:aws-config"]
|
|
jemalloc = ["dep:tikv-jemalloc-ctl"]
|
|
tantivy = []
|
|
prometheus = ["dep:prometheus"]
|
|
benchmark = []
|
|
parquet = []
|
|
aws_auth = ["dep:aws-sdk-sts", "dep:aws-config"]
|
|
otel = ["dep:opentelemetry-semantic-conventions", "dep:opentelemetry-otlp", "dep:opentelemetry_sdk",
|
|
"dep:tracing-opentelemetry", "dep:opentelemetry-appender-tracing", "dep:tonic", "dep:opentelemetry"]
|
|
smtp = ["dep:mail-send"]
|
|
scoped_cache = []
|
|
cloud = []
|
|
dev_override = []
|
|
openidconnect = ["dep:openidconnect"]
|
|
python = ["dep:windmill-parser-py"]
|
|
|
|
[lib]
|
|
name = "windmill_common"
|
|
path = "src/lib.rs"
|
|
|
|
[dependencies]
|
|
tar.workspace = true
|
|
hmac.workspace = true
|
|
sha2.workspace = true
|
|
thiserror.workspace = true
|
|
anyhow.workspace = true
|
|
serde.workspace = true
|
|
serde_json.workspace = true
|
|
serde_yml.workspace = true
|
|
memchr.workspace = true
|
|
erased-serde = "0.4"
|
|
chrono.workspace = true
|
|
chrono-tz.workspace = true
|
|
hex.workspace = true
|
|
async-trait.workspace = true
|
|
reqwest-middleware = { workspace = true }
|
|
reqwest-retry = { workspace = true }
|
|
rand.workspace = true
|
|
sqlx = { workspace = true, features = ["postgres"] }
|
|
uuid.workspace = true
|
|
tracing = { workspace = true }
|
|
axum = { workspace = true }
|
|
hyper = { workspace = true }
|
|
tokio = { workspace = true }
|
|
tokio-stream.workspace = true
|
|
tokio-util.workspace = true
|
|
datafusion = { workspace = true, optional = true}
|
|
reqwest = { workspace = true }
|
|
tracing-subscriber = { workspace = true }
|
|
aho-corasick = "1"
|
|
lazy_static.workspace = true
|
|
tracing-appender.workspace = true
|
|
gethostname.workspace = true
|
|
itertools.workspace = true
|
|
regex.workspace = true
|
|
git-version.workspace = true
|
|
cron.workspace = true
|
|
magic-crypt.workspace = true
|
|
prometheus = { workspace = true, optional = true }
|
|
aws-config = { workspace = true, optional = true }
|
|
aws-sdk-sts = { workspace = true, optional = true }
|
|
base64.workspace = true
|
|
bitflags.workspace = true
|
|
once_cell.workspace = true
|
|
phf.workspace = true
|
|
tokio-postgres.workspace = true
|
|
postgres-native-tls.workspace = true
|
|
native-tls.workspace = true
|
|
|
|
aws-smithy-types-convert = { workspace = true, optional = true }
|
|
aws-sdk-secretsmanager = { workspace = true, optional = true }
|
|
aws-sdk-rds = { workspace = true, optional = true }
|
|
indexmap.workspace = true
|
|
bytes.workspace = true
|
|
mail-send = { workspace = true, optional = true }
|
|
futures-core.workspace = true
|
|
async-stream.workspace = true
|
|
const_format.workspace = true
|
|
const-str.workspace = true
|
|
crc.workspace = true
|
|
windmill-macros.workspace = true
|
|
windmill-parser-sql.workspace = true
|
|
windmill-parser-sql-asset.workspace = true
|
|
windmill-parser-ts.workspace = true
|
|
windmill-parser-py = { workspace = true, optional = true }
|
|
windmill-parser.workspace = true
|
|
jsonwebtoken.workspace = true
|
|
backon.workspace = true
|
|
openidconnect = { workspace = true, optional = true }
|
|
schemars = { workspace = true, optional = true }
|
|
strum.workspace = true
|
|
strum_macros.workspace = true
|
|
windmill-types.workspace = true
|
|
url.workspace = true
|
|
urlencoding.workspace = true
|
|
async-recursion.workspace = true
|
|
pep440_rs.workspace = true
|
|
systemstat.workspace = true
|
|
size.workspace = true
|
|
rsa = { workspace = true, optional = true }
|
|
aes-gcm = { workspace = true, optional = true }
|
|
|
|
semver.workspace = true
|
|
croner.workspace = true
|
|
quick_cache.workspace = true
|
|
arc-swap.workspace = true
|
|
pin-project-lite.workspace = true
|
|
futures.workspace = true
|
|
tempfile.workspace = true
|
|
globset.workspace = true
|
|
dashmap.workspace = true
|
|
|
|
opentelemetry-semantic-conventions = { workspace = true, optional = true }
|
|
opentelemetry-otlp = { workspace = true, optional = true }
|
|
opentelemetry_sdk = { workspace = true, optional = true }
|
|
opentelemetry = { workspace = true, optional = true }
|
|
tracing-opentelemetry = { workspace = true, optional = true }
|
|
opentelemetry-appender-tracing = { workspace = true, optional = true }
|
|
tonic = { workspace = true, optional = true }
|
|
equivalent = "1.0.2"
|
|
|
|
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
|
tikv-jemalloc-ctl = { optional = true, workspace = true }
|
|
|
|
[target.'cfg(windows)'.dependencies]
|
|
sysinfo.workspace = true
|