Summary
Every tool that generates ObjectScript source embeds user-supplied strings into ObjectScript "..." string literals with escaping borrowed from another language:
- SQL-style single-quote doubling:
.replace('\'', "''")
- or C-style backslash escaping:
.replace('\\', "\\\\").replace('"', "\\\"")
ObjectScript string literals have exactly one escape: a " inside a literal is written doubled (""). Backslash is an ordinary character — \" leaves the \ literal and the " terminates the string. A literal also cannot span source lines, and the exec-class builder splits generated code on \n, so control characters must never land inside a literal.
Verified on master @ 107cfb6.
Consequences
iris_lookup_transfer action=import fails 100% of the time with <SYNTAX>. %Export XML always contains attribute quotes; the C-style escaping turns version="1.0" into version=\"1.0\" inside Do tStream.Write("..."), which tears the literal open. The export → import round-trip can never succeed. (We measured 7/7 failures on the fork before fixing.)
- Silent data corruption for
'. Any value containing an apostrophe is stored doubled, with success:true reported: credential passwords via Ens.Config.Credentials.SetCredential (O'Brien → stored O''Brien), Security.Users passwords in create_user/modify_user, lookup table values, production item setting values.
- Values containing
" or newlines crash the generated routine with <SYNTAX> (lookup values, settings, credentials, usernames...).
Repro (clearest case)
iris_lookup_manage action=set table=T key=K value=V → ok
iris_lookup_transfer action=export table=T → returns XML
iris_lookup_transfer action=import table=T2 xml=<that same XML> → INTEROP_ERROR ... <SYNTAX>
And the silent one:
iris_lookup_manage action=set table=T key=K value=O'Brien → success:true
iris_lookup_manage action=get table=T key=K → value: "O''Brien"
Affected sites (each verified at 107cfb6)
crates/iris-agentic-dev-core/src/tools/interop.rs
- 501 — production item name, embedded at 515–516 (enable/disable), 558–559 (get_settings), 628–629 (set_settings) into
tProd.FindItemByConfigName("…")
- 613–614 — set_settings key/value into
FindSettingByName("…") / Set tS.Name="…" / Set tS.Value="…" (silent '' corruption of setting values)
- 753, 759, 763 (create) and 797, 804 (update) — credential id/username/password into
##class(Ens.Config.Credentials).SetCredential("…","…","…",…)
- 937, 941 / 976, 980, 984 / 1015, 1019 / 1053 — lookup manage get/set/delete/list_keys into
^Ens.LookupTable("…","…"), %UpdateValue, %RemoveValue
- 1104 — lookup transfer table name (export 1109–1116, import 1157)
- 1149 — import XML, C-style
\\ \" escaping into Do tStream.Write("…") — the guaranteed <SYNTAX>
- 1279 — autostart production name into
SetAutoStart("…")
- 2885–2921 — unit tests that assert the wrong escaping (
production_item_escape_in_objectscript_code, credential_id_escaping, credential_username_escaping, credential_password_escaping, xml_escaping_backslash_and_quote)
crates/iris-agentic-dev-core/src/tools/admin.rs
- 299 (get user roles), 334 (get webapp), 390–391 (
%SYSTEM.Security.Check), 431–434 (create_user — including password), 477, 482, 494 (modify_user — including password), 535 (delete_user), 575–577 (create_namespace), 615 (delete_namespace), 655–657 (create_webapp), 697 (delete_webapp) — all embed into "…" ObjectScript literals with '' doubling
crates/iris-agentic-dev-core/src/tools/dict.rs
- 169 —
build_message_map_code: C-style \\ \" for the class name into %OpenId("…")
Not bugs (correct for their context, listed to scope a fix): the SQL-literal escapes at interop.rs 381, 444, 447, 452, 1646, 1793, coverage.rs:150, info.rs:330; the TOML escaping in manifest/resolve.rs:221-223.
Side note: crates/iris-agentic-dev-core/src/benchmark/container.rs:122 already has a correct private helper (objectscript_string_literal, quote doubling) — the tools just don't use anything like it.
Fix reference
We hit this on the intersystems-ib/iris-interop-dev fork (based on 0.6.x of this repo) and fixed it there: intersystems-ib/iris-interop-dev#7. The fix adds a small objectscript.rs module with os_str_expr() (renders any string as a single-line ObjectScript expression: quotes doubled, control characters spliced via $CHAR(n,...)) and os_stream_write_stmts() (chunked stream writes for long payloads like the import XML), then converts every ObjectScript-context embed site; SQL-context escapes are left untouched. Happy to open a PR porting it onto current master.
Summary
Every tool that generates ObjectScript source embeds user-supplied strings into ObjectScript
"..."string literals with escaping borrowed from another language:.replace('\'', "''").replace('\\', "\\\\").replace('"', "\\\"")ObjectScript string literals have exactly one escape: a
"inside a literal is written doubled (""). Backslash is an ordinary character —\"leaves the\literal and the"terminates the string. A literal also cannot span source lines, and the exec-class builder splits generated code on\n, so control characters must never land inside a literal.Verified on
master@ 107cfb6.Consequences
iris_lookup_transfer action=importfails 100% of the time with<SYNTAX>.%ExportXML always contains attribute quotes; the C-style escaping turnsversion="1.0"intoversion=\"1.0\"insideDo tStream.Write("..."), which tears the literal open. The export → import round-trip can never succeed. (We measured 7/7 failures on the fork before fixing.)'. Any value containing an apostrophe is stored doubled, withsuccess:truereported: credential passwords viaEns.Config.Credentials.SetCredential(O'Brien→ storedO''Brien),Security.Userspasswords in create_user/modify_user, lookup table values, production item setting values."or newlines crash the generated routine with<SYNTAX>(lookup values, settings, credentials, usernames...).Repro (clearest case)
And the silent one:
Affected sites (each verified at 107cfb6)
crates/iris-agentic-dev-core/src/tools/interop.rstProd.FindItemByConfigName("…")FindSettingByName("…")/Set tS.Name="…"/Set tS.Value="…"(silent''corruption of setting values)##class(Ens.Config.Credentials).SetCredential("…","…","…",…)^Ens.LookupTable("…","…"),%UpdateValue,%RemoveValue\\\"escaping intoDo tStream.Write("…")— the guaranteed<SYNTAX>SetAutoStart("…")production_item_escape_in_objectscript_code,credential_id_escaping,credential_username_escaping,credential_password_escaping,xml_escaping_backslash_and_quote)crates/iris-agentic-dev-core/src/tools/admin.rs%SYSTEM.Security.Check), 431–434 (create_user — including password), 477, 482, 494 (modify_user — including password), 535 (delete_user), 575–577 (create_namespace), 615 (delete_namespace), 655–657 (create_webapp), 697 (delete_webapp) — all embed into"…"ObjectScript literals with''doublingcrates/iris-agentic-dev-core/src/tools/dict.rsbuild_message_map_code: C-style\\\"for the class name into%OpenId("…")Not bugs (correct for their context, listed to scope a fix): the SQL-literal escapes at
interop.rs381, 444, 447, 452, 1646, 1793,coverage.rs:150,info.rs:330; the TOML escaping inmanifest/resolve.rs:221-223.Side note:
crates/iris-agentic-dev-core/src/benchmark/container.rs:122already has a correct private helper (objectscript_string_literal, quote doubling) — the tools just don't use anything like it.Fix reference
We hit this on the
intersystems-ib/iris-interop-devfork (based on 0.6.x of this repo) and fixed it there: intersystems-ib/iris-interop-dev#7. The fix adds a smallobjectscript.rsmodule withos_str_expr()(renders any string as a single-line ObjectScript expression: quotes doubled, control characters spliced via$CHAR(n,...)) andos_stream_write_stmts()(chunked stream writes for long payloads like the import XML), then converts every ObjectScript-context embed site; SQL-context escapes are left untouched. Happy to open a PR porting it onto current master.