- Location
- src/platform/windows.js:75
- Finding
- Predictable and Unsafely Cleaned Temporary PowerShell Script<![CDATA[
## Vulnerability Details
**File Location**: `src/platform/windows.js`, lines 18–19 and 75–84
**Vulnerability Type**: Unsafe predictable temporary-file handling
**Risk Level**: Medium
### Vulnerable Code
```javascript
const baseDir = getBaseDir();
const scriptPath = path.join(baseDir, 'temp_calc.ps1');
```
```javascript
fs.writeFileSync(scriptPath, psScript, 'utf8');
try {
execSync(`powershell -ExecutionPolicy Bypass -File "${scriptPath}" -Num "${number}"`, {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe']
});
} catch (error) {
throw error;
}
if (fs.existsSync(scriptPath)) {
fs.unlinkSync(scriptPath);
```
### Technical Analysis
The module writes executable PowerShell content to a fixed filename, `temp_calc.ps1`, in the executable’s base directory. The predictable shared path can collide with another invocation or with a pre-existing file. Where another local process can modify that directory or target path, there is a race window between writing the script and executing it.
Cleanup is not placed in a `finally` block. If `execSync()` throws, the catch block immediately rethrows the error, so the subsequent deletion code is never reached. This leaves the script behind after failed executions.
The function also overwrites an existing file at the predictable path without checking whether it was created by the current invocation.
### Attack Path
1. The calculator module selects the fixed `temp_calc.ps1` path.
2. Another invocation or a local actor targets the same writable path before PowerShell finishes using it.
3. The file may be replaced, modified, or involved in a path collision before execution.
4. PowerShell executes content from that path.
5. Alternatively, an execution failure causes the function to rethrow before cleanup, leaving the script on disk.
Practical exploitation of the race requires local write access to the selected directory or path. Even without an attacker, concurrent ca
...[truncated 392 chars]
- Remediation
- <![CDATA[
## Remediation Suggestions
- Prefer avoiding temporary script files entirely, for example by shipping a read-only script as part of the package or invoking a fixed command safely.
- If a temporary script is necessary, create a unique directory with `fs.mkdtempSync()` under `os.tmpdir()`.
- Use a cryptographically unpredictable per-invocation filename and restrictive file permissions.
- Never place temporary executable content in a shared or package installation directory.
- Perform cleanup in a `finally` block so it runs after both successful and failed execution.
- Avoid check-then-delete sequences where possible and tolerate cleanup errors safely.
- Ensure concurrent invocations cannot share the same script path.
Example structure:
```javascript
const os = require('os');
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'calc-chat-'));
const scriptPath = path.join(tempDir, 'calculator.ps1');
try {
fs.writeFileSync(scriptPath, psScript, {
encoding: 'utf8',
mode: 0o600,
flag: 'wx'
});
execFileSync('powershell.exe', ['-NoProfile', '-File', scriptPath, '-Num', value], {
shell: false,
stdio: ['ignore', 'pipe', 'pipe']
});
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
```
]]>