# Fileset Scripts

# Fileset Scripts Overview

FileWave can run scripts at seven stages of Fileset deployment, called **activation states**:

- Requirements
- Preflight
- Activation
- Postflight
- Verification
- Pre-Uninstallation
- Post-Uninstallation

In FileWave Admin, open the **Filesets** view, select the Fileset you want to edit, and click the **Scripts** toolbar icon. The Scripts dialog shows each script and the activation state where it will run.

![Fileset Scripts dialog showing the seven script activation states](https://kb.filewave.com/uploads/images/gallery/2023-07/zF04tsW7k78GexaK-embedded-image-fxwzjver.png)

Scripts in the same activation state run from top to bottom as shown in the list. Drag scripts to change their execution order.

Use the create or import buttons to add scripts. You can also edit a script, or drag a script from Finder into the dialog to import it.

Click **OK** to save changes to the Fileset. Click **Cancel** to discard the changes.

Double-clicking a script opens the file properties dialog. Most script attributes can be changed in the same way as files in an open Fileset, but some settings are locked because the Fileset workflow depends on them. For example, the Execute flag cannot be cleared. Requirement scripts also cannot be changed between interactive and non-interactive execution because the client must be able to use the script exit code to decide whether the Fileset should download.

The checkbox **Re-run requirement scripts on change and uninstall active Fileset if they failed** controls the same internal setting as **Evaluate requirements on change and uninstall active Fileset if they failed** in the Requirements tab of the Fileset properties. When this option is enabled and the Fileset changes, the client re-evaluates the Fileset requirements, including requirement scripts. If any requirement or requirement script fails, the Fileset is uninstalled.

## Fileset script types

- ***Requirements Scripts*** – Run before the Fileset or its dependencies are downloaded. If any requirement script returns a non-zero exit code, the Fileset and its dependencies are not downloaded or installed.
- ***Preflight Scripts*** – Run after dependencies are installed, but before the Fileset downloads. If any preflight script returns a non-zero exit code, the Fileset is not downloaded or installed.
- ***Activation Scripts*** – Run when the Fileset is activated.
- ***Postflight Scripts*** – Run after the Fileset installation has completed.
- ***Verification Scripts*** – Run after postflight scripts and during each verification of the Fileset.
- ***Pre-Uninstallation Scripts*** – Run when a Fileset is inactivated, immediately before the Fileset is uninstalled. Use this state when the script needs access to files that self-healing will remove during uninstall.
- ***Post-Uninstallation Scripts*** – Run immediately after the Fileset and its dependencies have been uninstalled or removed from the client.

## Related Content

- [Fileset / Payload Script Exit Code Status](https://kb.filewave.com/books/filesets-payloads/page/fileset-payload-script-exit-code-status "Fileset / Payload Script Exit Code Status")
- [Script Best Practices](https://kb.filewave.com/books/filewave-general-info/page/script-best-practices "Script Best Practices")
- [How the Client Communicates](https://kb.filewave.com/books/filewave-client/page/how-the-filewave-client-communicates)

# Fileset / Payload Script Exit Code Status

## Script Exit Codes

<div class="panelHeader" id="bkmrk-"></div><div class="panelHeader" id="bkmrk-when-a-script-runs%2C-">When a script runs, one of the below errors may be the outcome. </div><div class="panelHeader" id="bkmrk--1">  
</div><div class="panelContent" id="bkmrk-status-value-status-"><div class="panelContent"><div class="table-wrap"><table class="confluenceTable tablesorter tablesorter-default" role="grid" style="width: 100%;"><colgroup><col style="width: 11.2485%;"></col><col style="width: 22.3733%;"></col><col style="width: 10.2574%;"></col><col style="width: 56.1208%;"></col></colgroup><thead><tr role="row" style="background-color: rgb(251, 238, 184);"><th aria-disabled="false" aria-label="Status Value: No sort applied, activate to apply an ascending sort" aria-sort="none" class="confluenceTh tablesorter-header sortableHeader tablesorter-headerUnSorted" data-column="0" role="columnheader" scope="col" tabindex="0"><div class="tablesorter-header-inner">Status Value</div></th><th aria-disabled="false" aria-label="Status Description: No sort applied, activate to apply an ascending sort" aria-sort="none" class="confluenceTh tablesorter-header sortableHeader tablesorter-headerUnSorted" data-column="1" role="columnheader" scope="col" tabindex="0"><div class="tablesorter-header-inner">Status Description</div></th><th aria-disabled="false" aria-label="Severity: No sort applied, activate to apply an ascending sort" aria-sort="none" class="confluenceTh tablesorter-header sortableHeader tablesorter-headerUnSorted" data-column="2" role="columnheader" scope="col" tabindex="0"><div class="tablesorter-header-inner">Severity</div></th><th aria-disabled="false" aria-label="Status Details: No sort applied, activate to apply an ascending sort" aria-sort="none" class="confluenceTh tablesorter-header sortableHeader tablesorter-headerUnSorted" data-column="3" role="columnheader" scope="col" tabindex="0"><div class="tablesorter-header-inner">Status Details</div></th></tr></thead><tbody aria-live="polite" aria-relevant="all"><tr role="row"><td class="confluenceTd">220</td><td class="confluenceTd">Failed! (Will Not Retry)</td><td class="confluenceTd">ERROR</td><td class="confluenceTd">Script exited with a failure, do not automatically retry</td></tr><tr role="row"><td class="confluenceTd">210</td><td class="confluenceTd">Success (Skipped Install)</td><td class="confluenceTd">ERROR</td><td class="confluenceTd">Script exited successfully, report fileset as installed but skip actual installation </td></tr><tr role="row"><td class="confluenceTd">0</td><td class="confluenceTd">Success</td><td class="confluenceTd">OK</td><td class="confluenceTd">Script exited successfully</td></tr><tr role="row"><td class="confluenceTd">-1000</td><td class="confluenceTd">Crashed</td><td class="confluenceTd">ERROR</td><td class="confluenceTd">Script crashed during execution</td></tr><tr role="row"><td class="confluenceTd">-1001</td><td class="confluenceTd">Time Out Exceeded</td><td class="confluenceTd">ERROR</td><td class="confluenceTd">Script execution time took longer than Get Info &gt; Executable &gt; 'Wait for executable to finish' &gt; 'Wait for:'</td></tr><tr role="row"><td class="confluenceTd">-1002</td><td class="confluenceTd">No Logged In User</td><td class="confluenceTd">ERROR</td><td class="confluenceTd">Script could not run - no user currently logged in</td></tr><tr role="row"><td class="confluenceTd">-1003</td><td class="confluenceTd">Failed To Start</td><td class="confluenceTd">ERROR</td><td class="confluenceTd">Script could not run - failure to start the script</td></tr></tbody></table>

</div></div></div># Expected behavior

## Requirements scripts processing rules

- If any of the requirements scripts returns 220, we stop executing scripts and also stop trying to install the fileset. No further action will be done, unless manually requested by an administrator, or unless a newer version of the fileset is available. The fileset status will be reported as "Requirements Not Met: Will Not Retry".
- If any of the scripts returns non-zero and ≠210, we stop executing scripts. Requirement scripts will be executed again 2 minutes later.
- If any of the scripts returns 210 when all other scripts return 0, the fileset status will be reported as "Skipped". The fileset will not be installed.
- Only if all scripts return 0, then we will install the fileset.

## Kiosk

If a requirements script returns 210 or 220, the fileset will not be available in Kiosk.

If the dependency of a fileset has a requirements script that returns 210, it will not affect the availability of the main fileset. However, if it returns 220, the dependencies will fail so the main fileset will not be available in Kiosk.

## Dependencies

- If the main fileset is at "Skipped" state, its dependencies will still be processed and installed.
- If a dependency is at "Requirements Not Met: Will Not Retry", that does not propagate up the tree. This means the installation of the main fileset will still fail due to failed requirements, but the main fileset will simply be reported as Download/Activation/Update of dependency fileset failure.

## Other scripts

Whenever a requirements script returns 210 or 220, the fileset does not get installed, so other types of scripts (preflight, activation, etc) will not be executed. Only in case the requirements scripts are run again and they change, then the fileset might get installed and in that case other types of scripts will get run as usual.

Inventory

- Filesets in "Skipped" status are reported to inventory, like if they were actually installed.
- Filesets in "Requirements Not Met: Will Not Retry" status are not reported to inventory at all.

# Windows Requirement Script Examples

Requirement scripts are executed on client devices with each tickle interval, 2 minutes by default, to check if the installation conditions outlined have been met. For example, it can used to verify if some file, registry key, service, or process is or is not present and decide whether to proceed with fileset activation or not, because you may want to

1. Block redundant installations if the app is already present or
2. Ensure prerequisites are present or
3. Enforce a particular installation order.

If the requirement script returns any other exit code but 0 (e.g. 1 or -1) it is considered a failure and will be reported as a "Requirements Failure: Script" in the *Client Info* window and Fileset Report. This prevents the contents of the fileset from downloading and installing. As mentioned previously, requirement scripts will be executed every tickle interval and the fileset will be installed only when they all return 0. To check for multiple conditions simply specify multiple requirement scripts.

![](https://kb.filewave.com/uploads/images/gallery/2023-07/qfNh1KpcKOQ3Hvjw-embedded-image-uhfx0p7y.png)

Below are some sample requirement scripts for Windows that can be customized for your own use. If you want the opposite condition, then flip the exit error codes in the examples.

***Install if registry key present***

```
::Replace HKLM\path\to\registry\key with the actual path to the registry key, e.g. HKLM\SOFTWARE\Macromedia\FlashPlayer
 
reg query "HKLM\path\to\registry\key"
if %ERRORLEVEL% EQU 0 (
    exit 0
) else (
    exit 1
)
```

***Install if registry value present***

```
::Replace HKLM\path\to\registry\key with the actual path to the registry key containing your value, e.g. HKLM\SOFTWARE\Macromedia\FlashPlayer
::Replace <value> with the actual name of the value, e.g. CurrentVersion in this example
 
reg query "HKLM\path\to\registry\key" /v <value>
if %ERRORLEVEL% EQU 0 (
    exit 0
) else (
    exit 1
)
```

***Install if file or folder present***

```
::Replace <drive>:\path\to\file\or\folder with the actual path to the file or folder, e.g. %ProgramFiles(x86)%\Mozilla Firefox\firefox.exe
 
if exist "<drive>:\path\to\file\or\folder" (
    exit 0
) else (
    exit 1
)
```

***Install if application present in Programs &amp; Features***

```
::Replace <AppName> with the name of your app, e.g. Adobe Acrobat Reader DC
::Be as specific as possible because partial app names may provide a match when you don't necessarily want it to, e.g. Adobe will match both Adobe Acrobat Reader DC and Adobe Flash
 
reg export HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall %temp%\applist1.txt
reg export HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall %temp%\applist2.txt
copy %temp%\applist1.txt + %temp%\applist2.txt %temp%\applisttemp.txt
find "DisplayName" %temp%\applisttemp.txt > %temp%\applist.txt
 
find "<AppName>" %temp%\applist.txt
if %ERRORLEVEL% EQU 0 (
    del %temp%\applist*
    exit 0
) else (
    del %temp%\applist*
    exit 1
)
```

***Install if service present***

```
::Replace <service> with the name of your service from the Services control panel, e.g. Adobe Acrobat Update Service
::Be as specific as possible because partial service names may provide a match when you don't necessarily want it to, e.g. FileWave will match both FileWave Client and FileWave UltraVNC Server
 
sc query | find "DISPLAY_NAME" | find "<service>"
if %ERRORLEVEL% EQU 0 (
    exit 0
) else (
    exit 1
)
```

***Install if process present***

```
::Replace <process> with the name of your process, e.g. notepad.exe
 
tasklist /FI "IMAGENAME eq <process>" 2>NUL | find /I /N "<process>"
if %ERRORLEVEL% EQU 0 (
    exit 0
) else (
    exit 1
)
```

If you find that you need to delete a requirement script for any reason, right-click that script and choose *Reveal in Fileset*. That will open the *Fileset Contents* window with the script file highlighted. Click the *Delete* icon in the toolbar to delete your script.

![](https://kb.filewave.com/uploads/images/gallery/2023-07/2q00MQgiNYbRhaIb-embedded-image-dhd0mgqw.png) ![](https://kb.filewave.com/uploads/images/gallery/2023-07/4m0mqzIZnZIGnYYB-embedded-image-b0rs0i3m.png)

Remember to always test your requirement scripts locally first before adding them to a fileset. With the above building blocks as examples, you'll quickly be on your way to taking advantage of requirements scripts for providing conditional intelligence to your Windows filesets.

# Mitigating Privilege Escalation in Fileset Executables

## What

<p class="callout info">This article reflects the behavior in FileWave 16.0.0+</p>

When deploying Filesets in FileWave;

- It’s possible to include a Verification Script that runs every 24 hours (or at system restart) on Windows and macOS.
- You might also have a Requirements Script that runs every 2 minutes to test for a condition needed to install something.

These scripts are typically used for tasks such as re-applying Group Policy settings—using tools like LGPO.exe on Windows—as an example. However, if an executable (like LGPO.exe) is replaced by a non-administrator, there’s a risk that the malicious replacement could be executed with elevated privileges (SYSTEM on Windows or root on macOS).

By default, Filesets benefit from a self-healing mechanism. If an executable is modified, the Fileset will restore the original file before the Verification Script executes. An image below shows this status and picking **Get Info** for a file may be used to change this on a per-file basis. Properties for a Fileset can be used to change the behavior across the entire Fileset as well.

[![image.png](https://kb.filewave.com/uploads/images/gallery/2025-02/scaled-1680-/qXzyhyqHfvEn8zaa-image.png)](https://kb.filewave.com/uploads/images/gallery/2025-02/qXzyhyqHfvEn8zaa-image.png)

This same protection applies when the executable is deployed and then called by a Custom Field—the Fileset’s verification step precedes the Custom Field execution, thereby mitigating the risk of local privilege escalation.

However, there is one scenario where this protection of Self-Healing does not apply: the Blocker Script, which by default runs every 5 minutes. Its more frequent execution window could allow a local user to replace an executable before the next Fileset verification occurs. However this too can be mitigated as we will discuss below.

## When/Why

This article is particularly relevant in environments where:

- Executable files (e.g., LGPO.exe) are deployed via Filesets that also include a Verification Script or are triggered via Custom Fields.
- There is a risk of local users, who lack administrator rights, replacing these executables to attempt privilege escalation.
- The deployment involves Blocker Scripts, which run at more frequent intervals (every 5 minutes), thus presenting a window where an attacker might replace a file before it is healed by the Fileset.

#### **Why is this important?**

Ensuring that only the intended executable is run with elevated privileges is critical for system security. A malicious modification could lead to unauthorized privilege escalation. Mitigating this risk by enforcing strict file ownership and permission settings prevents non-administrative users from replacing or modifying executable files.

## How

To mitigate this risk, it is recommended to either pick properties on the file in the Fileset and properly secure it or use a Postflight Script in your Fileset deployment that sets the file permissions so that only SYSTEM (on Windows) or root (on macOS) can modify the file.

On Windows systems as of FileWave 16.0.0 the c:\\ProgramData\\FileWave\\ directories like FWClient and the scripts directory are restricted so that a non-Administrator is unable to manipulate files located there. This is one way that FileWave will prevent a local attack by default. Files on Windows will also inherit the permissions of the directory that they are deployed in to which will help for a more consistent permissions experience.

For some examples of setting permissions on files please take a look at the below. For the scripts you could have them run as Fileset scripts when deploying the files to adjust permissions to how you'd like.

#### **macOS Example (Properties of file in Fileset)**

In the below images you can see an example file in a Fileset where **Get Info** has been clicked in the toolbar when viewing the Fileset and the permissions have been adjusted so only root:wheel has access to the file. Make sure you click **Apply** on each screen to make the setting change.

![image.png](https://kb.filewave.com/uploads/images/gallery/2025-02/scaled-1680-/Vt54E3NZtamleYOt-image.png)![image.png](https://kb.filewave.com/uploads/images/gallery/2025-02/scaled-1680-/nrsHrVWRWlt8VEtB-image.png)

#### **macOS Example (zsh Script)**

Below is an example zsh script that sets the ownership to <span class="s1">root:wheel</span> and the permissions to <span class="s1">770</span> for the target executable.

```shell
#!/bin/zsh
# Define the path to the executable deployed via the Fileset
TARGET_FILE="/path/to/executable"

# Change the ownership of the file to root:wheel
chown root:wheel "$TARGET_FILE"

# Set the permissions to 770 (owner and group have full permissions, others have none)
chmod 770 "$TARGET_FILE"

# Explanation:
# - chown root:wheel: sets the file owner to root and group to wheel.
# - chmod 770: grants read, write, and execute permissions to the owner and group,
#   while denying any permissions to others.
```

#### **Windows Example (PowerShell Script)**

The following PowerShell script sets the file’s owner to SYSTEM and adjusts the permissions so that only Administrators and SYSTEM have full control. This ensures that no non-administrator can replace the file.

```powershell
# Define the path to the executable deployed via the Fileset
$TargetFile = "C:\Path\To\executable.exe"

# Set the owner of the file to SYSTEM using icacls
icacls $TargetFile /setowner "SYSTEM"

# Remove inherited permissions to ensure only our defined permissions are in place
icacls $TargetFile /inheritance:r

# Grant full control (F) to SYSTEM
icacls $TargetFile /grant SYSTEM:(F)

# Grant full control (F) to Administrators
icacls $TargetFile /grant Administrators:(F)

# Explanation:
# - icacls /setowner "SYSTEM": sets the file owner to SYSTEM.
# - icacls /inheritance:r: removes inherited permissions so only our custom permissions apply.
# - icacls /grant SYSTEM:(F) and /grant Administrators:(F): 
#   ensures that only SYSTEM and Administrators have full control over the file.
```

## Related Content

- [FileWave Version 16.0.2 Security Notices](https://kb.filewave.com/link/1045#bkmrk-security-notices-the)
- [Verification](https://kb.filewave.com/books/filesets-payloads/page/verification "Verification")
- [Script Best Practices](https://kb.filewave.com/books/filewave-general-info/page/script-best-practices "Script Best Practices")
- [Fileset Scripts Overview](https://kb.filewave.com/books/filesets-payloads/page/fileset-scripts-overview "Fileset Scripts Overview")