{"id":24965,"date":"2026-03-06T15:14:42","date_gmt":"2026-03-06T14:14:42","guid":{"rendered":"https:\/\/herolab.usd.de\/security-advisories\/usd-2025-0013\/"},"modified":"2026-03-10T10:03:03","modified_gmt":"2026-03-10T09:03:03","slug":"usd-2025-0013","status":"publish","type":"page","link":"https:\/\/herolab.usd.de\/en\/security-advisories\/usd-2025-0013\/","title":{"rendered":"usd-2025-0013"},"content":{"rendered":"<p>[et_pb_section fb_built=\"1\" _builder_version=\"4.21.0\" _module_preset=\"default\" background_color=\"#2E353D\" custom_padding=\"||0px|||\" global_colors_info=\"{}\"][et_pb_row _builder_version=\"4.25.2\" _module_preset=\"default\" global_colors_info=\"{}\"][et_pb_column type=\"4_4\" _builder_version=\"4.21.0\" _module_preset=\"default\" global_colors_info=\"{}\"][et_pb_text _builder_version=\"4.27.5\" _module_preset=\"default\" custom_padding=\"||13px|||\" global_colors_info=\"{}\"]<\/p>\n<h1>usd-2025-13 | ArcGIS Arcade (at least until v1.28) - Language Escape in JavaScript Runtime<\/h1>\n<h1><\/h1>\n<p><strong>Product<\/strong>: ArcGIS Arcade<br \/><strong>Affected Version<\/strong>: at least until v1.28<br \/><strong>Vulnerability Type<\/strong>: Sandbox \/ Language Escape<br \/><strong>Security Risk<\/strong>: Low<br \/><strong>Vendor<\/strong>: ArcGIS<br \/><strong>Vendor URL<\/strong>: <a>https:\/\/www.arcgis.com\/index.html<\/a><br \/><strong>Vendor acknowledged vulnerability<\/strong>: Yes<br \/><strong>Vendor Status<\/strong>: Not fixed<br \/><strong>CVE Number<\/strong>: N\/A<br \/><strong>CVE Link<\/strong>: Not requested, Vendor is CNA<br \/><strong>Advisory ID<\/strong>: usd-2025-13<\/p>\n<h3>Description<\/h3>\n<p>Arcade is a scripting Language used in various ArcGIS Products.<br \/>When executed in a JavaScript Runtime (either ArcGIS Maps SDK for JavaScript or ArcGIS Online) Arcade scripts can execute arbitrary JavaScript.<br \/>This is possible, even though there are measures in place to prevent a language escape.<\/p>\n<h3>Proof of Concept<\/h3>\n<p>The following snippet leads to an alert on <a href=\"https:\/\/developers.arcgis.com\/arcade\/playground\/\" target=\"_blank\" rel=\"noopener\">Arcade Playground<\/a>.<\/p>\n<pre class=\"codehilite\" style=\"line-height: 125%;background: #263238;color: #eff\">var obj = {};<br \/>var func = {\"__proto__\": obj, \"attributes\": obj.constructor};<br \/>var target = {\"__proto__\": obj, \"attributes\": obj};<br \/>var wrapper = {\"__proto__\": obj, \"attributes\": target};<br \/>wrapper.hasField = func.constructor;<br \/>var payload = HasKey(target, \"alert(1)\");<br \/>wrapper.castToText = payload;Text(target)<\/pre>\n<p>The main vulnerability lies in the way __<strong><strong>proto__<\/strong><\/strong>\/ <strong>constructor<\/strong> are handled as properties in Arcade dictionaries.<br \/>Internally an Arcade dictionary is a class with an <strong>attributes<\/strong> field that stores the actual dictionary.<br \/>For instance the dictionary <strong>{\"test\":1}<\/strong> would be stored as follows:<\/p>\n<pre class=\"codehilite\" style=\"line-height: 125%;background: #263238;color: #eff\">{<br \/>    \"declaredClass\": \"esri.arcade.Dictionary\",<br \/>    \"plain\": false,<br \/>    \"immutable\": false,<br \/>    \"attributes\": {<br \/>        \"test\": 1<br \/>    }<br \/>}<\/pre>\n<p>When creating a dictonary with a __<strong><strong>proto__<\/strong><\/strong> key, this structure is destroyed. For example <strong>{\"__<strong>proto__<\/strong>\":{}, \"attributes\":1}<\/strong><br \/>will lead to the following JavaScript object:<\/p>\n<pre class=\"codehilite\" style=\"line-height: 125%;background: #263238;color: #eff\">{<br \/>    \"declaredClass\": \"esri.arcade.Dictionary\",<br \/>    \"plain\": false,<br \/>    \"immutable\": false,<br \/>    \"attributes\": 1<br \/>}<\/pre>\n<p>Now accessing the <strong>toString<\/strong> property of the Arcade dictionary will resolve to <strong>1.toString<\/strong>.<br \/>This way, properties can also be overwritten and hence internal fields manipulated.<br \/>Even without manipulation of the internal structure the <strong>constructor<\/strong> property allows access to the constructor of the <strong>attributes<\/strong> field (usually an Object).<\/p>\n<p>The Proof of Concept uses these two primitives in the following way:<\/p>\n<p>1. Create a <strong>func<\/strong> object, that has an attributes field containing the <strong>Object<\/strong> constructor internally.Accessing <strong>func.constructor<\/strong> hence resolves to <strong>Object.constructor<\/strong>, which is the <strong>Function<\/strong> constructor.<\/p>\n<p>2. Create a <strong>target<\/strong> object and a <strong>wrapper<\/strong> object that has <strong>target<\/strong> as its attributes field.<\/p>\n<p>3. Accessing <strong>wrapper.hasField<\/strong> accesses the <strong>hasField<\/strong> property of <strong>target<\/strong>.This method is called internally, when using the <strong>HasKey<\/strong> function. Hence overwriting it with the <strong>Function<\/strong> constructor leads to the ability of creating arbitrary JavaScript functions.<\/p>\n<p>4. Using a similar trick, a created function can be executed. This time the <strong>castToText<\/strong> method is overwritten.<\/p>\n<h3>Fix<\/h3>\n<p>Instead of using a primitive JavaScript object to store the attributes of a dictionary, a <strong>Map<\/strong> should be used instead.<br \/>Also property accesses should be checked using the <strong>Map.has<\/strong> method before evaluation.<br \/>The square bracket property access should never be done with user input.<\/p>\n<p>As a general precaution the compiled Arcade script could also be evaluated in an HTML iFrame with a dedicated origin.<br \/>This way, even if a language escape is possible, no critical information (e.g., Cookies \/ localStorage) can be accessed by a malicious script.<\/p>\n<h3>References<\/h3>\n<ul>\n<li><a>https:\/\/developers.arcgis.com\/arcade\/<\/a><\/li>\n<li><a>https:\/\/developers.arcgis.com\/arcade\/playground\/<\/a><\/li>\n<li><a>https:\/\/github.com\/eslint-community\/eslint-plugin-security\/blob\/main\/docs\/the-dangers-of-square-bracket-notation.md<\/a><\/li>\n<li><a>https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map<\/a><\/li>\n<\/ul>\n<h3>Timeline<\/h3>\n<ul>\n<li><strong>2025-04-11<\/strong>: Initial contact request is assigned PSIRT-5462 by Esri<\/li>\n<li><strong>2025-04-11<\/strong>: Esri PSIRT assigns BUG-000176907.<\/li>\n<li><strong>2025-06-03<\/strong>: BUG-000176907 is fixed, see <a href=\"https:\/\/support.esri.com\/en-us\/bug\/arcgis-desktop-has-a-security-vulnerability-bug-000176907\" target=\"_blank\" rel=\"noopener\">https:\/\/support.esri.com\/en-us\/bug\/arcgis-desktop-has-a-security-vulnerability-bug-000176907<\/a><\/li>\n<li><strong>2026-03-06<\/strong>: This advisory is published.<\/li>\n<\/ul>\n<h3>Credits<\/h3>\n<p>This security vulnerability was identified by Yannick Sommer of usd AG.<\/p>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>usd-2025-13 | ArcGIS Arcade (at least until v1.28) - Language Escape in JavaScript Runtime Product: ArcGIS ArcadeAffected Version: at least until v1.28Vulnerability Type: Sandbox \/ Language EscapeSecurity Risk: LowVendor: ArcGISVendor URL: https:\/\/www.arcgis.com\/index.htmlVendor acknowledged vulnerability: YesVendor Status: Not fixedCVE Number: N\/ACVE Link: Not requested, Vendor is CNAAdvisory ID: usd-2025-13 Description Arcade is a scripting Language used [&hellip;]<\/p>\n","protected":false},"author":114,"featured_media":0,"parent":16124,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"","_et_gb_content_width":"","inline_featured_image":false,"footnotes":""},"class_list":["post-24965","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/24965","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/users\/114"}],"replies":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/comments?post=24965"}],"version-history":[{"count":3,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/24965\/revisions"}],"predecessor-version":[{"id":24968,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/24965\/revisions\/24968"}],"up":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/16124"}],"wp:attachment":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/media?parent=24965"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}