X-Git-Url: https://git.p6c8.net/policy-templates.git/blobdiff_plain/21efc47e069d7b6bb953beaf371e500abdf90d00..831939465c14f6d1394005e794c2b5590ebc82d9:/docs/index.md?ds=sidebyside
diff --git a/docs/index.md b/docs/index.md
index 2ac1142..1299c69 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,7 +1,6 @@
Firefox policies can be specified using the [Group Policy templates on Windows](https://github.com/mozilla/policy-templates/tree/master/windows), [Intune on Windows](https://support.mozilla.org/kb/managing-firefox-intune), [configuration profiles on macOS](https://github.com/mozilla/policy-templates/tree/master/mac), or by creating a file called `policies.json`. On Windows, create a directory called `distribution` where the EXE is located and place the file there. On Mac, the file goes into `Firefox.app/Contents/Resources/distribution`. On Linux, the file goes into `firefox/distribution`, where `firefox` is the installation directory for firefox, which varies by distribution or you can specify system-wide policy by placing the file in `/etc/firefox/policies`.
Unfortunately, JSON files do not support comments, but you can add extra entries to the JSON to use as comments. You will see an error in about:policies, but the policies will still work properly. For example:
-
```
{
"policies": {
@@ -12,6 +11,7 @@ Unfortunately, JSON files do not support comments, but you can add extra entries
}
}
```
+Note: The `policies.json` must use the UTF-8 encoding.
| Policy Name | Description
| --- | --- |
@@ -36,6 +36,7 @@ Unfortunately, JSON files do not support comments, but you can add extra entries
| **[`Certificates -> ImportEnterpriseRoots`](#certificates--importenterpriseroots)** | Trust certificates that have been added to the operating system certificate store by a user or administrator.
| **[`Certificates -> Install`](#certificates--install)** | Install certificates into the Firefox certificate store.
| **[`Containers`](#containers)** | Set policies related to [containers](https://addons.mozilla.org/firefox/addon/multi-account-containers/).
+| **[`ContentAnalysis`](#contentanalysis)** | Configure Firefox to use an agent for Data Loss Prevention (DLP) that is compatible with the [Google Chrome Content Analysis Connector Agent SDK](https://github.com/chromium/content_analysis_sdk).
| **[`Cookies`](#cookies)** | Configure cookie preferences.
| **[`DefaultDownloadDirectory`](#defaultdownloaddirectory)** | Set the default download directory.
| **[`DisableAppUpdate`](#disableappupdate)** | Turn off application updates.
@@ -668,6 +669,18 @@ Value (string):
}
]'/>
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/AutoLaunchProtocolsFromOriginsOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -934,6 +947,18 @@ Value (string):
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/BookmarksOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -1167,6 +1192,18 @@ Value (string):
}
'/>
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/ContainersOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -1202,6 +1239,174 @@ Value (string):
}
}
```
+### ContentAnalysis
+Configure Firefox to use an agent for Data Loss Prevention (DLP) that is compatible with the [Google Chrome Content Analysis Connector Agent SDK](https://github.com/chromium/content_analysis_sdk).
+
+`AgentName` is the name of the DLP agent. This is used in dialogs and notifications about DLP operations. The default is "A DLP Agent".
+
+`AgentTimeout` is the timeout in number of seconds after a DLP request is sent to the agent. After this timeout, the request will be denied unless `DefaultResult` is set to 1 or 2. The default is 30.
+
+`AllowUrlRegexList` is a space-separated list of regular expressions that indicates URLs for which DLP operations will always be allowed without consulting the agent. The default is "^about:(?!blank|srcdoc).*", meaning that any pages that start with "about:" will be exempt from DLP except for "about:blank" and "about:srcdoc", as these can be controlled by web content.
+
+`BypassForSameTabOperations` indicates whether Firefox will automatically allow DLP requests whose data comes from the same tab and frame - for example, if data is copied to the clipboard and then pasted on the same page. The default is false.
+
+`ClientSignature` indicates the required signature of the DLP agent connected to the pipe. If this is a non-empty string and the DLP agent does not have a signature with a Subject Name that exactly matches this value, Firefox will not connect to the pipe. The default is the empty string.
+
+`DefaultResult` indicates the desired behavior for DLP requests if there is a problem connecting to the DLP agent. The default is 0.
+
+| Value | Description
+| --- | --- |
+| 0 | Deny the request (default)
+| 1 | Warn the user and allow them to choose whether to allow or deny
+| 2 | Allow the request
+
+`DenyUrlRegexList` is a space-separated list of regular expressions that indicates URLs for which DLP operations will always be denied without consulting the agent. The default is the empty string.
+
+`Enabled` indicates whether Firefox should use DLP. Note that if this value is true and no DLP agent is running, all DLP requests will be denied unless `DefaultResult` is set to 1 or 2.
+
+`IsPerUser` indicates whether the pipe the DLP agent has created is per-user or per-system. The default is true, meaning per-user.
+
+`PipePathName` is the name of the pipe the DLP agent has created and Firefox will connect to. The default is "path_user".
+
+`ShowBlockedResult` indicates whether Firefox should show a notification when a DLP request is denied. The default is true.
+
+**Compatibility:** Firefox 130\
+**CCK2 Equivalent:** N/A\
+**Preferences Affected:** `browser.contentanalysis.agent_name`, `browser.contentanalysis.agent_timeout`, `browser.contentanalysis.allow_url_regex_list`, `browser.contentanalysis.bypass_for_same_tab_operations`, `browser.contentanalysis.client_signature`, `browser.contentanalysis.default_result`, `browser.contentanalysis.deny_url_regex_list`, `browser.contentanalysis.enabled`, `browser.contentanalysis.is_per_user`, `browser.contentanalysis.pipe_path_name`, `browser.contentanalysis.show_blocked_result`
+
+#### Windows (GPO)
+```
+Software\Policies\Mozilla\Firefox\ContentAnalysis\AgentName = "My DLP Product"
+Software\Policies\Mozilla\Firefox\ContentAnalysis\AgentTimeout = 60
+Software\Policies\Mozilla\Firefox\ContentAnalysis\AllowUrlRegexList = "https://example\.com/.* https://subdomain\.example\.com/.*"
+Software\Policies\Mozilla\Firefox\ContentAnalysis\BypassForSameTabOperations = 0x1 | 0x0
+Software\Policies\Mozilla\Firefox\ContentAnalysis\ClientSignature = "My DLP Company"
+Software\Policies\Mozilla\Firefox\ContentAnalysis\DefaultResult = 0x0 | 0x1 | 0x2
+Software\Policies\Mozilla\Firefox\ContentAnalysis\DenyUrlRegexList = "https://example\.com/.* https://subdomain\.example\.com/.*"
+Software\Policies\Mozilla\Firefox\ContentAnalysis\Enabled = 0x1 | 0x0
+Software\Policies\Mozilla\Firefox\ContentAnalysis\IsPerUser = 0x1 | 0x0
+Software\Policies\Mozilla\Firefox\ContentAnalysis\PipePathName = "pipe_custom_name"
+Software\Policies\Mozilla\Firefox\ContentAnalysis\ShowBlockedResult = 0x1 | 0x0
+```
+
+#### Windows (Intune)
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_AgentName
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_AgentTimeout
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_AllowUrlRegexList
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_BypassForSameTabOperations
+```
+Value (string):
+```
+ or
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_ClientSignature
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_DefaultResult
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_DenyUrlRegexList
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_Enabled
+```
+Value (string):
+```
+ or
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_IsPerUser
+```
+Value (string):
+```
+ or
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_PipePathName
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~ContentAnalysis/ContentAnalysis_ShowBlockedResult
+```
+Value (string):
+```
+ or
+```
+
+#### policies.json
+```
+{
+ "policies": {
+ "ContentAnalysis": {
+ "AgentName": "My DLP Product",
+ "AgentTimeout": 60,
+ "AllowUrlRegexList": "https://example\.com/.* https://subdomain\.example\.com/.*",
+ "BypassForSameTabOperations": true | false,
+ "ClientSignature": "My DLP Company",
+ "DefaultResult": 0 | 1 | 2,
+ "DenyUrlRegexList": "https://example\.com/.* https://subdomain\.example\.com/.*",
+ "Enabled": true | false,
+ "IsPerUser": true | false,
+ "PipePathName": "pipe_custom_name",
+ "ShowBlockedResult": true | false,
+ }
+ }
+}
+```
+
### Cookies
Configure cookie preferences.
@@ -2752,6 +2957,18 @@ Value (string):
]
'/>
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/ExemptDomainFileTypePairsFromFileTypeDownloadWarningsOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -2939,6 +3156,18 @@ Value (string):
}
}'/>
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/ExtensionSettingsOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -3322,6 +3551,18 @@ Value (string):
}
'/>
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/HandlersOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -3613,7 +3854,7 @@ Value (string):
```
HttpsOnlyMode
- allowed | disallowed | enabled| force_enabled
+ allowed | disallowed | enabled | force_enabled
```
#### policies.json
@@ -3926,6 +4167,18 @@ Value (string):
}
]'/>
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/ManagedBoomarksOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -4288,7 +4541,7 @@ Remove access to the password manager via preferences and blocks about:logins on
**Compatibility:** Firefox 70, Firefox ESR 60.2\
**CCK2 Equivalent:** N/A\
-**Preferences Affected:** `pref.privacy.disable_button.view_passwords`
+**Preferences Affected:** `pref.privacy.disable_button.view_passwords`, `signon.rememberSignons`
#### Windows (GPO)
```
@@ -4432,27 +4685,32 @@ Set permissions associated with camera, microphone, location, notifications, aut
#### Windows (GPO)
```
Software\Policies\Mozilla\Firefox\Permissions\Camera\Allow\1 = "https://example.org"
-Software\Policies\Mozilla\Firefox\Permissions\Camera\Allow\2 = "https://example.org:1234"
+Software\Policies\Mozilla\Firefox\Permissions\Camera\Allow\2 = "https://example.com"
Software\Policies\Mozilla\Firefox\Permissions\Camera\Block\1 = "https://example.edu"
Software\Policies\Mozilla\Firefox\Permissions\Camera\BlockNewRequests = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Camera\Locked = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Microphone\Allow\1 = "https://example.org"
+Software\Policies\Mozilla\Firefox\Permissions\Microphone\Allow\2 = "https://example.com"
Software\Policies\Mozilla\Firefox\Permissions\Microphone\Block\1 = "https://example.edu"
Software\Policies\Mozilla\Firefox\Permissions\Microphone\BlockNewRequests = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Microphone\Locked = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Location\Allow\1 = "https://example.org"
+Software\Policies\Mozilla\Firefox\Permissions\Location\Allow\2 = "https://example.com"
Software\Policies\Mozilla\Firefox\Permissions\Location\Block\1 = "https://example.edu"
Software\Policies\Mozilla\Firefox\Permissions\Location\BlockNewRequests = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Location\Locked = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Notifications\Allow\1 = "https://example.org"
+Software\Policies\Mozilla\Firefox\Permissions\Notifications\Allow\2 = "https://example.com"
Software\Policies\Mozilla\Firefox\Permissions\Notifications\Block\1 = "https://example.edu"
Software\Policies\Mozilla\Firefox\Permissions\Notifications\BlockNewRequests = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Notifications\Locked = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\Autoplay\Allow\1 = "https://example.org"
+Software\Policies\Mozilla\Firefox\Permissions\Autoplay\Allow\2 = "https://example.com"
Software\Policies\Mozilla\Firefox\Permissions\Autoplay\Block\1 = "https://example.edu"
Software\Policies\Mozilla\Firefox\Permissions\Autoplay\Default = "allow-audio-video" | "block-audio" | "block-audio-video"
Software\Policies\Mozilla\Firefox\Permissions\Autoplay\Locked = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\VirtualReality\Allow\1 = "https://example.org"
+Software\Policies\Mozilla\Firefox\Permissions\VirtualReality\Allow\2 = "https://example.com"
Software\Policies\Mozilla\Firefox\Permissions\VirtualReality\Block\1 = "https://example.edu"
Software\Policies\Mozilla\Firefox\Permissions\VirtualReality\BlockNewRequests = 0x1 | 0x0
Software\Policies\Mozilla\Firefox\Permissions\VirtualReality\Locked = 0x1 | 0x0
@@ -4460,6 +4718,92 @@ Software\Policies\Mozilla\Firefox\Permissions\VirtualReality\Locked = 0x1 | 0x0
#### Windows (Intune)
OMA-URI:
```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Camera/Camera_Allow
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Camera/Camera_Block
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Camera/Camera_BlockNewRequests
+```
+Value (string):
+```
+ or
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Camera/Camera_Locked
+```
+Value (string):
+```
+ or
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Microphone/Microphone_Allow
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Microphone/Microphone_Block
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Microphone/Microphone_BlockNewRequests
+```
+Value (string):
+```
+ or
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Microphone/Microphone_Locked
+```
+Value (string):
+```
+ or
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Location/Location_Allow
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Location/Location_Block
+```
+Value (string):
+```
+
+
+```
+OMA-URI:
+```
./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Location/Location_BlockNewRequests
```
Value (string):
@@ -4481,7 +4825,7 @@ OMA-URI:
Value (string):
```
-
+
```
OMA-URI:
```
@@ -4506,7 +4850,7 @@ OMA-URI:
Value (string):
```
-
+
```
OMA-URI:
```
@@ -4536,16 +4880,16 @@ Value (string):
```
OMA-URI:
```
-./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Notifications/VirtualReality_Allow
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~VirtualReality/VirtualReality_Allow
```
Value (string):
```
-
+
```
OMA-URI:
```
-./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Notifications/VirtualReality_Block
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~VirtualReality/VirtualReality_Block
```
Value (string):
```
@@ -4554,7 +4898,7 @@ Value (string):
```
OMA-URI:
```
-./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~Notifications/VirtualReality_BlockNewRequests
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Permissions~VirtualReality/VirtualReality_BlockNewRequests
```
Value (string):
```
@@ -5002,6 +5346,18 @@ Value (string):
}
}'/>
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/PreferencesOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```
@@ -5441,13 +5797,32 @@ or
### SanitizeOnShutdown (Selective)
-Clear data on shutdown. Choose from Cache, Cookies, Download History, Form & Search History, Browsing History, Active Logins, Site Preferences and Offline Website Data.
+Clear data on shutdown.
+
+Note: Starting with Firefox 128, History clears FormData and Downloads as well.
+
+`Cache`
+
+`Cookies`
-Previously, these values were always locked. Starting with Firefox 74 and Firefox ESR 68.6, you can use the `Locked` option to either keep the values unlocked (set it to false), or lock only the values you set (set it to true). If you want the old behavior of locking everything, do not set `Locked` at all.
+`Downloads` Download History (*Deprecated*)
-**Compatibility:** Firefox 68, Firefox ESR 68 (Locked added in 74/68.6)\
+`FormData` Form & Search History (*Deprecated*)
+
+`History` Browsing History, Download History, Form & Search History.
+
+`Sessions` Active Logins
+
+`SiteSettings` Site Preferences
+
+`OfflineApps` Offline Website Data.
+
+`Locked` prevents the user from changing these preferences.
+
+**Compatibility:** Firefox 68, Firefox ESR 68 (Locked added in 74/68.6, History update in Firefox 128)\
**CCK2 Equivalent:** N/A\
-**Preferences Affected:** `privacy.sanitize.sanitizeOnShutdown`, `privacy.clearOnShutdown.cache`, `privacy.clearOnShutdown.cookies`, `privacy.clearOnShutdown.downloads`, `privacy.clearOnShutdown.formdata`, `privacy.clearOnShutdown.history`, `privacy.clearOnShutdown.sessions`, `privacy.clearOnShutdown.siteSettings`, `privacy.clearOnShutdown.offlineApps`
+**Preferences Affected:** `privacy.sanitize.sanitizeOnShutdown`, `privacy.clearOnShutdown.cache`, `privacy.clearOnShutdown.cookies`, `privacy.clearOnShutdown.downloads`, `privacy.clearOnShutdown.formdata`, `privacy.clearOnShutdown.history`, `privacy.clearOnShutdown.sessions`, `privacy.clearOnShutdown.siteSettings`, `privacy.clearOnShutdown.offlineApps`, `privacy.clearOnShutdown_v2.historyFormDataAndDownloads` (Firefox 128), `privacy.clearOnShutdown_v2.cookiesAndStorage` (Firefox 128), `privacy.clearOnShutdown_v2.cache` (Firefox 128), `privacy.clearOnShutdown_v2.siteSettings` (Firefox 128)
+
#### Windows (GPO)
```
Software\Policies\Mozilla\Firefox\SanitizeOnShutdown\Cache = 0x1 | 0x0
@@ -6414,6 +6789,18 @@ Value (string):
```
+If you are using custom ADMX and ADML administrative templates in Intune, you can use this OMA-URI instead
+to workaround the limit on the length of strings. Put all of your JSON on one line.
+
+OMA-URI:
+```
+./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~firefox~Extensions/WebsiteFilterOneLine
+```
+Value (string):
+```
+
+
+```
#### macOS
```