-
Notifications
You must be signed in to change notification settings - Fork 0
feat(windows): platform title bar + PowerShell hooks + WSL integration #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| *.sh text eol=lf | ||
| *.zsh text eol=lf | ||
| *.bash text eol=lf | ||
| *.py text eol=lf |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| # Windows & WSL Setup | ||
|
|
||
| Running RedLog on Windows — build environment, PowerShell hooks, WSL integration, | ||
| and operational isolation. | ||
|
|
||
| --- | ||
|
|
||
| ## 1. Prerequisites | ||
|
|
||
| RedLog uses `better-sqlite3` (native C++ module), which must be compiled for | ||
| Electron's ABI. That requires a C/C++ toolchain. | ||
|
|
||
| | Requirement | Notes | | ||
| |---|---| | ||
| | **Node.js 20 or 22 (LTS)** | Not 24+ — no prebuilt `better-sqlite3` binary yet. | | ||
| | **Visual Studio Build Tools** | Install the **"Desktop development with C++"** workload. | | ||
| | **Python 3** | Required by `node-gyp`. | | ||
|
|
||
| ```powershell | ||
| winget install Microsoft.VisualStudio.2022.BuildTools | ||
| winget install --id OpenJS.NodeJS.22 -e | ||
| ``` | ||
|
|
||
| ### PowerShell notes | ||
|
|
||
| - Windows PowerShell 5.1 does **not** support `&&`. Either install PowerShell 7 | ||
| (`winget install Microsoft.PowerShell`, run as `pwsh`) or chain with | ||
| `cmd1; if ($?) { cmd2 }`. | ||
| - If `npm` reports *"running scripts is disabled"*, allow user scripts once: | ||
| `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`. | ||
|
|
||
| --- | ||
|
|
||
| ## 2. Build & Run | ||
|
|
||
| ```powershell | ||
| npm install | ||
| npm run rebuild # compile better-sqlite3 for Electron's ABI | ||
| npm run dev # launch the app | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## 3. Packaging | ||
|
|
||
| Installers are built by [electron-builder](https://www.electron.build). The | ||
| GitHub Actions workflow (`.github/workflows/release.yml`) produces releases on | ||
| tags. To build locally: | ||
|
|
||
| ```powershell | ||
| npm run build | ||
| npx electron-builder --win # NSIS + portable → dist\ | ||
| ``` | ||
|
|
||
| **First-run note:** electron-builder downloads `winCodeSign`, which may need | ||
| symlink privileges. Either enable **Developer Mode** (Settings → For developers) | ||
| or run the packaging command once from an elevated terminal. | ||
|
|
||
| --- | ||
|
|
||
| ## 4. PowerShell Shell Hook | ||
|
|
||
| The PowerShell hook (`hooks/shell-hook.ps1`) captures every command from | ||
| PowerShell 5.1+ and pwsh 7+ and sends it to RedLog's timeline. | ||
|
|
||
| ### Quick start | ||
|
|
||
| ```powershell | ||
| # Source in current session | ||
| . "C:\path\to\redlog\hooks\shell-hook.ps1" | ||
|
|
||
| # Or add to your profile for every session | ||
| Add-Content $PROFILE '. "C:\path\to\redlog\hooks\shell-hook.ps1"' | ||
| ``` | ||
|
|
||
| The hook: | ||
| - Overrides the `prompt` function to capture commands via `Get-History` | ||
| - Sends `command_start` + `command_end` events with exit code and duration | ||
| - Uses background runspaces so the prompt is never blocked | ||
| - No-ops silently when RedLog isn't running | ||
|
|
||
| ### Event sender (scripting) | ||
|
|
||
| For custom scripts, use `hooks/redlog-send.ps1`: | ||
|
|
||
| ```powershell | ||
| . ".\hooks\redlog-send.ps1" | ||
| Send-RedLogEvent "invoke-mimikatz" command_start | ||
| # ... run tool ... | ||
| Send-RedLogEvent "invoke-mimikatz" command_end @{ exit_code = 0 } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## 5. WSL Integration | ||
|
|
||
| Pentest tooling often runs in WSL. Two things must line up for WSL → RedLog. | ||
|
|
||
| ### 5.1 Token path | ||
|
|
||
| RedLog writes `api-token` and `api-port` to `%USERPROFILE%\.redlog\`. The WSL | ||
| hook scripts resolve this via `cmd.exe /c 'echo %USERPROFILE%'` + `wslpath`. | ||
|
|
||
| ### 5.2 Networking — mirrored mode | ||
|
|
||
| The API binds `127.0.0.1` on Windows. Under WSL2's default NAT mode, WSL's | ||
| `127.0.0.1` is a separate loopback. Enable **mirrored networking**: | ||
|
|
||
| 1. Create `%USERPROFILE%\.wslconfig`: | ||
| ```ini | ||
| [wsl2] | ||
| networkingMode=mirrored | ||
| ``` | ||
| 2. `wsl --shutdown` (from Windows PowerShell), then reopen WSL. | ||
| 3. Verify: `wslinfo --networking-mode` → `mirrored`. | ||
|
|
||
| ### 5.3 WSL hooks | ||
|
|
||
| ```bash | ||
| # Diagnose the WSL → RedLog link | ||
| bash /mnt/c/Users/<you>/Desktop/REDLOG/hooks/wsl-redlog-test.sh | ||
|
|
||
| # Fire-and-forget event sender | ||
| hooks/redlog-send.sh "nmap -sV $TARGET" command_start | ||
| nmap -sV "$TARGET" | ||
| hooks/redlog-send.sh "nmap -sV $TARGET" command_end "{\"exit_code\":$?}" | ||
| ``` | ||
|
|
||
| The bash/zsh `shell-preexec-hook.sh` also works inside WSL — source it in your | ||
| WSL distro's `~/.bashrc` or `~/.zshrc`. | ||
|
|
||
| --- | ||
|
|
||
| ## 6. Operational Privacy | ||
|
|
||
| ### 6.1 Workspace isolation (primary control) | ||
|
|
||
| Source hooks **only** in engagement shells. Commands in unhooked shells are never | ||
| logged. A clean pattern: do engagement work in a dedicated WSL distro with the | ||
| hook in `~/.bashrc`; keep personal work on the Windows host (unhooked). | ||
|
|
||
| ### 6.2 Pause limitation | ||
|
|
||
| The status-bar recording toggle only hides events from the live timeline — it | ||
| does **not** stop database writes. For genuine isolation, stop the producer | ||
| (unsource the hook or close the engagement workspace). | ||
|
|
||
| ### 6.3 Per-engagement isolation | ||
|
|
||
| Each project is a separate SQLite DB (`~/.redlog/projects/<id>/`). Close / | ||
| switch the project when you stop working an engagement. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # RedLog Event Sender for PowerShell | ||
| # Fire-and-forget event posting from any PowerShell script or tool. | ||
| # | ||
| # Usage: | ||
| # . .\hooks\redlog-send.ps1 | ||
| # Send-RedLogEvent "nmap -sV 10.0.0.1" command_start | ||
| # nmap -sV 10.0.0.1 | ||
| # Send-RedLogEvent "nmap -sV 10.0.0.1" command_end @{ exit_code = $LASTEXITCODE } | ||
|
|
||
| $script:_RedLogPortFile = Join-Path $env:USERPROFILE '.redlog\api-port' | ||
| $script:_RedLogTokenFile = Join-Path $env:USERPROFILE '.redlog\api-token' | ||
|
|
||
| function Send-RedLogEvent { | ||
| [CmdletBinding()] | ||
| param( | ||
| [Parameter(Mandatory, Position = 0)] | ||
| [string]$Command, | ||
|
|
||
| [Parameter(Mandatory, Position = 1)] | ||
| [ValidateSet('command_start', 'command_end', 'note', 'marker')] | ||
| [string]$Subtype, | ||
|
|
||
| [Parameter(Position = 2)] | ||
| [hashtable]$Extra = @{} | ||
| ) | ||
|
|
||
| if (-not (Test-Path $script:_RedLogPortFile) -or | ||
| -not (Test-Path $script:_RedLogTokenFile)) { | ||
| return | ||
| } | ||
|
|
||
| try { | ||
| $port = (Get-Content $script:_RedLogPortFile -Raw).Trim() | ||
| $token = (Get-Content $script:_RedLogTokenFile -Raw).Trim() | ||
|
|
||
| $data = @{ | ||
| subtype = $Subtype | ||
| command = $Command | ||
| shell = 'powershell' | ||
| pid = $PID | ||
| } | ||
| foreach ($k in $Extra.Keys) { $data[$k] = $Extra[$k] } | ||
|
|
||
| $body = @{ agent_type = 'shell'; data = $data } | ConvertTo-Json -Depth 4 -Compress | ||
| $uri = "http://127.0.0.1:${port}/api/events" | ||
| $headers = @{ | ||
| 'Authorization' = "Bearer $token" | ||
| 'Content-Type' = 'application/json' | ||
| } | ||
|
|
||
| # Fire-and-forget via background runspace | ||
| $rs = [runspacefactory]::CreateRunspace() | ||
| $rs.Open() | ||
| $ps = [powershell]::Create().AddScript({ | ||
| param($uri, $headers, $body) | ||
| try { | ||
| Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body -TimeoutSec 2 | Out-Null | ||
| } catch {} | ||
| }).AddArgument($uri).AddArgument($headers).AddArgument($body) | ||
| $ps.Runspace = $rs | ||
| $null = $ps.BeginInvoke() | ||
|
Comment on lines
+52
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift Dispose completed fire-and-forget runspaces. Each event creates a runspace and
🧰 Tools🪛 PSScriptAnalyzer (1.25.0)[warning] 58-58: Empty catch block is used. Please use Write-Error or throw statements in catch blocks. (PSAvoidUsingEmptyCatchBlock) 📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| } catch {} | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,93 @@ | ||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||
| # RedLog Event Sender — fire-and-forget from any shell / hook script. | ||||||||||||||||
| # Works natively on macOS/Linux and inside WSL2 (auto-resolves Windows token path). | ||||||||||||||||
| # | ||||||||||||||||
| # Usage: | ||||||||||||||||
| # hooks/redlog-send.sh "nmap -sV $TARGET" command_start | ||||||||||||||||
| # nmap -sV "$TARGET" | ||||||||||||||||
| # hooks/redlog-send.sh "nmap -sV $TARGET" command_end "{\"exit_code\":$?}" | ||||||||||||||||
|
|
||||||||||||||||
| set -u | ||||||||||||||||
|
|
||||||||||||||||
| COMMAND="${1:-}" | ||||||||||||||||
| SUBTYPE="${2:-command_start}" | ||||||||||||||||
| EXTRA="${3:-}" | ||||||||||||||||
|
|
||||||||||||||||
| [[ -z "$COMMAND" ]] && exit 0 | ||||||||||||||||
|
|
||||||||||||||||
| # --- Locate token / port files --- | ||||||||||||||||
| _resolve_redlog_dir() { | ||||||||||||||||
| # Native (macOS / Linux / WSL with symlink) | ||||||||||||||||
| if [[ -f "$HOME/.redlog/api-token" ]]; then | ||||||||||||||||
| echo "$HOME/.redlog" | ||||||||||||||||
| return | ||||||||||||||||
| fi | ||||||||||||||||
| # WSL: resolve from Windows %USERPROFILE% | ||||||||||||||||
| if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then | ||||||||||||||||
| local win_profile | ||||||||||||||||
| win_profile=$(cmd.exe /c 'echo %USERPROFILE%' 2>/dev/null | tr -d '\r') | ||||||||||||||||
| if [[ -n "$win_profile" ]]; then | ||||||||||||||||
| local wsl_path | ||||||||||||||||
| wsl_path=$(wslpath "$win_profile" 2>/dev/null) | ||||||||||||||||
| if [[ -f "$wsl_path/.redlog/api-token" ]]; then | ||||||||||||||||
| echo "$wsl_path/.redlog" | ||||||||||||||||
| return | ||||||||||||||||
| fi | ||||||||||||||||
| fi | ||||||||||||||||
| fi | ||||||||||||||||
| return 1 | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| # Cache the resolved dir for the session | ||||||||||||||||
| if [[ -z "${_REDLOG_DIR:-}" ]]; then | ||||||||||||||||
| _REDLOG_DIR=$(_resolve_redlog_dir) || exit 0 | ||||||||||||||||
| export _REDLOG_DIR | ||||||||||||||||
| fi | ||||||||||||||||
|
|
||||||||||||||||
| PORT_FILE="$_REDLOG_DIR/api-port" | ||||||||||||||||
| TOKEN_FILE="$_REDLOG_DIR/api-token" | ||||||||||||||||
|
|
||||||||||||||||
| [[ -f "$PORT_FILE" ]] || exit 0 | ||||||||||||||||
| [[ -f "$TOKEN_FILE" ]] || exit 0 | ||||||||||||||||
|
|
||||||||||||||||
| PORT=$(<"$PORT_FILE") | ||||||||||||||||
| TOKEN=$(<"$TOKEN_FILE") | ||||||||||||||||
|
|
||||||||||||||||
| # --- Resolve reachable host --- | ||||||||||||||||
| if [[ -z "${_REDLOG_HOST:-}" ]]; then | ||||||||||||||||
| _REDLOG_HOST="127.0.0.1" | ||||||||||||||||
| if ! curl -sf --connect-timeout 1 "http://127.0.0.1:${PORT}/api/health" >/dev/null 2>&1; then | ||||||||||||||||
| # WSL2 NAT mode: try the Windows host gateway | ||||||||||||||||
| if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then | ||||||||||||||||
| local gw | ||||||||||||||||
| gw=$(ip route show default 2>/dev/null | awk '{print $3; exit}') | ||||||||||||||||
| if [[ -n "$gw" ]] && curl -sf --connect-timeout 1 "http://${gw}:${PORT}/api/health" >/dev/null 2>&1; then | ||||||||||||||||
|
Comment on lines
+61
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'Files matching redlog-send.sh:\n'
fd -a 'redlog-send\.sh$' . || true
file="$(fd 'redlog-send\.sh$' . | head -n1 || true)"
if [[ -n "${file:-}" ]]; then
echo
printf 'Line count and relevant slice for %s:\n' "$file"
wc -l "$file"
sed -n '1,110p' "$file" | nl -ba
fi
printf '\nParsing/local context probe if file exists:\n'
python3 - <<'PY'
from pathlib import Path
files = list(Path('.').rglob('redlog-send.sh'))
print(files)
for p in files:
text = p.read_text()
lines = text.splitlines()
for i,line in enumerate(lines, start=1):
if 'if [[ -n "${WSL_DISTRO_NAME:-}"' in line:
print(f"match at {p}:{i}")
for j in range(max(1,i-4), min(len(lines),i+8)+1):
print(f"{j}: {lines[j-1]}")
PYRepository: guan4tou2/REDLOG Length of output: 351 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo 'Relevant lines from hooks/redlog-send.sh:'
awk '1' hooks/redlog-send.sh | sed -n '1,110p' | cat -n
echo
echo 'Bash local-on-top-level behavior probe:'
bash -n <<'EOF'
set -euo pipefail
if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
local gw
gw='x'
fi
EOF
echo "bash -n exit code: $?" || true
bash -n <<'EOF'
set -euo pipefail
if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
gw='x'
fi
EOF
echo "bash -n normal variable exit code: $?" || trueRepository: guan4tou2/REDLOG Length of output: 3556 Remove the invalid This - local gw
- gw=$(ip route show default 2>/dev/null | awk '{print $3; exit}')
+ gw=$(ip route show default 2>/dev/null | awk '{print $3; exit}')📝 Committable suggestion
Suggested change
🧰 Tools🪛 Shellcheck (0.11.0)[error] 62-62: 'local' is only valid in functions. (SC2168) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||
| _REDLOG_HOST="$gw" | ||||||||||||||||
| fi | ||||||||||||||||
| fi | ||||||||||||||||
| fi | ||||||||||||||||
| export _REDLOG_HOST | ||||||||||||||||
| fi | ||||||||||||||||
|
|
||||||||||||||||
| # --- Build and send event --- | ||||||||||||||||
| PAYLOAD=$(python3 -c " | ||||||||||||||||
| import json, sys, os | ||||||||||||||||
| d = { | ||||||||||||||||
| 'agent_type': 'shell', | ||||||||||||||||
| 'data': { | ||||||||||||||||
| 'subtype': sys.argv[1], | ||||||||||||||||
| 'command': sys.argv[2], | ||||||||||||||||
| 'shell': os.environ.get('SHELL', 'bash').rsplit('/', 1)[-1], | ||||||||||||||||
| 'pid': os.getppid() | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| if sys.argv[3]: | ||||||||||||||||
| d['data'].update(json.loads(sys.argv[3])) | ||||||||||||||||
| print(json.dumps(d)) | ||||||||||||||||
| " "$SUBTYPE" "$COMMAND" "$EXTRA" 2>/dev/null) || exit 0 | ||||||||||||||||
|
|
||||||||||||||||
| curl -sf -X POST "http://${_REDLOG_HOST}:${PORT}/api/events" \ | ||||||||||||||||
| -H "Authorization: Bearer $TOKEN" \ | ||||||||||||||||
| -H "Content-Type: application/json" \ | ||||||||||||||||
| -d "$PAYLOAD" \ | ||||||||||||||||
| --connect-timeout 1 --max-time 2 >/dev/null 2>&1 & | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: guan4tou2/REDLOG
Length of output: 11458
Document mirrored mode as optional and version-gated.
These steps present mirrored networking as mandatory, but it requires Windows 11 22H2 or later. Since the WSL hooks already fall back to
gateway.docker.internalfor NAT mode, describe mirrored mode as recommended and mention the NAT fallback for Windows 10/unavailable mirrored setups.🤖 Prompt for AI Agents